git.y1.nz

gbdk-2020

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

gbdk-support/bankpack/files.c

      1 // This is free and unencumbered software released into the public domain.
      2 // For more information, please refer to <https://unlicense.org>
      3 // bbbbbr 2020
      4 
      5 #include <stdio.h>
      6 #include <string.h>
      7 #include <stdlib.h>
      8 #include <stdbool.h>
      9 #include <unistd.h>
     10 #include <stdint.h>
     11 
     12 #include "common.h"
     13 #include "path_ops.h"
     14 #include "list.h"
     15 #include "files.h"
     16 #include "obj_data.h"
     17 
     18 static void files_set_output_name(void);
     19 static char * file_read_to_buffer(char *);
     20 
     21 list_type filelist;
     22 
     23 char g_out_ext[MAX_FILE_STR];
     24 char g_out_path[MAX_FILE_STR];
     25 char g_out_linkerfile_name[MAX_FILE_STR] = {'\0'};
     26 
     27 static unsigned int get_obj_file_format(FILE * obj_file, const char * str_filename);
     28 
     29 void files_set_out_ext(char * ext_str) {
     30     if (snprintf(g_out_ext, sizeof(g_out_ext), "%s", ext_str) > sizeof(g_out_ext))
     31         printf("Bankpack: Warning: truncated output extension to:%s\n",g_out_ext);
     32 }
     33 
     34 void files_set_out_path(char * path_str) {
     35     if (snprintf(g_out_path, sizeof(g_out_path), "%s", path_str) > sizeof(g_out_path))
     36         printf("Bankpack: Warning: truncated output path to:%s\n",g_out_path);
     37 }
     38 
     39 
     40 void files_init(void) {
     41     list_init(&filelist, sizeof(file_item));
     42     g_out_ext[0]  = '\0';
     43     g_out_path[0] = '\0';
     44 }
     45 
     46 void files_cleanup(void) {
     47     list_cleanup(&filelist);
     48 }
     49 
     50 
     51 // Reads a list of object files from a linkerfile
     52 // (one filename per line) and adds them
     53 void files_read_linkerfile(char * filename_in) {
     54     char strline_in[MAX_FILE_STR] = "";
     55 
     56     FILE * in_file;
     57 
     58     // Open each object file and try to process all the lines
     59     in_file = fopen(filename_in, "r");
     60     if (in_file) {
     61 
     62         // Read one line at a time into \0 terminated string
     63         // Skip empty lines
     64         // Strip newlines and carraige returns from end of filename
     65         while ( fgets(strline_in, sizeof(strline_in), in_file) != NULL) {
     66             if ((strline_in[0] != '\n') || (strline_in[0] != '\r')) {
     67                 strline_in[strcspn(strline_in, "\r\n")] = '\0';
     68                 files_add(strline_in);
     69             }
     70         }
     71         fclose(in_file);
     72 
     73     } // end: if valid file
     74     else {
     75         printf("BankPack: ERROR: failed to open input linkerfile: %s\n", filename_in);
     76         exit(EXIT_FAILURE);
     77     }
     78 }
     79 
     80 
     81 void files_set_linkerfile_outname(char * filename) {
     82 
     83     if (snprintf(g_out_linkerfile_name, sizeof(g_out_linkerfile_name), "%s", filename) > sizeof(g_out_linkerfile_name))
     84         printf("Warning: truncated output linkerfile name to:%s\n",g_out_linkerfile_name);
     85 }
     86 
     87 
     88 // qsort compare rule function for linkerfile_order
     89 static int linkerfile_order_compare(const void* a, const void* b) {
     90 
     91     // Sort by linkerfile_order [asc]
     92     if (((file_order_t *)a)->linkerfile_order != ((file_order_t *)b)->linkerfile_order)
     93         return (((file_order_t *)a)->linkerfile_order < ((file_order_t *)b)->linkerfile_order) ? QSORT_A_FIRST : QSORT_A_AFTER;
     94     else
     95         return QSORT_A_SAME;
     96 }
     97 
     98 // Set linkerfile order
     99 //
    100 // Helps ensure fixed bank areas get linked/placed before autobank via linkerfile output order.
    101 // Intended to facilitate alignment assumptions for size padded object files.
    102 // - Files that only have non-banked data ("_CODE", "_HOME") will always be first in the order
    103 // - See obj_data_process() and area_item_compare() for full details of how linkerfile_order gets set
    104 static void linkerfile_output_order_sort(const file_item * p_files, file_order_t * p_filelist_order, const uint32_t count) {
    105 
    106     uint32_t c;
    107 
    108     if (p_filelist_order && p_files) {
    109         // Create sort-able list from list of files and their assigned linkerfile order
    110         for (c = 0; c < filelist.count; c++) {
    111             p_filelist_order[c].file_id = c;
    112             p_filelist_order[c].linkerfile_order = p_files[c].linkerfile_order;
    113         }
    114 
    115         // Sort the new list by linkerfile order
    116         qsort (p_filelist_order, count, sizeof(file_order_t), linkerfile_order_compare);
    117     } else {
    118         printf("BankPack: ERROR: failed to allocate memory for sorting linkerfile output\n");
    119         exit(EXIT_FAILURE);
    120     }
    121 }
    122 
    123 
    124 // Writes a list of loaded object filenames to
    125 // a linkerfile (one filename per line)
    126 void files_write_linkerfile(void) {
    127     uint32_t c;
    128     file_item * files = (file_item *)filelist.p_array;
    129     FILE * out_file;
    130 
    131     // Instead of sorting the file array itself, which would break references
    132     // (i.e. "files[ areas[c].file_id ]."), create a sorted reference array to order the file output
    133     file_order_t * filelist_order = malloc(filelist.count * sizeof(file_order_t));
    134     linkerfile_output_order_sort(files, filelist_order, filelist.count);
    135 
    136     // Open the linkerfile output and write all object filenames
    137     out_file = fopen(g_out_linkerfile_name, "w");
    138     if (out_file) {
    139 
    140         // Process stored file names
    141         for (c = 0; c < filelist.count; c++) {
    142             // printf("linkerfile out: c = %d : file_id = %d linkerfile_order = %d, name = %s\n", c, filelist_order[c].file_id,  filelist_order[c].linkerfile_order, files[ filelist_order[c].file_id ].name_out);
    143             fprintf(out_file, "%s\n", files[ filelist_order[c].file_id ].name_out);
    144         }
    145 
    146         fclose(out_file);
    147 
    148     } // end: if valid file
    149     else {
    150         printf("BankPack: ERROR: failed to open output linkerfile: %s\n", g_out_linkerfile_name);
    151         exit(EXIT_FAILURE);
    152     }
    153 
    154     if (filelist_order)
    155         free(filelist_order);
    156 }
    157 
    158 
    159 void files_add(char * filename) {
    160 
    161     file_item newfile;
    162 
    163     if (snprintf(newfile.name_in, sizeof(newfile.name_in), "%s", filename) > sizeof(newfile.name_in))
    164         printf("Warning: truncated input filename to:%s\n",newfile.name_in);
    165 
    166     newfile.name_out[0] = '\0';
    167     newfile.rewrite_needed = false;
    168     newfile.bank_num = BANK_NUM_UNASSIGNED;
    169     newfile.linkerfile_order = LINKERFILE_ORDER_FIRST;  // This won't get changed for non-banked object files
    170 
    171     list_additem(&filelist, &newfile);
    172 }
    173 
    174 
    175 char * file_get_name_in_by_id(uint32_t file_id) {
    176 
    177     file_item * files = (file_item *)filelist.p_array;
    178 
    179     if ((file_id >= 0) && (file_id < filelist.count))
    180         return files[file_id].name_in;
    181     else
    182         return (char *)"\0";
    183 }
    184 
    185 
    186 char * file_get_name_out_by_id(uint32_t file_id) {
    187 
    188     file_item * files = (file_item *)filelist.p_array;
    189 
    190     if ((file_id >= 0) && (file_id < filelist.count))
    191         return files[file_id].name_out;
    192     else
    193         return (char *)"\0";
    194 }
    195 
    196 
    197 // Update output names based on -path= and -ext= option params
    198 static void files_set_output_name(void) {
    199 
    200     uint32_t c;
    201     file_item * files = (file_item *)filelist.p_array;
    202 
    203     // Process stored file names
    204     for (c = 0; c < filelist.count; c++) {
    205 
    206         snprintf(files[c].name_out, sizeof(files[c].name_out), "%s", files[c].name_in);
    207 
    208         if (g_out_ext[0])
    209             filename_replace_extension(files[c].name_out, g_out_ext, sizeof(files[c].name_out));
    210         if (g_out_path[0])
    211             filename_replace_path(files[c].name_out, g_out_path, sizeof(files[c].name_out));
    212     }
    213 }
    214 
    215 
    216 // Read from a file into a buffer (will allocate needed memory)
    217 // Returns NULL if reading file didn't succeed
    218 static char * file_read_to_buffer(char * filename) {
    219 
    220     long fsize;
    221     FILE * file_in = fopen(filename, "rb");
    222     char * filedata = NULL;
    223 
    224     if (file_in) {
    225         // Get file size
    226         fseek(file_in, 0, SEEK_END);
    227         fsize = ftell(file_in);
    228         if (fsize != -1L) {
    229             fseek(file_in, 0, SEEK_SET);
    230 
    231             filedata = malloc(fsize + 1); // One extra byte to add string null terminator
    232             if (filedata) {
    233                 if (fsize != fread(filedata, 1, fsize, file_in))
    234                     printf("BankPack: Warning: File read size didn't match expected for %s\n", filename);
    235                 filedata[fsize] = '\0'; // Add null string terminator at end
    236             } else printf("BankPack: ERROR: Failed to allocate memory to read file %s\n", filename);
    237 
    238         } else printf("BankPack: ERROR: Failed to read size of file %s\n", filename);
    239 
    240         fclose(file_in);
    241     } else printf("BankPack: ERROR: Failed to open input file %s\n", filename);
    242 
    243     return filedata;
    244 }
    245 
    246 
    247 // Scan the file looking for lines that start with XL3 or XL4
    248 // which indicate object file version type
    249 static unsigned int get_obj_file_format(FILE * obj_file, const char * str_filename) {
    250 
    251     char strline_in[OBJ_NAME_MAX_STR_LEN] = "";
    252 
    253     if (obj_file) {
    254 
    255         // Rewind file and read one line at a time into \0 terminated string
    256         rewind(obj_file);
    257         while ( fgets(strline_in, sizeof(strline_in), obj_file) != NULL) {
    258 
    259             // Check if a version version indicator was found
    260             if (strncmp(strline_in, OBJ_FILE_XL3_ID, strlen(OBJ_FILE_XL3_ID)) == 0) {
    261                 return (OBJ_FILE_XL3_24BIT_ADDR);
    262             }
    263             else if (strncmp(strline_in, OBJ_FILE_XL4_ID, strlen(OBJ_FILE_XL4_ID)) == 0) {
    264                 return (OBJ_FILE_XL4_32BIT_ADDR);
    265             }
    266         }
    267     } else {
    268         printf("BankPack: ERROR: invalid file pointer for file: \"%s\"\n", str_filename);
    269         exit(EXIT_FAILURE);
    270     }
    271 
    272     printf("BankPack: ERROR: unable to determine Object File version for file: \"%s\"\n", str_filename);
    273     exit(EXIT_FAILURE);
    274 }
    275 
    276 
    277 // Extract areas from files, once collected assign them to banks
    278 void files_extract(void) {
    279     uint32_t c;
    280     char strline_in[OBJ_NAME_MAX_STR_LEN] = "";
    281     file_item * files = (file_item *)filelist.p_array;
    282     FILE * obj_file;
    283 
    284     // Process stored file names
    285     for (c = 0; c < filelist.count; c++) {
    286 
    287         // Open each object file and try to process all the lines
    288         obj_file = fopen(files[c].name_in, "r");
    289         if (obj_file) {
    290             files[c].obj_file_format = get_obj_file_format(obj_file, files[c].name_in);
    291 
    292             // Rewind file (due to format scan above)
    293             // Then read one line at a time into \0 terminated string
    294             rewind(obj_file);
    295             while ( fgets(strline_in, sizeof(strline_in), obj_file) != NULL) {
    296                 if (strline_in[0] == 'A')
    297                     areas_add(strline_in, c);
    298                 else if (strline_in[0] == 'S')
    299                     symbols_add(strline_in, c, files[c].obj_file_format);
    300             }
    301             fclose(obj_file);
    302 
    303         } // end: if valid file
    304         else {
    305             printf("BankPack: ERROR: failed to open file %s\n", files[c].name_in);
    306             exit(EXIT_FAILURE);
    307         }
    308     }
    309 
    310     obj_data_process(&filelist);
    311     files_set_output_name();
    312 }
    313 
    314 
    315 void files_rewrite(void) {
    316 
    317     uint32_t c;
    318     char * in_file_buf = NULL;
    319     char * strline_in  = NULL;
    320     FILE * out_file    = NULL;
    321     file_item * files  = (file_item *)filelist.p_array;
    322 
    323     // If linkerfile output is enabled, write it
    324     if (g_out_linkerfile_name[0] != '\0')
    325         files_write_linkerfile();
    326 
    327     // Process stored file names - (including unchanged ones since output may get new extensions or path)
    328     for (c = 0; c < filelist.count; c++) {
    329 
    330         in_file_buf = file_read_to_buffer(files[c].name_in);
    331         if (!in_file_buf)
    332             exit(EXIT_FAILURE);
    333 
    334         out_file = fopen(files[c].name_out, "w");
    335         if (!out_file) {
    336             printf("BankPack: ERROR: failed to open output file %s\n", files[c].name_out);
    337             exit(EXIT_FAILURE);
    338         }
    339 
    340         // Read one line at a time from a string buffer
    341         // Note: strtok will replace the \n chars with \0 on each split, so be sure to add those back
    342         strline_in = strtok(in_file_buf,"\n");
    343         while (strline_in != NULL) {
    344 
    345             // Only modify lines in flagged files
    346             if (files[c].rewrite_needed) {
    347 
    348                 if (!area_modify_and_write_to_file(strline_in, out_file, files[c].bank_num)) {
    349                     if (!symbol_modify_and_write_to_file(strline_in, out_file, files[c].bank_num, c, files[c].obj_file_format)) {
    350                         // Default is to write line with no changes
    351                         fprintf(out_file, "%s\n", strline_in);
    352                     }
    353                 }
    354             } else
    355                 fprintf(out_file, "%s\n", strline_in);
    356 
    357             // Read next line
    358             strline_in = strtok(NULL,"\n");
    359         }
    360 
    361         if (in_file_buf)
    362             free(in_file_buf);
    363         fclose(out_file);
    364     }
    365 
    366 }

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