init with content

This commit is contained in:
theBreadCompany 2023-12-09 03:51:00 +01:00
commit b4918934dc
16 changed files with 982 additions and 0 deletions

66
CMakeLists.txt Normal file
View file

@ -0,0 +1,66 @@
set()
cmake_minimum_required(VERSION 3.12)
project(libbpic VERSION 1.0.0 LANGUAGES C DESCRIPTION "encode and decode any file to an image")
# dependencies
find_package(PNG REQUIRED)
message(STATUS ${PNG_INCLUDE_DIR})
include_directories(${PNG_INCLUDE_DIRS})
# sources
set(BPIC_SOURCES
src/bpic/common_utils.c
src/bpic/encode.c
src/bpic/decode.c)
set(BPIC_HEADERS
src/bpic/include/bpic.h
src/bpic/include/common_utils.h
src/bpic/include/decode.h
src/bpic/include/encode.h)
set(BPICENCODE_SOURCES
src/bpicencode/main.c)
set(BPICDECODE_SOURCES
src/bpicdecode/main.c)
set(BPICENCODE_TEST_SOURCES
src/bpicencode_test/main.c)
set(BPICDECODE_TEST_SOURCES
src/bpicdecode_test/main.c)
# building
add_library(bpic ${BPIC_HEADERS} ${BPIC_SOURCES})
set_target_property(TARGET bpic PROPERTY C_STANDARD 90)
target_include_directories(bpic PUBLIC src/bpic/include)
set_target_properties(bpic PROPERTIES PUBLIC_HEADER bpic.h)
add_executable(bpicencode ${BPICENCODE_SOURCES})
add_executable(bpicdecode ${BPICDECODE_SOURCES})
add_executable(bpicencode_test ${BPICENCODE_TEST_SOURCES})
add_executable(bpicdecode_test ${BPICDECODE_TEST_SOURCES})
# linking
message(STATUS ${PNG_LIBRARIES})
target_link_libraries(bpic ${PNG_LIBRARIES})
target_link_libraries(bpicencode bpic)
target_link_libraries(bpicdecode bpic)
target_link_libraries(bpicdecode_test bpic)
target_link_libraries(bpicencode_test bpic)
# installing
install(
TARGETS bpic
EXPORT bpic
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
PUBLIC_HEADER DESTINATION include/${PROJECT_NAME}
)

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 theBreadCompany
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

28
README.md Normal file
View file

@ -0,0 +1,28 @@
# libbpic
**P**NG **V**isual **S**torage **F**format library to encode data to images and back.
## disclaimer
This library is not considered to be safe for production and is not delivered under any kind of warranty! Use with caution and only for educational and research purposes!
## building
Simply call:
```bash
git clone https://github.com/thebreadcompany/libbpic
cd libbpic
mkdir build && cd build
cmake .. && cmake --build .
```
You can find the library
## installing
## usage
## use cases

BIN
src/.DS_Store vendored Normal file

Binary file not shown.

BIN
src/bpic/.DS_Store vendored Normal file

Binary file not shown.

37
src/bpic/common_utils.c Normal file
View file

@ -0,0 +1,37 @@
//
// common_utils.c
// bpic
//
// Created by theBreadCompany on 04.03.23.
//
#include <common_utils.h>
void throw_err(const char* message, int error) {
char *category = error ? "ERROR:" : "NOTE:";
fprintf(stderr, "%s %s", category, message);
}
void debug(FILE *pipe, const char *message, ...) {
#if DEBUG || debug
fprintf(pipe, "%s", message);
#endif
}
bpic_message bpic_error_input_file_null = "Input file is null!";
bpic_message bpic_error_input_buffer_null = "Input buffer is null!";
bpic_message bpic_error_input_buffer_small = "Input buffer too small!";
bpic_message bpic_warn_encoding_invalid = "Encoding is invalid, defaulting to bitwise (0)";
bpic_message bpic_warn_pxsize_null = "Pixel size is null, defaulting to 2";
uint16_t fletcher16(const unsigned char *data, size_t size) {
uint16_t sum1 = 0;
uint16_t sum2 = 0;
for (int index = 0; index < size; index++) {
sum1 = (sum1 + data[index]) % 0xFF;
sum2 = (sum2 + sum1) % 0xFF;
}
return (sum2 << 8) | sum1;
}

