git.y1.nz

gbdk-2020

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

gbdk-support/gbcompress/files_c_source.c

      1 
      2 #include <stdio.h>
      3 #include <stdbool.h>
      4 #include <stdint.h>
      5 #include <string.h>
      6 #include <stdlib.h>
      7 
      8 #include "common.h"
      9 #include "files.h"
     10 
     11 #define STR_FFWD_MATCH_ANY NULL
     12 
     13 #define BUF_DEFAULT_SIZE 20000
     14 #define BUF_GROW_SIZE    10000
     15 
     16 
     17 static uint32_t size_compressed = 0;
     18 static uint32_t size_decompressed = 0;
     19 
     20 
     21 void c_source_set_sizes(uint32_t size_compressed_in, uint32_t size_decompressed_in) {
     22     size_compressed = size_compressed_in;
     23     size_decompressed = size_decompressed_in;
     24 }
     25 
     26 
     27 // Search for a character in a string
     28 //
     29 // Terminate search if:
     30 // * any non-allowed character was found (str_chars_allowed, NULL for allow any chars)
     31 // * end of string is reached (by terminator or length)
     32 //
     33 static char * str_ffwd_to(char * str_in, uint32_t * max_len, char char_match, char * str_chars_allowed) {
     34 
     35     char str_cur[2] = " \0"; // current character to test, for strpbrk
     36 
     37     while ((*max_len) && (*str_in != '\0')) {
     38 
     39         str_cur[0] = *str_in;
     40         if (*str_in == char_match)
     41             return str_in;
     42         else if (str_chars_allowed) {
     43             if (strpbrk(str_cur, str_chars_allowed) == NULL) {
     44                 // signal failure if any non-allowed character was found
     45                 return NULL;
     46             }
     47         }
     48 
     49         str_in++;
     50         (*max_len)--;
     51     }
     52 
     53     return NULL;
     54 }
     55 
     56 
     57 
     58 // Convert an array of comma delimited numbers into a buffer
     59 //
     60 // If conversion fails: returns NULL, *p_ret_size == 0
     61 //
     62 static uint8_t * str_array_to_buf(char * str_in, uint32_t * p_ret_len) {
     63 
     64     uint32_t  buf_size  = BUF_DEFAULT_SIZE;
     65     uint8_t * p_buf     = malloc(buf_size);
     66     uint8_t * p_buf_last;
     67     char    * end_ptr;
     68 
     69     // Read value at a time from a string buffer
     70     // (strtok will replace the , chars with \0 on each split)
     71     char * str_cur = strtok(str_in,",");
     72 
     73     if (p_buf) {
     74 
     75         // Start with size zero
     76         *p_ret_len = 0;
     77 
     78         // Loop as long as strtok returns comma separated items
     79         while (str_cur != NULL) {
     80 
     81             // Grow buffer if needed
     82             if ((*p_ret_len) >= buf_size)  {
     83 
     84                 buf_size += BUF_GROW_SIZE;
     85                 p_buf_last = p_buf;
     86                 p_buf = (uint8_t *)realloc(p_buf, buf_size);
     87 
     88                 // Exit if realloc failed
     89                 if (p_buf == NULL) {
     90                     *p_ret_len = 0;
     91                     free(p_buf_last);
     92                     return NULL;
     93                 }
     94             }
     95 
     96             // Store current value
     97             p_buf[ *p_ret_len ] = (uint8_t)strtol(str_cur, &end_ptr, 0);
     98 
     99             // Only advance data buffer if conversion didn't fail
    100             if (str_cur != end_ptr)
    101                 (*p_ret_len)++;
    102 
    103             // Read next value (strtok maintains state of last call)
    104             str_cur = strtok(NULL,",");
    105         }
    106     }
    107 
    108     return p_buf;
    109 }
    110 
    111 
    112 
    113 // Read from a file into a buffer, find first C source formatted
    114 // array and parse it into another buffer (will allocate needed memory)
    115 //
    116 // Returns NULL if reading file didn't succeed
    117 //
    118 uint8_t * file_read_c_input_into_buffer(char * filename, uint32_t *p_ret_size) {
    119 
    120     char * filedata = NULL;
    121     uint32_t  file_size;
    122 
    123     char * str_c_array = NULL;
    124     char * str_c_array_start = NULL;
    125     uint8_t * p_array_data = NULL;
    126 
    127     *p_ret_size = 0;
    128 
    129     filedata = file_read_into_buffer_char(filename, &file_size);
    130 
    131     if (filedata) {
    132         str_c_array = filedata;
    133 
    134         // Find array opening bracket `[`
    135         str_c_array = str_ffwd_to(str_c_array, &file_size, '[', STR_FFWD_MATCH_ANY);
    136         if (str_c_array) {
    137             // Find Array closing bracket `]`
    138             str_c_array = str_ffwd_to(str_c_array, &file_size, ']', "[xX0123456789ABCDEFabcdef\t\n\r ");
    139             if (str_c_array) {
    140                 // Find Start or array
    141                 str_c_array = str_ffwd_to(str_c_array, &file_size, '{', "]\t\n\r =");
    142                 if (str_c_array) {
    143                     // Save start of array
    144                     str_c_array_start = str_c_array + 1;
    145 
    146                     // Find end of array
    147                     str_c_array = str_ffwd_to(str_c_array, &file_size, '}', "{xX0123456789ABCDEFabcdef,\t\n\r ");
    148                     if (str_c_array) {
    149                         // Terminate string at end of array
    150                         *str_c_array = '\0';
    151 
    152                         // If conversion fails: p_array_data == NULL, p_ret_size == 0
    153                         p_array_data = str_array_to_buf(str_c_array_start, p_ret_size);
    154 
    155                         // printf("C array length in = %d", *p_ret_size);
    156                     }
    157                 }
    158             }
    159         }
    160 
    161         // Free file data if allocated
    162         free(filedata);
    163         filedata = NULL;
    164 
    165         if (*p_ret_size == 0) {
    166             printf("gbcompress: ERROR: Failed to read any bytes in\n");
    167         }
    168     }
    169 
    170 
    171     return p_array_data;
    172 }
    173 
    174 
    175 
    176 // Writes a buffer to a file in C source format
    177 // Adds a matching .h if possible
    178 //
    179 bool file_write_c_output_from_buffer(char * filename, uint8_t * p_buf, uint32_t data_len, char * var_name, char * bankref_name, bool var_is_const, uint16_t bank_num) {
    180 
    181     bool status = false;
    182     size_t wrote_bytes;
    183     FILE * file_out = fopen(filename, "w");
    184     int i;
    185 
    186     if (file_out) {
    187         // C Source array output
    188 
    189         // If Bank Num is set add a .h bank ref
    190         if (bank_num != BANK_NUM_ROM_UNSET) {
    191             fprintf(file_out, "#pragma bank %d\n\n", bank_num);
    192             fprintf(file_out, "#include <gbdk/platform.h>\n\n");
    193             fprintf(file_out, "BANKREF(%s)\n\n", bankref_name);
    194         }
    195 
    196         // Array entry with variable name
    197         fprintf(file_out, "%s unsigned char %s[] = {", (var_is_const) ? "const" : "", var_name);
    198 
    199         for (i = 0; i < data_len; i++) {
    200 
    201             // Line break every 16 bytes (includes at start. but exclude if on last entry)
    202             if ((i != data_len -1 ) && ((i % 16) == 0))
    203                 fprintf(file_out, "\n    ");
    204 
    205             // Write current byte
    206             fprintf(file_out, "0x%.2X", p_buf[i]);
    207 
    208             // Add comma after every entry except last one
    209             if (i != data_len -1)
    210                 fprintf(file_out, ", ");
    211         }
    212         fprintf(file_out, "\n};\n\n");
    213         status = true;
    214 
    215         fclose(file_out);
    216 
    217 
    218         // Create matching .h header file output, if file ends in .c or .C
    219         if (strlen(filename) >= 2) {
    220             if ((strcmp(&filename[strlen(filename) - 2],".c") == 0) ||
    221                 (strcmp(&filename[strlen(filename) - 2],".C") == 0)) {
    222 
    223                 // Replace file extension
    224                 filename[strlen(filename) - 2] = '.';
    225                 filename[strlen(filename) - 1] = 'h';
    226 
    227                 FILE * file_out = fopen(filename, "w");
    228 
    229                 if (file_out) {
    230                     // Include guards
    231                     fprintf(file_out, "#ifndef GBCOMPRESS_%s_H\n", var_name);
    232                     fprintf(file_out, "#define GBCOMPRESS_%s_H\n\n", var_name);
    233                     
    234                     // If Bank Num is set add a .h bank ref
    235                     if (bank_num != BANK_NUM_ROM_UNSET) {
    236                         fprintf(file_out, "#include <gbdk/metasprites.h>\n\n");
    237                         fprintf(file_out, "BANKREF_EXTERN(%s)\n\n", bankref_name);
    238                     }
    239 
    240                     fprintf(file_out, "\n\n#define %s_sz_comp %d\n", var_name, size_compressed);
    241                     fprintf(file_out, "#define %s_sz_decomp %d\n", var_name, size_decompressed);
    242 
    243                     // array entry with variable name
    244                     fprintf(file_out, "\nextern %s unsigned char %s[];\n\n", (var_is_const) ? "const" : "", var_name);
    245 
    246                     fprintf(file_out, "#endif\n");
    247 
    248                     fclose(file_out);
    249                 }
    250             }
    251         }
    252     } else {
    253         printf("gbcompress: Error: Failed to open output file: %s\n", filename);
    254     }
    255 
    256     return status;
    257 }

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