gbdk-2020 | GameBoy Development Kit |
| download: https://git.y1.nz/archives/gbdk.tar.gz | |
| README | Files | Log | Refs | LICENSE |
gbdk-support/gbcompress/main.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 <stdint.h>
10
11 #include "common.h"
12 #include "gbcompress.h"
13 #include "rlecompress.h"
14 #include "files.h"
15 #include "files_c_source.h"
16
17 #include "zx0/zx0_libsalvador.h"
18
19 #define MAX_STR_LEN 4096
20
21 #define COMPRESSION_TYPE_GB 0
22 #define COMPRESSION_TYPE_RLE_BLOCK 1
23 #define COMPRESSION_TYPE_ZX0 2
24 #define COMPRESSION_TYPE_DEFAULT COMPRESSION_TYPE_GB
25
26 char filename_in[MAX_STR_LEN] = {'\0'};
27 char filename_out[MAX_STR_LEN] = {'\0'};
28
29 uint8_t * p_buf_in = NULL;
30 uint8_t * p_buf_out = NULL;
31
32 #define VAR_IS_CONST_YES true
33 #define VAR_IS_CONST_NO false
34
35 #define OPT_HELP "-h"
36 #define OPT_DECOMPRESS "-d"
37 #define OPT_VERBOSE "-v"
38 #define OPT_CIN "--cin"
39 #define OPT_COUT "--cout"
40 #define OPT_VARNAME "--varname="
41 #define OPT_BANKREFNAME "--bankrefname="
42 #define OPT_ALG "--alg="
43 #define OPT_BANK "--bank="
44
45
46 bool opt_mode_compress = true;
47 bool opt_verbose = false;
48 int opt_compression_type = COMPRESSION_TYPE_DEFAULT;
49 bool opt_c_source_input = false;
50 bool opt_c_source_output = false;
51 char opt_c_source_output_var_name[MAX_STR_LEN] = "var_name";
52 char opt_c_source_output_bankref_name[MAX_STR_LEN] = {'\0'};
53 uint16_t opt_bank_num = BANK_NUM_ROM_UNSET;
54
55 static void display_help(void);
56 static int handle_args(int argc, char * argv[]);
57 static int compress(void);
58 static int decompress(void);
59 void cleanup(void);
60
61
62 static void display_help(void) {
63 fprintf(stdout,
64 "gbcompress [options] infile outfile\n"
65 "Use: compress a binary file and write it out.\n"
66 "\n"
67 "Options\n"
68 "-h : Show this help screen\n"
69 "-d : Decompress (default is compress)\n"
70 "-v : Verbose output\n"
71 "--cin : Read input as .c source format (8 bit char ONLY, uses first array found)\n"
72 "--cout : Write output in .c / .h source format (8 bit char ONLY) \n"
73 "--varname=<NAME> : specify variable name for c source output\n"
74 "--bankrefname=<NAME> : specify bankref name for c source output\n"
75 "--alg=<type> : specify compression type: 'zx0', 'rle', 'gb' (default)\n"
76 "--bank=<num> : Add Bank Ref: %d - %d (default is none, with --cout only)\n"
77 "Example: \"gbcompress binaryfile.bin compressed.bin\"\n"
78 "Example: \"gbcompress -d compressedfile.bin decompressed.bin\"\n"
79 "Example: \"gbcompress --alg=rle binaryfile.bin compressed.bin\"\n"
80 "\n"
81 "The default compression (gb) is the type used by gbtd/gbmb\n"
82 "The rle compression is Amiga IFF style\n",
83 BANK_NUM_ROM_MIN, BANK_NUM_ROM_MAX
84 );
85 }
86
87
88 int handle_args(int argc, char * argv[]) {
89
90 int i = 1; // start at first arg
91
92 if( argc < 3 ) {
93 display_help();
94 return false;
95 }
96
97 // Start at first optional argument
98 // Last two arguments *must* be input/output files
99 for (i = 1; i < (argc - 2); i++ ) {
100
101 if (argv[i][0] == '-') {
102 if (strstr(argv[i], OPT_HELP) == argv[i]) {
103 display_help();
104 return false; // Don't parse input when -h is used
105 } else if (strstr(argv[i], OPT_VERBOSE) == argv[i]) {
106 opt_verbose = true;
107 } else if (strstr(argv[i], OPT_CIN) == argv[i]) {
108 opt_c_source_input = true;
109 } else if (strstr(argv[i], OPT_COUT) == argv[i]) {
110 opt_c_source_output = true;
111 } else if (strstr(argv[i], OPT_VARNAME) == argv[i]) {
112 snprintf(opt_c_source_output_var_name, sizeof(opt_c_source_output_var_name), "%s", argv[i] + strlen(OPT_VARNAME));
113 } else if (strstr(argv[i], OPT_BANKREFNAME) == argv[i]) {
114 snprintf(opt_c_source_output_bankref_name, sizeof(opt_c_source_output_bankref_name), "%s", argv[i] + strlen(OPT_BANKREFNAME));
115 } else if (strstr(argv[i], OPT_ALG"gb") == argv[i]) {
116 opt_compression_type = COMPRESSION_TYPE_GB;
117 } else if (strstr(argv[i], OPT_ALG"rle") == argv[i]) {
118 opt_compression_type = COMPRESSION_TYPE_RLE_BLOCK;
119 } else if (strstr(argv[i], OPT_ALG"zx0") == argv[i]) {
120 opt_compression_type = COMPRESSION_TYPE_ZX0;
121 } else if (strstr(argv[i], OPT_DECOMPRESS) == argv[i]) {
122 opt_mode_compress = false;
123 } else if (strstr(argv[i], OPT_BANK) == argv[i]) {
124 opt_bank_num = atoi(argv[i] + strlen(OPT_BANK));
125 if ((opt_bank_num < BANK_NUM_ROM_MIN) || (opt_bank_num > BANK_NUM_ROM_MAX)) {
126 printf("gbcompress: Warning: Bank Num %d outside of range %d - %d\n", opt_bank_num, BANK_NUM_ROM_MIN, BANK_NUM_ROM_MAX);
127 return false;
128 }
129 } else
130 printf("gbcompress: Warning: Ignoring unknown option %s\n", argv[i]);
131 }
132 }
133
134 // If custom bankref name not specified, copy from varname
135 if (opt_c_source_output_bankref_name[0] == '\0') {
136 snprintf(opt_c_source_output_bankref_name, sizeof(opt_c_source_output_bankref_name), "%s", opt_c_source_output_var_name);
137 }
138
139 // Copy input and output filenames from last two arguments
140 // if not preceded with option dash
141 if (argv[i][0] != '-') {
142 snprintf(filename_in, sizeof(filename_in), "%s", argv[i++]);
143
144 if (argv[i][0] != '-') {
145 snprintf(filename_out, sizeof(filename_out), "%s", argv[i++]);
146 return true;
147 }
148 }
149
150 return false;
151 }
152
153
154 void cleanup(void) {
155 if (p_buf_in != NULL) {
156 free(p_buf_in);
157 p_buf_in = NULL;
158 }
159 if (p_buf_out != NULL) {
160 free(p_buf_out);
161 p_buf_out = NULL;
162 }
163 }
164
165
166 static int compress() {
167
168 uint32_t buf_size_in = 0;
169 uint32_t buf_size_out = 0;
170 uint32_t out_len = 0;
171 bool result = false;
172
173 if (opt_c_source_input)
174 p_buf_in = file_read_c_input_into_buffer(filename_in, &buf_size_in);
175 else
176 p_buf_in = file_read_into_buffer(filename_in, &buf_size_in);
177
178 // Allocate buffer output buffer same size as input
179 // It can grow more in gbdecompress_buf()
180 if (opt_compression_type == COMPRESSION_TYPE_ZX0)
181 buf_size_out = salvador_get_max_compressed_size(buf_size_in);
182 else
183 buf_size_out = buf_size_in;
184 p_buf_out = malloc(buf_size_out);
185
186 if (!p_buf_out) return EXIT_FAILURE;
187
188
189 if ((p_buf_in) && (buf_size_in > 0)) {
190
191 if (opt_compression_type == COMPRESSION_TYPE_GB)
192 out_len = gbcompress_buf(p_buf_in, buf_size_in, &p_buf_out, buf_size_out);
193 else if (opt_compression_type == COMPRESSION_TYPE_RLE_BLOCK)
194 out_len = rlecompress_buf(p_buf_in, buf_size_in, &p_buf_out, buf_size_out);
195 else if (opt_compression_type == COMPRESSION_TYPE_ZX0) {
196 // FLG_IS_INVERTED Flag, no offset, no dictionary, no progress callback, no stats
197 memset(p_buf_out, 0, buf_size_out);
198 out_len = salvador_compress(p_buf_in, p_buf_out, buf_size_in, buf_size_out, FLG_IS_INVERTED, 0, 0, NULL, NULL);
199 } else
200 return EXIT_FAILURE;
201
202 if (out_len > 0) {
203
204 if (opt_c_source_output) {
205 c_source_set_sizes(out_len, buf_size_in); // compressed, decompressed
206 result = file_write_c_output_from_buffer(filename_out, p_buf_out, out_len,
207 opt_c_source_output_var_name, opt_c_source_output_bankref_name,
208 VAR_IS_CONST_YES, opt_bank_num);
209 }
210 else
211 result = file_write_from_buffer(filename_out, p_buf_out, out_len);
212
213 if (result) {
214 if (opt_verbose)
215 printf("Compressed: %d bytes -> %d bytes (%%%.2f)\n", buf_size_in, out_len, ((double)out_len / (double)buf_size_in) * 100);
216 return EXIT_SUCCESS;
217 }
218 }
219 }
220
221 return EXIT_FAILURE;
222 }
223
224
225 static int decompress() {
226
227 uint32_t buf_size_in = 0;
228 uint32_t buf_size_out = 0;
229 uint32_t out_len = 0;
230 bool result = false;
231
232 if (opt_c_source_input)
233 p_buf_in = file_read_c_input_into_buffer(filename_in, &buf_size_in);
234 else
235 p_buf_in = file_read_into_buffer(filename_in, &buf_size_in);
236
237 // Allocate buffer output buffer 3x size of input
238 // It can grow more in gbdecompress_buf()
239 if (opt_compression_type == COMPRESSION_TYPE_ZX0)
240 buf_size_out = salvador_get_max_decompressed_size(p_buf_in, buf_size_in, 0); // No flags
241 else
242 buf_size_out = buf_size_in * 3;
243
244 p_buf_out = malloc(buf_size_out);
245
246 if ((p_buf_in) && (p_buf_out) && (buf_size_in > 0)) {
247
248 if (opt_compression_type == COMPRESSION_TYPE_GB)
249 out_len = gbdecompress_buf(p_buf_in, buf_size_in, &p_buf_out, buf_size_out);
250 else if (opt_compression_type == COMPRESSION_TYPE_RLE_BLOCK)
251 out_len = rledecompress_buf(p_buf_in, buf_size_in, &p_buf_out, buf_size_out);
252 else if (opt_compression_type == COMPRESSION_TYPE_ZX0) {
253 memset(p_buf_out, 0, buf_size_out);
254 out_len = salvador_decompress(p_buf_in, p_buf_out, buf_size_in, buf_size_out, 0, FLG_IS_INVERTED); // No dictionary, FLG_IS_INVERTED Flag
255 } else
256 return EXIT_FAILURE;
257
258 if (out_len > 0) {
259
260 if (opt_c_source_output) {
261 c_source_set_sizes(buf_size_in, out_len); // compressed, decompressed
262 result = file_write_c_output_from_buffer(filename_out, p_buf_out, out_len,
263 opt_c_source_output_var_name, opt_c_source_output_bankref_name,
264 VAR_IS_CONST_YES, opt_bank_num);
265 }
266 else {
267 result = file_write_from_buffer(filename_out, p_buf_out, out_len);
268 }
269
270 if (result) {
271 if (opt_verbose)
272 printf("Decompressed: %d bytes -> %d bytes (compression was %%%.2f)\n", buf_size_in, out_len, ((double)buf_size_in / (double)out_len) * 100);
273 return EXIT_SUCCESS;
274 }
275 } else if (out_len == 0) {
276 printf("gbcompress: ERROR: Failed to decompress any bytes (from %d input). Data may not be compressed\n", buf_size_in);
277 }
278 }
279
280 return EXIT_FAILURE;
281 }
282
283
284 int main( int argc, char *argv[] ) {
285
286 // Exit with failure by default
287 int ret = EXIT_FAILURE;
288
289 // Register cleanup with exit handler
290 atexit(cleanup);
291
292 if (handle_args(argc, argv)) {
293
294 if (opt_mode_compress)
295 ret = compress();
296 else
297 ret = decompress();
298 }
299 cleanup();
300
301 return ret; // Exit with failure by default
302 }
This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.