git.y1.nz

gbdk-2020

GameBoy Development Kit
download: https://git.y1.nz/archives/gbdk.tar.gz
README | Files | Log | Refs | LICENSE

commit 042489596d9098403f1258faaa3456ab40033017
parent bd8486078e1a8bf01ff8649ecbc889cf450e3625
Author: Toxa <56631470+untoxa@users.noreply.github.com>
Date:   Mon, 24 May 2021 23:02:42 +0300

Merge pull request #188 from bbbbbr/gbcompress_c_output

Gbcompress: add c source array format input and output
Diffstat:
Mgbdk-support/gbcompress/Makefile7++++++-
Mgbdk-support/gbcompress/files.c2--
Mgbdk-support/gbcompress/files.h2+-
Agbdk-support/gbcompress/files_c_source.c224+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agbdk-support/gbcompress/files_c_source.h9+++++++++
Mgbdk-support/gbcompress/main.c67++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
6 files changed, 292 insertions(+), 19 deletions(-)

diff --git a/gbdk-support/gbcompress/Makefile b/gbdk-support/gbcompress/Makefile @@ -6,7 +6,7 @@ endif CC = $(TOOLSPREFIX)gcc CFLAGS = -ggdb -O -Wno-incompatible-pointer-types -DGBDKLIBDIR=\"$(TARGETDIR)\" -OBJ = main.o gbcompress.o files.o +OBJ = main.o gbcompress.o files.o files_c_source.o BIN = gbcompress all: $(BIN) @@ -16,6 +16,11 @@ $(BIN): $(OBJ) clean: rm -f *.o $(BIN) *~ rm -f tmp.* + # round trip the executable through compression and de-compression as a brief test test: + rm -f tmp.cmp; rm -f tmp.dcmp cp $(BIN) tmp.in; ./gbcompress -v tmp.in tmp.cmp; ./gbcompress -v -d tmp.cmp tmp.dcmp; diff -s tmp.in tmp.dcmp + rm -f tmp.cmp.c; rm -f tmp.dcmp.c; rm -f tmp.cmp; rm -f tmp.dcmp + cp $(BIN) tmp.in; ./gbcompress -v --cout --varname=some_array tmp.in tmp.cmp.c; ./gbcompress -v -d --cin tmp.cmp.c tmp.dcmp; diff -s tmp.in tmp.dcmp + rm tmp.* diff --git a/gbdk-support/gbcompress/files.c b/gbdk-support/gbcompress/files.c @@ -2,9 +2,7 @@ #include <stdio.h> #include <stdbool.h> #include <stdint.h> -//#include <string.h> #include <stdlib.h> -//#include <stdint.h> // Read from a file into a buffer (will allocate needed memory) diff --git a/gbdk-support/gbcompress/files.h b/gbdk-support/gbcompress/files.h @@ -1,7 +1,7 @@ #ifndef _FILES_H #define _FILES_H -char * file_read_into_buffer(char * filename, uint32_t *ret_size); +uint8_t * file_read_into_buffer(char * filename, uint32_t *ret_size); bool file_write_from_buffer(char * filename, uint8_t * p_buf, uint32_t data_len); #endif // _FILES_H diff --git a/gbdk-support/gbcompress/files_c_source.c b/gbdk-support/gbcompress/files_c_source.c @@ -0,0 +1,224 @@ + +#include <stdio.h> +#include <stdbool.h> +#include <stdint.h> +#include <string.h> +#include <stdlib.h> + +#include "files.h" + +#define STR_FFWD_MATCH_ANY NULL + +#define BUF_DEFAULT_SIZE 20000 +#define BUF_GROW_SIZE 10000 + + +static uint32_t size_compressed = 0; +static uint32_t size_decompressed = 0; + + +void c_source_set_sizes(uint32_t size_compressed_in, uint32_t size_decompressed_in) { + size_compressed = size_compressed_in; + size_decompressed = size_decompressed_in; +} + + +// Search for a character in a string +// +// Terminate search if: +// * any non-allowed character was found (str_chars_allowed, NULL for allow any chars) +// * end of string is reached (by terminator or length) +// +static char * str_ffwd_to(char * str_in, uint32_t * max_len, char char_match, char * str_chars_allowed) { + + char str_cur[2] = " \0"; // current character to test, for strpbrk + + while ((*max_len) && (*str_in != '\0')) { + + str_cur[0] = *str_in; + if (*str_in == char_match) + return str_in; + else if (str_chars_allowed) { + if (strpbrk(str_cur, str_chars_allowed) == NULL) { + // signal failure if any non-allowed character was found + return NULL; + } + } + + str_in++; + (*max_len)--; + } + + return NULL; +} + + + +// Convert an array of comma delimtied numbers into a buffer +// +// If conversion fails: returns NULL, *p_ret_size == 0 +// +static uint8_t * str_array_to_buf(char * str_in, uint32_t * p_ret_len) { + + uint32_t buf_size = BUF_DEFAULT_SIZE; + uint8_t * p_buf = malloc(buf_size); + uint8_t * p_buf_last; + char * end_ptr; + + // Read value at a time from a string buffer + // (strtok will replace the , chars with \0 on each split) + char * str_cur = strtok(str_in,","); + + if (p_buf) { + + // Start with size zero + *p_ret_len = 0; + + // Loop as long as strtok returns comma separated items + while (str_cur != NULL) { + + // Grow buffer if needed + if ((*p_ret_len) >= buf_size) { + + buf_size += BUF_GROW_SIZE; + p_buf_last = p_buf; + p_buf = (uint8_t *)realloc(p_buf, buf_size); + + // Exit if realloc failed + if (p_buf == NULL) { + *p_ret_len = 0; + free(p_buf_last); + return NULL; + } + } + + // Store current value + p_buf[ *p_ret_len ] = (uint8_t)strtol(str_cur, &end_ptr, 0); + + // Only advance data buffer if conversion didn't fail + if (str_cur != end_ptr) + (*p_ret_len)++; + + // Read next value (strtok maintains state of last call) + str_cur = strtok(NULL,","); + } + } + + return p_buf; +} + + + +// Read from a file into a buffer, find first C source formatted +// array and parse it into another buffer (will allocate needed memory) +// +// Returns NULL if reading file didn't succeed +// +uint8_t * file_read_c_input_into_buffer(char * filename, uint32_t *ret_size) { + + uint8_t * filedata = NULL; + uint32_t file_size; + + uint8_t * str_c_array = NULL; + uint8_t * str_c_array_start = NULL; + uint8_t * p_array_data = NULL; + + filedata = file_read_into_buffer(filename, &file_size); + + if (filedata) { + str_c_array = filedata; + + // Find array opening bracket `[` + if ( str_c_array = str_ffwd_to(str_c_array, &file_size, '[', STR_FFWD_MATCH_ANY) ) { + // Find Array closing bracket `]` + if ( str_c_array = str_ffwd_to(str_c_array, &file_size, ']', "[xX0123456789ABCDEFabcdef\t\n\r ") ) { + // Find Start or array + if ( str_c_array = str_ffwd_to(str_c_array, &file_size, '{', "]\t\n\r =") ) { + // Save start of array + str_c_array_start = str_c_array + 1; + + // Find end of array + if ( str_c_array = str_ffwd_to(str_c_array, &file_size, '}', "{xX0123456789ABCDEFabcdef,\t\n\r ") ) { + // Terminate string at end of array + *str_c_array = '\0'; + + // If conversion fails: p_array_data == NULL, ret_size == 0 + p_array_data = str_array_to_buf(str_c_array_start, ret_size); + + // printf("C array length in = %d", *ret_size); + } + } + } + } + + // Free file data if allocated + free(filedata); + filedata = NULL; + } + + return p_array_data; +} + + + +// Writes a buffer to a file in C source format +// Adds a matching .h if possible +// +bool file_write_c_output_from_buffer(char * filename, uint8_t * p_buf, uint32_t data_len, char * var_name, bool var_is_const) { + + bool status = false; + size_t wrote_bytes; + FILE * file_out = fopen(filename, "w"); + int i; + + if (file_out) { + + // Array entry with variable name + fprintf(file_out, "\n\n%s unsigned char %s[] = {", (var_is_const) ? "const" : "", var_name); + + for (i = 0; i < data_len; i++) { + + // Line break every 16 bytes (includes at start. but exclude if on last entry) + if ((i != data_len -1 ) && ((i % 16) == 0)) + fprintf(file_out, "\n "); + + // Write current byte + fprintf(file_out, "0x%.2X", p_buf[i]); + + // Add comma after every entry except last one + if (i != data_len -1) + fprintf(file_out, ", "); + } + fprintf(file_out, "\n};\n\n"); + status = true; + + fclose(file_out); + + + // Create matching .h header file output, if file ends in .c or .C + if (strlen(filename) >= 2) { + if ((strcmp(&filename[strlen(filename) - 2],".c") == 0) || + (strcmp(&filename[strlen(filename) - 2],".C") == 0)) { + + // Replace file extension + filename[strlen(filename) - 2] = '.'; + filename[strlen(filename) - 1] = 'h'; + + FILE * file_out = fopen(filename, "w"); + + if (file_out) { + + fprintf(file_out, "\n\n#define %s_sz_comp %d\n", var_name, size_compressed); + fprintf(file_out, "#define %s_sz_decomp %d\n", var_name, size_decompressed); + + // array entry with variable name + fprintf(file_out, "\n\nextern %s unsigned char %s[];\n\n", (var_is_const) ? "const" : "", var_name); + + fclose(file_out); + } + } + } + } + + return status; +} diff --git a/gbdk-support/gbcompress/files_c_source.h b/gbdk-support/gbcompress/files_c_source.h @@ -0,0 +1,9 @@ +#ifndef _FILES_C_SOURCE_H +#define _FILES_C_SOURCE_H + +void c_source_set_sizes(uint32_t, uint32_t); + +bool file_write_c_output_from_buffer(char *, uint8_t *, uint32_t, char *, bool); +char * file_read_c_input_into_buffer(char * filename, uint32_t *ret_size); + +#endif // _FILES_C_SOURCE_H diff --git a/gbdk-support/gbcompress/main.c b/gbdk-support/gbcompress/main.c @@ -10,6 +10,7 @@ #include "gbcompress.h" #include "files.h" +#include "files_c_source.h" #define MAX_STR_LEN 4096 @@ -19,9 +20,11 @@ char filename_out[MAX_STR_LEN] = {'\0'}; uint8_t * p_buf_in = NULL; uint8_t * p_buf_out = NULL; -bool opt_mode_compress = true; -bool opt_verbose = false; - +bool opt_mode_compress = true; +bool opt_verbose = false; +bool opt_c_source_input = false; +bool opt_c_source_output = false; +char opt_c_source_output_varname[MAX_STR_LEN] = "var_name"; static void display_help(void); static int handle_args(int argc, char * argv[]); @@ -36,9 +39,12 @@ static void display_help(void) { "Use: Gbcompress a binary file and write it out.\n" "\n" "Options\n" - "-h : Show this help screen\n" - "-d : Decompress (default is compress)\n" - "-v : Verbose output\n" + "-h : Show this help screen\n" + "-d : Decompress (default is compress)\n" + "-v : Verbose output\n" + "-cin : Read input as .c source format (8 bit char ONLY, uses first array found)\n" + "-cout : Write output in .c / .h source format (8 bit char ONLY) \n" + "-varname=<NAME> : specify variable name for c source output\n" "Example: \"gbcompress binaryfile.bin compressed.bin\"\n" "Example: \"gbcompress -d compressedfile.bin decompressed.bin\"\n" ); @@ -64,10 +70,16 @@ int handle_args(int argc, char * argv[]) { return false; // Don't parse input when -h is used } else if (strstr(argv[i], "-v") == argv[i]) { opt_verbose = true; + } else if (strstr(argv[i], "--cin") == argv[i]) { + opt_c_source_input = true; + } else if (strstr(argv[i], "--cout") == argv[i]) { + opt_c_source_output = true; + } else if (strstr(argv[i], "--varname=") == argv[i]) { + snprintf(opt_c_source_output_varname, sizeof(opt_c_source_output_varname), "%s", argv[i] + 10); } else if (strstr(argv[i], "-d") == argv[i]) { opt_mode_compress = false; } else - printf("BankPack: Warning: Ignoring unknown option %s\n", argv[i]); + printf("gbcompress: Warning: Ignoring unknown option %s\n", argv[i]); } } @@ -104,23 +116,36 @@ static int compress() { uint32_t buf_size_in = 0; uint32_t buf_size_out = 0; uint32_t out_len = 0; + bool result = false; - p_buf_in = file_read_into_buffer(filename_in, &buf_size_in); + if (opt_c_source_input) + p_buf_in = file_read_c_input_into_buffer(filename_in, &buf_size_in); + else + p_buf_in = file_read_into_buffer(filename_in, &buf_size_in); // Allocate buffer output buffer same size as input // It can grow more in gbdecompress_buf() buf_size_out = buf_size_in; p_buf_out = malloc(buf_size_out); - if ((p_buf_out) && (buf_size_in > 0)) { - out_len = gbcompress_buf(p_buf_in, buf_size_in, - &p_buf_out, buf_size_out); + if ((p_buf_in) && (p_buf_out) && (buf_size_in > 0)) { + out_len = gbcompress_buf(p_buf_in, buf_size_in, &p_buf_out, buf_size_out); + if (out_len > 0) - if (file_write_from_buffer(filename_out, p_buf_out, out_len)) { + + if (opt_c_source_output) { + c_source_set_sizes(out_len, buf_size_in); // compressed, decompressed + result = file_write_c_output_from_buffer(filename_out, p_buf_out, out_len, opt_c_source_output_varname, true); + } + else + result = file_write_from_buffer(filename_out, p_buf_out, out_len); + + if (result) { if (opt_verbose) printf("Compressed: %d bytes -> %d bytes (%%%.2f)\n", buf_size_in, out_len, ((double)out_len / (double)buf_size_in) * 100); return EXIT_SUCCESS; } + } return EXIT_FAILURE; @@ -132,19 +157,31 @@ static int decompress() { uint32_t buf_size_in = 0; uint32_t buf_size_out = 0; uint32_t out_len = 0; + bool result = false; - p_buf_in = file_read_into_buffer(filename_in, &buf_size_in); + if (opt_c_source_input) + p_buf_in = file_read_c_input_into_buffer(filename_in, &buf_size_in); + else + p_buf_in = file_read_into_buffer(filename_in, &buf_size_in); // Allocate buffer output buffer 3x size of input // It can grow more in gbdecompress_buf() buf_size_out = buf_size_in * 3; p_buf_out = malloc(buf_size_out); - if ((p_buf_out) && (buf_size_in > 0)) { + if ((p_buf_in) && (p_buf_out) && (buf_size_in > 0)) { out_len = gbdecompress_buf(p_buf_in, buf_size_in, &p_buf_out, buf_size_out); if (out_len > 0) - if (file_write_from_buffer(filename_out, p_buf_out, out_len)) { + + if (opt_c_source_output) { + c_source_set_sizes(buf_size_in, out_len); // compressed, decompressed + result = file_write_c_output_from_buffer(filename_out, p_buf_out, out_len, opt_c_source_output_varname, true); + } + else + result = file_write_from_buffer(filename_out, p_buf_out, out_len); + + if (result) { if (opt_verbose) printf("Decompressed: %d bytes -> %d bytes (compression was %%%.2f)\n", buf_size_in, out_len, ((double)buf_size_in / (double)out_len) * 100); return EXIT_SUCCESS;

This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.