git.y1.nz

gbdk-2020

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

gbdk-support/png2hicolorgb/src/main.c

      1 // See LICENSE  file for license details
      2 
      3 #include <stdio.h>
      4 #include <string.h>
      5 #include <stdlib.h>
      6 #include <unistd.h>
      7 #include <stdbool.h>
      8 #include <stdint.h>
      9 
     10 
     11 #include "common.h"
     12 #include "path_ops.h"
     13 #include "options.h"
     14 #include "logging.h"
     15 #include "image_load.h"
     16 
     17 #include <hicolour.h>
     18 
     19 
     20 #define VERSION "version 1.4.2"
     21 
     22 #define HELP_HISTORICAL_CREDITS_STR \
     23         "Historical credits and info:\n" \
     24         "   Original Concept : Icarus Productions\n" \
     25         "   Original Code : Jeff Frohwein\n" \
     26         "   Full Screen Modification : Anon\n" \
     27         "   Adaptive Code : Glen Cook\n" \
     28         "   Windows Interface : Glen Cook\n" \
     29         "   Additional Windows Programming : Rob Jones\n" \
     30         "   Original Quantiser Code : Benny\n" \
     31         "   Quantiser Conversion : Glen Cook\n" \
     32         "\n"
     33 
     34 static void init(void);
     35 static void cleanup(void);
     36 static void display_help(void);
     37 static int  handle_args(int argc, char * argv[]);
     38 static bool handle_args_L_R_patterns(const char * str_opt_side, const char * str_value);
     39 #ifdef DRAG_AND_DROP_MODE
     40     static void set_drag_and_drop_mode_defaults(void);
     41 #endif
     42 
     43 image_data src_image;
     44 char filename_in[MAX_STR_LEN] = "";
     45 char opt_filename_out[MAX_STR_LEN] = "";
     46 char opt_varname[MAX_STR_LEN] = "";
     47 // bool opt_strip_output_filename_ext = true;
     48 
     49 bool show_help_and_exit = false;
     50 bool show_full_credits = false;
     51 
     52 
     53 static void init(void) {
     54     // Handle global init
     55     src_image.p_img_data = NULL;
     56     hicolor_init();
     57 }
     58 
     59 
     60 // Registered as an atexit handler
     61 static void cleanup(void) {
     62     // Handle any cleanup on exit
     63     // Free the image data
     64     if (src_image.p_img_data != NULL)
     65         free(src_image.p_img_data);
     66 
     67 }
     68 
     69 
     70 int main( int argc, char *argv[] )  {
     71 
     72     int ret = EXIT_FAILURE; // Default to failure on exit
     73 
     74     // Register cleanup with exit handler
     75     atexit(cleanup);
     76 
     77     init();
     78 
     79     #ifdef DRAG_AND_DROP_MODE
     80         // Non-interactive mode, set some reasonable default
     81         set_drag_and_drop_mode_defaults();
     82     #endif
     83 
     84     if (handle_args(argc, argv)) {
     85 
     86         if (show_help_and_exit) {
     87             ret = EXIT_SUCCESS;
     88         }
     89         else {
     90             // detect file extension
     91             if (matches_extension(filename_in, (char *)".png")) {
     92 
     93                 // Load source image (from first argument)
     94                 if (image_load(&src_image, filename_in, IMG_TYPE_PNG)) {
     95 
     96                     // If output filename is not specified, use source filename
     97                     if (opt_filename_out[0] == '\0')
     98                         strcpy(opt_filename_out, filename_in);
     99 
    100                     filename_remove_extension(opt_filename_out);
    101 
    102                     // If output symbol name is not specified, use output filename
    103                     if (opt_varname[0] == '\0')
    104                         strcpy(opt_varname, opt_filename_out);
    105 
    106                     hicolor_process_image(&src_image, opt_filename_out, opt_varname);
    107                     ret = EXIT_SUCCESS; // Exit with success
    108                 }
    109             }
    110         }
    111     }
    112 
    113     // Override exit code if was set during processing
    114     if (get_exit_error())
    115         ret = EXIT_FAILURE;
    116 
    117     // if (ret == EXIT_FAILURE)
    118     //     log_error("Error loading, converting or writing out the image: %s\n", filename_in);
    119 
    120     #ifdef DRAG_AND_DROP_MODE
    121         // Wait for input to keep the console window open after processing
    122         LOG("\n\nPress Any Key to Continue\n");
    123         getchar();
    124     #endif
    125 
    126     return ret;
    127 }
    128 
    129 
    130 static void display_help(void) {
    131     LOG(
    132         "\n"
    133         "png2hicolorgb input_image.png [options]\n"
    134         VERSION": bbbbbr. Based on Glen Cook's Windows GUI \"hicolour.exe\" 1.2\n"
    135         "Convert an image to Game Boy Hi-Color format\n"
    136         "\n"
    137         "Options\n"
    138         "-h         : Show this help\n"
    139         "-v*        : Set log level: \"-v\" verbose, \"-vQ\" quiet, \"-vE\" only errors, \"-vD\" debug\n"
    140         "-o <file>  : Set base output filename (otherwise from input image)\n"
    141         "-s <name>  : Set output variable/symbol name (otherwise derived from output filename)\n"
    142         // "--keepext   : Do not strip extension from output filename\n"
    143         "--csource  : Export C source format with incbins for data files\n"
    144         "--bank=N   : Set bank number for C source output where N is decimal bank number 1-511\n"
    145         "--type=N   : Set conversion type where N is one of below \n"
    146         "              1: Median Cut - No Dither (*Default*)\n"
    147         "              2: Median Cut - With Dither\n"
    148         "              3: Wu Quantiser (best quality)\n"
    149         "-p         : Show screen attribute pattern options (no processing)\n"
    150         "-L=N       : Set Left  side of screen palette arrangement where N is name listed below or decimal entry\n"
    151         "-R=N       : Set Right side of screen palette arrangement where N is name listed below or decimal entry\n"
    152         "             Named options for N: \"adaptive-fast\", \"adaptive-medium\", \"adaptive-best\" (-p for full options) \n"
    153         "--best     : Use highest quality conversion settings (--type=3 -L=adaptive-best -R=adaptive-best)\n"
    154         "--vaddrid  : Map uses vram id (128->255->0->127) instead of (*Default*) sequential tile order (0->255)\n"
    155         "--nodedupe : Turn off tile pattern deduplication\n"
    156         "--precompiled   : Export Palette data as pre-compiled executable loading code\n"
    157         "--palendbit     : Set unused bit .15 = 1 for last u16 entry in palette data indicating end (not in precompiled)\n"
    158         "--addendcolor=N : Append 32 x color N (hex BGR555) in pal data to clear BG for shorter images (64 bytes) (not in precompiled)\n"
    159         "\n"
    160         "Example 1: \"png2hicolorgb myimage.png\"\n"
    161         "Example 2: \"png2hicolorgb myimage.png --csource -o my_output_filename\"\n"
    162         "Example 2: \"png2hicolorgb myimage.png --palendbit --addendcolor 0x7FFF -o my_output_filename -s my_variable_name\"\n"
    163         "* Default settings provide good results. Better quality but slower: \"--type=3 -L=adaptive-best -R=adaptive-best\"\n"
    164         "\n"
    165    );
    166 
    167     if (show_full_credits)
    168         LOG(HELP_HISTORICAL_CREDITS_STR);
    169 }
    170 
    171 
    172 #ifdef DRAG_AND_DROP_MODE
    173 // Default options for Windows Drag and Drop recipient mode
    174 static void set_drag_and_drop_mode_defaults(void) {
    175 
    176     // Set some options here
    177 }
    178 #endif
    179 
    180 
    181 
    182 static int handle_args(int argc, char * argv[]) {
    183 
    184     int i;
    185 
    186     if( argc < 2 ) {
    187         display_help();
    188         return false;
    189     }
    190 
    191     // Copy input filename (if not preceded with option dash)
    192     if (argv[1][0] != '-')
    193         snprintf(filename_in, sizeof(filename_in), "%s", argv[1]);
    194 
    195     // Start at first optional argument, argc is zero based
    196     for (i = 1; i <= (argc -1); i++ ) {
    197 
    198         if ((strstr(argv[i], "-h") == argv[i]) || (strstr(argv[i], "-?") == argv[i])) {
    199             show_full_credits  = true;
    200             display_help();
    201             show_help_and_exit = true;
    202             return true;  // Don't parse further input when -h is used
    203         } else if (strstr(argv[i], "-p") == argv[i]) {
    204             LOG(HELP_CONV_PATTERN_STR);
    205             show_help_and_exit = true;
    206             return true;  // Don't parse further input when -h is used
    207 
    208         } else if (strstr(argv[i], "-vD") == argv[i]) {
    209             set_log_level(OUTPUT_LEVEL_DEBUG);
    210         } else if (strstr(argv[i], "-vE") == argv[i]) {
    211             set_log_level(OUTPUT_LEVEL_ONLY_ERRORS);
    212         } else if (strstr(argv[i], "-vQ") == argv[i]) {
    213             set_log_level(OUTPUT_LEVEL_ONLY_ERRORS);
    214         } else if (strstr(argv[i], "-v") == argv[i]) {
    215             set_log_level(OUTPUT_LEVEL_VERBOSE);
    216 
    217         } else if (strstr(argv[i], "--type=") == argv[i]) {
    218             uint8_t new_type = strtol(argv[i] + strlen("--type="), NULL, 10);
    219             if ((new_type < CONV_TYPE_MIN) || (new_type > CONV_TYPE_MAX)) {
    220                 LOG("Error: --type specified with invalid conversion setting: %d\n", new_type);
    221                 display_help();
    222                 show_help_and_exit = true;
    223                 return false; // Abort
    224             }
    225             else
    226                 hicolor_set_type(new_type);
    227         } else if (strstr(argv[i], "-L=") == argv[i]) {
    228             if (!handle_args_L_R_patterns((const char *)"-L=", argv[i] + strlen("-L="))) {
    229                 show_help_and_exit = true;
    230                 return false; // Abort
    231             }
    232 
    233         } else if (strstr(argv[i], "-R=") == argv[i]) {
    234             if (!handle_args_L_R_patterns((const char *)"-R=", argv[i] + strlen("-R="))) {
    235                 show_help_and_exit = true;
    236                 return false; // Abort
    237             }
    238 
    239         } else if (strstr(argv[i], "--best") == argv[i]) {
    240             hicolor_set_type(CONV_TYPE_WU);
    241             hicolor_set_convert_left_pattern(HICOLOR_PATTERN_ADAPTIVE_BEST);
    242             hicolor_set_convert_right_pattern(HICOLOR_PATTERN_ADAPTIVE_BEST);
    243 
    244 
    245         } else if (strstr(argv[i], "-o") == argv[i]) {
    246             if (i < (argc -1))
    247                 i++; // Move to next argument if one is available
    248             else {
    249                 LOG("Error: -o specified but filename is missing\n");
    250                 show_help_and_exit = true;
    251                 return false; // Abort
    252             }
    253 
    254             // Require filename to be present
    255             if (*argv[i] == '-') LOG("Warning: -o specified but filename (%s) has dash and looks like an option argument. Usage: -o my_base_output_filename\n", argv[i]);
    256             snprintf(opt_filename_out, sizeof(opt_filename_out), "%s", argv[i]);
    257 
    258         } else if (strstr(argv[i], "-s") == argv[i]) {
    259             if (i < (argc -1))
    260                 i++; // Move to next argument if one is available
    261             else {
    262                 LOG("Error: -s specified but variable name is missing\n");
    263                 show_help_and_exit = true;
    264                 return false; // Abort
    265             }
    266 
    267             // Require varname to be present
    268             if (*argv[i] == '-') LOG("Warning: -s specified but varname (%s) has dash and looks like an option argument. Usage: -s my_varname\n", argv[i]);
    269             snprintf(opt_varname, sizeof(opt_varname), "%s", argv[i]);
    270 
    271 
    272         } else if (strstr(argv[i], "--csource") == argv[i]) {
    273             opt_set_c_file_output(true);
    274 
    275         } else if (strstr(argv[i], "--bank=") == argv[i]) {
    276             opt_set_bank_num( strtol(argv[i] + strlen("--bank="), NULL, 10) );
    277             if ((opt_get_bank_num() < BANK_NUM_MIN) || (opt_get_bank_num() > BANK_NUM_MAX)) {
    278                 LOG("Error: Invalid bank number specified with --bank=%d\n", opt_get_bank_num());
    279                 display_help();
    280                 show_help_and_exit = true;
    281                 return false; // Abort
    282             }
    283 
    284         } else if (strstr(argv[i], "--vaddrid") == argv[i]) {
    285             opt_set_map_tile_order(OPT_MAP_TILE_ORDER_BY_VRAM_ID);
    286 
    287         } else if (strstr(argv[i], "--nodedupe") == argv[i]) {
    288             opt_set_tile_dedupe(false);
    289 
    290         } else if (strstr(argv[i], "--precompiled") == argv[i]) {
    291             opt_set_precompiled_palette(true);
    292 
    293         } else if (strstr(argv[i], "--palendbit") == argv[i]) {
    294             opt_set_pal_end_bit(true);
    295 
    296         } else if (strstr(argv[i], "--addendcolor=") == argv[i]) {
    297             // Color should be in BGR555 hex format, so white: 0x7FFFF, max red 0x7C00, max green 0x3E00, etc
    298             opt_set_enable_pal_end_color( strtol(argv[i] + strlen("--addendcolor="), NULL, 16) );
    299 
    300         } else if (argv[i][0] == '-') {
    301             ERR("Unknown argument: %s\n\n", argv[i]);
    302             display_help();
    303             return false;
    304         }
    305 
    306     }
    307 
    308 
    309     // Check and warn about option compatibility
    310     if (opt_get_pal_end_bit() && opt_get_precompiled_palette()) {
    311         LOG("Warning: --palendbit ignored when --precompiled is enabled\n");
    312     }
    313 
    314     if (opt_get_enable_pal_end_color() && opt_get_precompiled_palette()) {
    315         LOG("Warning: --addendcolor ignored when --precompiled is enabled\n");
    316     }
    317 
    318     return true;
    319 }
    320 
    321 
    322 static bool handle_args_L_R_patterns(const char * str_opt_side, const char * str_value) {
    323 
    324     if (*str_value == '\0') {
    325         ERR("Missing value for %s argument\n", str_opt_side);
    326         return false;
    327     }
    328 
    329     unsigned int pattern_num = hicolor_get_pattern_by_name(str_value);
    330 
    331     if (pattern_num == HICOLOR_PATTERN_NOT_FOUND_HAS_CHARS) {
    332         ERR("Invalid value for %s argument: %s\n", str_opt_side, str_value);
    333         return false;
    334     }
    335     else if (pattern_num == HICOLOR_PATTERN_NOT_FOUND) {
    336         // Try numeric conversion on option instead named conversion
    337         pattern_num = (unsigned int)strtoul(str_value, NULL, 10);
    338     }
    339 
    340     if (pattern_num > HICOLOR_PATTERN_OPT_MAX) {
    341         ERR("Invalid value for %s argument: %s\n", str_opt_side, str_value);
    342         return false;
    343     }
    344 
    345     // Got a valid option value, now apply it
    346     if (strstr(str_opt_side, "-L=") == str_opt_side)
    347         hicolor_set_convert_left_pattern(pattern_num);
    348     else
    349         hicolor_set_convert_right_pattern(pattern_num);
    350 
    351     return true;
    352 }

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