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.