212
src/bpic/decode.c Normal file
View file

@ -0,0 +1,212 @@
//
// decode.c
// bpic
//
// Created by theBreadCompany on 04.03.23.
//
#include "decode.h"
void read_to_buffer(png_structp png_ptr, png_bytep data, png_size_t length);
decoded_data *bpic_decode_memory(encoded_data *data, bool *checksum_matching) {
if(!data->buffer) { // is there even data
throw_err(bpic_error_input_buffer_null, 0);
return (void*)bpic_error_input_buffer_null;
}
if(data->size < 7) { // is there enough data to hold at least a header
throw_err(bpic_error_input_buffer_small, 0);
return (void*)bpic_error_input_buffer_small;
}
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
png_infop png_info = png_create_info_struct(png_ptr);
//printf("%zu\n", data->size);
/*
mem_decode *input = malloc(sizeof(mem_decode));
input->buffer = data->buffer;
input->size = data->size;
input->pos = 0;
png_set_read_fn(png_ptr, input, read_to_buffer);
*/
decoded_data *output = bpic_decode_file(fmemopen(data->buffer, data->size, "r"), checksum_matching);
png_free(png_ptr, png_info);
png_destroy_read_struct(&png_ptr, NULL, NULL);
//free(png_ptr);
return output;
}
decoded_data* bpic_decode_file(FILE *input, bool *checksum_matching) {
fseek(input, 0, SEEK_SET);
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
png_infop png_info = png_create_info_struct(png_ptr);
if(input) {
png_init_io(png_ptr, input);
} else {
printf("Failed to init png with file!\n");
return NULL;
}
image_data *output = bpic_decode_png(png_ptr, png_info, checksum_matching);
png_free(png_ptr, png_info);
png_destroy_read_struct(&png_ptr, NULL, NULL);
return output;
}
decoded_data* bpic_decode_png(png_structp png_ptr, png_infop png_info, bool *checksum_matching) {
png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_IF_SAFE, (png_bytep)"pvSF", 1);
png_read_info(png_ptr, png_info);
printf("%u x %u\n", png_get_image_height(png_ptr, png_info), png_get_image_width(png_ptr, png_info));
png_unknown_chunkp chunks;
int chunk_count = png_get_unknown_chunks(png_ptr, png_info, &chunks);
bpic_info bpic_chunk;
bpic_chunk.length = 0;
if(chunk_count) { // are there unknown chunks
for(uint8_t i = 0; i < chunk_count; i++) { // for every unknown chunk
png_unknown_chunk chunk = chunks[i]; // store chunk
char *name = malloc(5); // alloc name
memcpy(name, chunk.name, 5); // copy name (same procedure as setting it
if(!strcmp(name, "pvSF") && chunk.size == 7) { // if name is pvSF header name and size is 12 bytes
bpic_chunk.px_size = pow(2, chunk.data[0] >> 3); // remember, the stored pxsize is a power of 2 leftshifted by 2
bpic_chunk.encoding = chunk.data[0] & 0b11;
memcpy(&bpic_chunk.length, chunk.data + 1, 4);
memcpy(&bpic_chunk.fletcher16, chunk.data + 5, 2);
for(int byte = 0; byte < 7; byte++) {
for(int bit = 1; bit < 9; bit++) {
printf("%s", chunk.data[byte] >> (8 - bit) & 1 ? "1" : "0");
}
printf(" ");
}
printf("\n");
}
free(name);
free(chunk.data);
}
} else {
throw_err("input is missing unknown chunks, meaning that the pvSF chunk is also missing!", 0);
return (void*)"input is missing unknown chunks, meaning that the pvSF chunk is also missing!";
}
if(!bpic_chunk.length) {
fprintf(stderr, "Missing data length in bpic chunk!\n");
return NULL;
}
free(chunks);
printf("pxsize: %u; encoding: %u; length: %zu; checksum: %u\n", bpic_chunk.px_size, bpic_chunk.encoding, bpic_chunk.length, bpic_chunk.fletcher16);
int width = png_get_image_width(png_ptr, png_info);// * bpic_chunk.px_size;
int height = png_get_image_height(png_ptr, png_info);// * bpic_chunk.px_size;
png_bytep *row_data = png_malloc(png_ptr, sizeof(png_bytep) * height * bpic_chunk.px_size);
for(int y = 0; y < height; y++) row_data[y] = png_malloc(png_ptr, sizeof(png_byte) * width * bpic_chunk.px_size);
png_set_rows(png_ptr, png_info, row_data);
png_read_image(png_ptr, row_data);
png_read_end(png_ptr, NULL);
int bits = png_get_bit_depth(png_ptr, png_info);
// create a filebuffer that can hold as many bytes as needed according to the header
unsigned char *filebuffer = malloc(bpic_chunk.length);//(width/bits * height/bits);//(bpic_chunk.length);
// if encoding is bitwise
if(!bpic_chunk.encoding) {
// if the data is encoded in an 8 bit grayscale image
if(bits == 8) {
// for every [px_size]th in the data; also init a bit count
for(int y = 0, bit_i = 0; y < height; y += bpic_chunk.px_size) {
// for every [px_size]th column; also increment the bit count
for(int x = 0; x < width && (bit_i >> 3) < bpic_chunk.length; x += bpic_chunk.px_size, bit_i++) {
// store the pixel data
png_byte px = row_data[y][x * bpic_chunk.px_size];
// store the corresponding the data
uint8_t value = px & (0x80 >> (bit_i & 0b111));
// add the data in the byte of the buffer; 8 bit = 1 byte, so divide the bit count by 8 to get the byte position
filebuffer[bit_i >> 3] += value;
}
}
// if the data is encoded as a 1 bit grayscale image
} else if(bits == 1) {
// the image widths are required to be divisible by 8 to make parsing easier
// basically same algorithm, but now
for(int y = 0, bit_i = 0; y < height; y += bpic_chunk.px_size) {
// for every [px_size]th column; also increment the bit count
for(int x = 0; x < width && (bit_i >> 3) < bpic_chunk.length; x += bpic_chunk.px_size, bit_i++) {
// store the pixel data
png_byte px = row_data[y][x * bpic_chunk.px_size];
// store the corresponding the data
uint8_t value = px & (0x80 >> (bit_i & 0b111));
// add the data in the byte of the buffer; 8 bit = 1 byte, so divide the bit count by 8 to get the byte position
filebuffer[bit_i >> 3] += value;
}
}
// unknown bit count, cancel...
} else {
}
// othrwise the encoding is bytewise
} else {
// this is far more straightforward as the rows literally represent the data
if(bpic_chunk.px_size == 1) {
// every row, except the last as it may break the available range of the file buffer
for(int y = 0; y < height - 1; y++) {
// copy the row to the filebuffer with an offset of the number of already processed bytes
memcpy(filebuffer + y * width, row_data[y], width);
}
// the required data size to be copied is calculated as the required length minus the already parsed length
memcpy(filebuffer + (height - 1) * width, row_data[height-1], bpic_chunk.length - width * (height - 1));
// except if the pixel size is > 1, then only every [px_size]th pixel is valid data
} else {
// for every [px_size]th row except the last
for(int y = 0; y < height - bpic_chunk.px_size; y += bpic_chunk.px_size) {
// for every [px_size]th column
for(int x = 0; x < width; x += bpic_chunk.px_size) {
// copy every [px_size]th byte to the buffer at offset of the current byte
memcpy(filebuffer + y / bpic_chunk.px_size * width + x / bpic_chunk.px_size, row_data[y] + x * bpic_chunk.px_size, 1);
}
}
// separatly go through the last row as it may contain more data than required
for(int x = 0; x < bpic_chunk.length - (height - 1) * width; x += bpic_chunk.px_size) {
memcpy(filebuffer + (height - 1) * width + x / bpic_chunk.px_size, row_data[height - 1] + x * bpic_chunk.px_size, 1);
}
}
}
for(int y = 0; y < height; y++) png_free(png_ptr, row_data[y]);
png_free(png_ptr, row_data);
uint16_t checksum = fletcher16(filebuffer, bpic_chunk.length);
printf("Checksum of decoded data: %u\n", checksum);
*checksum_matching = bpic_chunk.fletcher16 == checksum;
decoded_data *output = malloc(sizeof(decoded_data));
output->buffer = filebuffer;
output->size = bpic_chunk.length;
return output;
}
void read_to_buffer(png_structp png_ptr, png_bytep data, png_size_t length) {
mem_decode *data_info = png_get_io_ptr(png_ptr);
if(data_info->size < data_info->pos + length) {
printf("How... The input is bigger than expected, already at %lu/%zu\n", data_info->pos + length, data_info->size);
}
memcpy(data, data_info->buffer + data_info->pos, length);
data_info->pos += length;
}

