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.