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/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.