243
src/bpic/encode.c Normal file
View file

@ -0,0 +1,243 @@
//
// encode.c
// bpic
//
// Created by theBreadCompany on 04.03.23.
//
#include "encode.h"
void write_to_buffer(png_structp png_ptr, png_bytep data, png_size_t length);
png_bytepp convert_bitwise_1bit_grayscale(image_data *data, png_structp png_ptr, png_infop info_ptr);
png_bytepp convert_bitwise_8bit_grayscale(image_data *data, png_structp png_ptr, png_infop info_ptr);
png_bytepp convert_bytewise_8bit_rgb(image_data *data, png_structp png_ptr, png_infop info_ptr);
png_bytepp convert_bitwise_1bit_grayscale(image_data *data, png_structp png_ptr, png_infop info_ptr) {
int width = round(sqrt((double)(data->size << 3)));
int height = round((data->size << 3) / (double)width);
png_bytepp row_data = convert_bytewise_8bit_rgb(data, png_ptr, info_ptr);
png_set_IHDR(png_ptr, info_ptr, width, height, 1, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
return row_data;
}
png_bytepp convert_bitwise_8bit_grayscale(image_data *data, png_structp png_ptr, png_infop info_ptr) {
// width as the squareroot of the size times 8 because each bit requires a separate pixel; round app for
int width = round(sqrt((double)(data->size << 3)));
// height is size divided by width,
//int height = round(data->size * 8 / (double)width);
int height = width + (width * width < data->size << 3);
// embed image information
png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
// alloc enough memory to hold the pointers to the individual rows
png_byte **row_data = calloc(height, sizeof(png_bytep));
// for every row
for(int y = 0; y < height; y++) {
// alloc row memory
row_data[y] = calloc(width, sizeof(png_byte));
// for every column
for(int x = 0; x < width; x++) {
// get bit index
int px_i = y * width + x;
// check if already at EOF/EOB
if(px_i == (data->size << 3)) return row_data;
// get on/of state at bit index
uint8_t value = data->buffer[px_i >> 3] >> (7 - px_i % 8) & 1;
// store value at the current position in the image data (assigning 266 to uint8 results in 0, as 256 > uint8)
row_data[y][x] = value * 0xFF;
}
}
return row_data;
}
png_bytepp convert_bytewise_8bit_rgb(image_data *data, png_structp png_ptr, png_infop info_ptr) {
int width = round(sqrt((double)data->size));
int height = width;
png_bytep *row_data = calloc(height-1, sizeof(png_bytep));
for(int y = 0; y < height - 1; y++) {
// alloc row memory
row_data[y] = malloc(width * sizeof(png_byte));
// literally copy chunks of the source to the PNG
memcpy(row_data[y], data->buffer + y * width, width);
}
// copy the last chunk separatly as it may be smaller than the width -> prevent random data
row_data[height-1] = calloc(width, sizeof(png_byte));
printf("Copying last chunk to row %d of length %lu, starting at index %d in buffer\n", height-1, data->size - width*(height-1), (height-1)*width);
memcpy(row_data[height-1], data->buffer + (height-1)*width, data->size - width*(height-1));
png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
return row_data;
}
image_data *bpic_encode_memory(image_data *data, bpic_info* info) {
int width = 0;
int height = 0;
if(!data->buffer) {
throw_err(bpic_error_input_buffer_null, 0);
return (void*)bpic_error_input_buffer_null;
}
if(!info) info = (bpic_info*) malloc(sizeof(bpic_info));
if(info->encoding != bpic_bitwise_encoding && info->encoding != bpic_bytewise_encoding) info->encoding = bpic_bitwise_encoding;
//if(!info->px_size) info->px_size = 4;
//if(!info->px_size) info->px_size = 1;
info->px_size = 1;
//if(!data->size) data->size = data->size;
if(info->encoding == bpic_bitwise_encoding) {
width = round(sqrt((double)(data->size << 3)));
//width += width % 8;
height = width;//round(data->size * 8 / (double)width);
} else if(info->encoding == bpic_bytewise_encoding) {
width = sqrt((double)data->size);
height = width + (width * width < data->size);
} else {
return NULL;
}
printf("%u x %u -> %u x %u\n", width, height, width * info->px_size, height * info->px_size);
info->fletcher16 = fletcher16(data->buffer, data->size);
png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
png_infop info_ptr = png_create_info_struct(png_ptr);
//png_bytep image_data = malloc(sizeof(png_byte) * width * height);
png_byte **row_data = NULL;
if(!info->encoding) {
clock_t start_bit8 = clock();
row_data = convert_bitwise_8bit_grayscale(data, png_ptr, info_ptr); // works
clock_t end_bit8 = clock();
printf("8-bit / grayscale; time: %f sec\n", (double)(end_bit8 - start_bit8)/CLOCKS_PER_SEC);
} else {
clock_t start_byte = clock();
row_data = convert_bytewise_8bit_rgb(data, png_ptr, info_ptr); // why u no work
clock_t end_byte = clock();
printf("1-byte / grayscale; time: %f sec\n", (double)(end_byte - start_byte)/CLOCKS_PER_SEC);
}
png_byte **scaled_data = NULL;
// then scale it to its actual size
if(info->px_size > 1) {
// cache row size
int row_size = sizeof(png_byte) * width * info->px_size * 2;
// create a pointer to the scaled up data
scaled_data = malloc(height * info->px_size * sizeof(png_bytep));
// alloc memory here as rows will be allocated that normally would not be in range with the row index
for(int y = 0; y < height * info->px_size; y++) scaled_data[y] = malloc(row_size);
// for every [pixelsize]th row
for(int y = 0; y < height * info->px_size; y+=info->px_size) {
// for every [pixelsize]th column
for(int x = 0; x < width * info->px_size; x+=info->px_size) {
// copy the pixeldata from the 1x1 version to the scaled version; i.e. px_size=4, y=4, x=4 results in row_data[1][1]
memcpy(&scaled_data[y][x], &row_data[(y / info->px_size)][x / info->px_size], sizeof(png_byte));
// fill the following pixels with the data at the position in scaled_data, in the previous example y=4 and x=5,6,7
for(int x_offset = 1; x_offset < info->px_size; x_offset++) memcpy(&scaled_data[y][x+x_offset], &scaled_data[y][x], sizeof(png_byte));
}
// copy the previously calculated row "down", meaning the data from y=4 to y=5,6,7
for(int y_offset = 1; y_offset < info->px_size; y_offset++) memcpy(scaled_data[y+y_offset], scaled_data[y], row_size);
}
} else {
// if the pixel size is actually 1, just point to the already calculated data
scaled_data = row_data;
}
png_bytep bpic_data = calloc(7, sizeof(png_byte));
bpic_data[0] += (uint8_t)sqrt(info->px_size) << 2;
bpic_data[0] += info->encoding;
memcpy(bpic_data + 1, &data->size, sizeof(uint32_t));
memcpy(bpic_data + 5, &info->fletcher16, sizeof(uint16_t));
for(int byte = 0; byte < 7; byte++) {
for(int bit = 1; bit < 9; bit++) {
printf("%s", bpic_data[byte] >> (8 - bit) & 1 ? "1" : "0");
}
printf(" ");
}
printf("\n");
printf("pxsize: %u; encoding: %u; length: %zu; checksum: %u\n", info->px_size, info->encoding, data->size, info->fletcher16);
// Finally, set up the libpng stuff for writing
mem_encode *target = malloc(sizeof(mem_encode));
target->buffer = NULL;
target->size = 0;
png_set_write_fn(png_ptr, target, write_to_buffer, NULL);
png_bytep chunk_data = calloc(7, sizeof(png_byte));
memcpy(chunk_data, bpic_data, 7);
png_size_t size = 7;
png_bytep type = (png_bytep)"pvSF";
png_write_info(png_ptr, info_ptr);
png_write_chunk(png_ptr, type, bpic_data, size);
png_write_image(png_ptr, scaled_data);
png_write_end(png_ptr, info_ptr);
//fclose(out);
png_destroy_info_struct(png_ptr, &info_ptr);
png_destroy_write_struct(&png_ptr, &info_ptr);
if(info->px_size > 1) {
for(int y = 0; y < height; y++) free(scaled_data[y]);
free(scaled_data);
}
for(int y = 0; y < height / info->px_size; y++) free(row_data[y]);
free(row_data);
free(bpic_data);
free(chunk_data);
free(info);
return target;
}
image_data *bpic_encode_file(FILE *input, bpic_info* info) {
if(!input) throw_err(bpic_error_input_file_null, 1);
// prepare file
fseek(input, 0, SEEK_END);
size_t flen = ftell(input);
fseek(input, 0, SEEK_SET);
image_data *data = malloc(sizeof(image_data));
data->buffer = malloc(flen);
fread(data->buffer, sizeof(unsigned char), flen, input);
data->size = flen;
encoded_data *output = bpic_encode_memory(data, info);
free(data->buffer);
free(data);
return output;
}
void write_to_buffer(png_structp png_ptr, png_bytep data, png_size_t length) {
mem_encode* data_info = png_get_io_ptr(png_ptr);
size_t size = data_info->size + length;
if(data_info->buffer) {
data_info->buffer = realloc(data_info->buffer, size);
} else {
data_info->buffer = malloc(size);
}
memcpy(data_info->buffer + data_info->size, data, length);
data_info->size += length;
}

15
src/bpic/include/bpic.h Normal file
View file

@ -0,0 +1,15 @@
//
// bpic.h
// VisualStorageKit
//
// Created by theBreadCompany on 04.03.23.
//
#ifndef bpic_h
#define bpic_h
#include "encode.h"
#include "decode.h"
#endif /* bpic_h */

View file

@ -0,0 +1,78 @@
//
// common_utils.h
// bpic
//
// Created by theBreadCompany on 04.03.23.
//
#ifndef common_utils_h
#define common_utils_h
#include <stdbool.h>
#include <png.h>
#include <stdlib.h>
#include <stdio.h>
#include <strings.h>
#include <math.h>
#include <time.h>
#pragma mark - Header Information
#define BPIC_BITWISE_ENCODING 0b00
#define BPIC_BYTEWISE_ENCODING 0b01
#define BPIC_UNCOMPRESSED 0b00
#define BPIC_COMPRESSED 0b10
enum bpic_encoding {
bpic_bitwise_encoding,
bpic_bytewise_encoding,
//bpic_bitwise_zipped_encoded,
//bpic_bytewise_zipped_encoded
};
typedef struct {
uint8_t px_size; // first 6 bits (limits px size to up to 2^6-1 = 63, which should be more than sufficient
enum bpic_encoding encoding; // 7th and 8th bit, allowing for 4 encodings (only two used for now
size_t length; // 9th - 40 th bit (4 bytes)
uint16_t fletcher16; // 41st - 56th bit (2 bytes)
} bpic_info;
#pragma mark - Helper structs
typedef struct {
unsigned char *buffer;
size_t size;
} mem_encode;
typedef mem_encode image_data;
typedef image_data encoded_data;
typedef image_data decoded_data;
typedef struct {
unsigned char *buffer;
size_t size;
png_uint_32 pos;
} mem_decode;
#pragma mark - Crypto
uint16_t fletcher16(const unsigned char *data, size_t size); //
#pragma mark - Error Handling
typedef const char* bpic_message;
extern bpic_message bpic_error_input_file_null;
extern bpic_message bpic_error_input_buffer_null;
extern bpic_message bpic_error_input_buffer_small;
extern bpic_message bpic_warn_encoding_invalid;
extern bpic_message bpic_warn_pxsize_null;
void throw_err(bpic_message message, int error);
void debug(FILE *pipe, const char *message, ...);
#endif /* common_utils_h */

43
src/bpic/include/decode.h Normal file
View file

@ -0,0 +1,43 @@
//
// decode.h
// bpic
//
// Created by theBreadCompany on 04.03.23.
//
#ifndef decode_h
#define decode_h
#include "common_utils.h"
/**
decode memory
@param data The buffer to decode
@param checksum_matching A pointer that stores stores whether the checksum matches the one encoded in the image
@return A pointer to the decoded memory or a bpic\_message on failure.
*/
decoded_data *bpic_decode_memory(encoded_data *data, bool *checksum_matching);
/**
decode a file
@param input A pointer to a file to decode; NULL is invalid
@param checksum_matching A pointer that stores stores whether the checksum matches the one encoded in the image
@returns A pointer to the decoded memory or a bpic\_message on failure.
*/
decoded_data *bpic_decode_file(FILE *input, bool *checksum_matching);
/**
Decode a png
This method may only be invoced via `bpic_decode_file(FILE *input)` or `bpic_decode_memory(encoded_data *data)`.
@param png_ptr A pointer to the png_ptr that is to be decoded. The input has to be set up, either via `png_set_read_fn` or `png_init_io`
@param png_info A pointer to a `png_info` struct if already available
@param checksum_matching A pointer that stores stores whether the checksum matches the one encoded in the image
@return A pointer to the decoded data
*/
decoded_data *bpic_decode_png(png_structrp png_ptr, png_inforp png_info, bool *checksum_matching);
#endif /* decode_h */

31
src/bpic/include/encode.h Normal file
View file

@ -0,0 +1,31 @@
//
// encode.h
// bpic
//
// Created by theBreadCompany on 04.03.23.
//
#ifndef encode_h
#define encode_h
#include "common_utils.h"
/**
encode memory
@param data The memory buffer to encode
@param info A bpic_info struct containing parameters like encoding and any calculated variables on return; NULL is valid.
@returns A pointer to the encoded memory or a bpic\_message on if an error occured.
*/
encoded_data *bpic_encode_memory(image_data *data, bpic_info* info);
/**
encode a file
@param input A pointer to the file to encode; NULL is invalid
@param info A bpic_info struct containing parameters like encoding and any calculated variables on return; NULL is valid.
@returns A pointer to the encoded memory or a bpic\_message on if an error occured.
*/
encoded_data *bpic_encode_file(FILE *input, bpic_info* info);
#endif /* encode_h */

74
src/bpicdecode/main.c Normal file
View file

@ -0,0 +1,74 @@
//
// main.m
// bpicdecode
//
// Created by theBreadCompany on 28.02.23.
//
#include <stdio.h>
#include <stdlib.h>
#include "bpic.h"
int main(int argc, const char * argv[]) {
FILE *inputFile;
FILE *outputFile;
if(argc < 2) {
fprintf(stderr, "Missing path to file!\n");
return 1;
}
inputFile = fopen(argv[1], "rb");
if(!inputFile) {
fprintf(stderr, "Could not open file %s\n", argv[2]);
return 1;
}
// input as memory
encoded_data *input = malloc(sizeof(encoded_data));
fseek(inputFile, 0, SEEK_END);
input->size = ftell(inputFile);
fseek(inputFile, 0, SEEK_SET);
input->buffer = malloc(input->size);
fread(input->buffer, sizeof(unsigned char), input->size, inputFile);
clock_t start = clock();
bool checksum_matching = false;
//encoded_data *data = bpic_decode_file(inputFile);
encoded_data *data = bpic_decode_memory(input, &checksum_matching);
clock_t end = clock();
free(input->buffer);
free(input);
fclose(inputFile);
printf("Done in %f seconds.\n", (double)(end - start) / CLOCKS_PER_SEC);
uint8_t len = strlen(argv[1]) + 5;
char *oname = calloc(len, sizeof(const char));
strcpy(oname, argv[1]);
strcpy(oname + len - 5, ".png");
outputFile = fopen(oname, "wb");
int error = 0;
if(fwrite(data->buffer, sizeof(unsigned char), data->size, outputFile) != data->size * sizeof(unsigned char)) {
fprintf(stderr, "Failed writing output file!");
error = 1;
}
fclose(outputFile);
//free(inputFile);
free(oname);
free(data->buffer);
free(data);
//free(outputFile);
inputFile = NULL;
outputFile = NULL;
return error;
}

View file

@ -0,0 +1,5 @@
#include <bpic.h>
int main(int argc, const char *argv[]) {
return 0;
}

121
src/bpicencode/main.c Normal file
View file

@ -0,0 +1,121 @@
//
// main.m
// bpicencode
//
// Created by theBreadCompany on 28.02.23.
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/time.h>
#include "bpic.h"
// prototype for earlier use
void printHelper(void);
bool processArgv(int argc, const char *argv[], unsigned int *encoding, const char **input, const char **output, unsigned int *bit_depth);
void printHelp(void) {
printf(
"Usage: bpicencode <file> [-o <output> -e <encoding> -b <bitdepth>]\n"
"<file> the file to encode\n"
"--output,-o sets a destination file name; default: <file>.png\n"
"--encoding,-o sets a encoding (0 for bitwise, 1 for bytewise); default: 0\n"
"--bitdepth,-b sets a bit depth (1 (doesnt work with bytewise) or 8); default: 8\n"
"file sizes: 8 bit > 1 bit > bytewise encoding; safety is the other way around\n"
);
}
int main(int argc, const char * argv[]) {
int encoding;
FILE *inputFile;
FILE *outputFile;
if(argc == 1 || strlen(argv[1]) != 1 || !(strcmp(&argv[1][0], "0") || strcmp(&argv[1][0], "1"))) {
fprintf(stderr, "Encoding has to be either '0' for bitwise or '1' for bytewise!\n");
return 1;
}
encoding = strcmp(argv[1], "0");
inputFile = fopen(argv[2], "rb");
if(!inputFile) {
fprintf(stderr, "Could not open file %s!\n", argv[2]);
return 1;
}
clock_t start = clock();
bpic_info *info = malloc(sizeof(bpic_info));
info->encoding = encoding;
decoded_data *data = bpic_encode_file(inputFile, info);
clock_t end = clock();
printf("Done in %f seconds.\n", (double)(end - start) / CLOCKS_PER_SEC);
uint8_t len = strlen(argv[2]);
char *oname = malloc(len + 4 + 1); // 4 bytes for ".png" and 1 byte for \0
strcpy(oname, argv[2]);
strcpy(oname + len, ".png");
outputFile = fopen(oname, "wb");
int code = 0;
if(fwrite(data->buffer, sizeof(unsigned char), data->size, outputFile) != data->size * sizeof(unsigned char)) {
fprintf(stderr, "Failed writing output file!");
code = 1;
}
fclose(outputFile);
free(oname);
free(data->buffer);
free(data);
return code;
}
bool processArgv(int argc, const char *argv[], unsigned int *encoding, const char **input, const char **output, unsigned int *bit_depth) {
for(int i = 0; i < argc; i++) {
if(strcmp(argv[i], "--output") || strcmp(argv[i], "-o")) {
if(i+1 <= argc || strncmp(argv[i+1], "-", 1)) {
fprintf(stderr, "missing output value!\n");
return false;
} else {
i++;
*output = argv[i];
}
} else if(strcmp(argv[i], "--bit-depth") || strcmp(argv[i], "-b")) {
if(i+1 <= argc || strncmp(argv[i+1], "-", 1)) {
fprintf(stderr, "missing bit depth value!\n");
return false;
} else {
i++;
*bit_depth = (int)strtol(argv[i], NULL, 10);
}
} else if(strcmp(argv[i], "--encoding") || strcmp(argv[i], "-e")) {
if(i+1 <= argc || strncmp(argv[i+1], "-", 1)) {
fprintf(stderr, "missing encoding value!\n");
return false;
} else {
i++;
*encoding = (int)strtol(argv[i], NULL, 10);
}
} else if(strncmp(argv[i], "--", 2)) {
fprintf(stderr, "unknown flag '%s'!\n", argv[i]);
return false;
} else {
FILE *fin = fopen(argv[i], "rb");
if(fin) {
*input = argv[i];
fclose(fin);
} else {
fprintf(stderr, "input file does not exist!\n");
return false;
}
}
}
return true;
}

View file

@ -0,0 +1,8 @@
#include <bpic.h>
int main(int argc, const char *argv[]) {
return 0;
}