/* SPDX-License-Identifier: GPL-3.0-or-later */ /* Copyright (C) 2021 KikooDX */ #include "editing_area/main.h" #include "info.h" #include "options.h" #include "shared_data.h" #include "strtocolor.h" #include "strtoint.h" #include "tile_picker/main.h" #include #include #include #include #include #include #include #include #include int verbose = 0; static void *create_shared_memory(size_t size); static void overflow_prevention(void); /* work on optarg */ static void process_arguments(int argc, char **argv, struct Options *options); static int int_arg_min(int opt, int min); static void set_tileset_dimensions(struct Options *options); /* The editor fork to create two windows. One will be the level editor * and the other the tile selection. This is done to allow the user to * organize these panels in the way they want to. */ int main(int argc, char **argv) { pid_t child_process; struct SharedData *shared_data; struct Options options = options_defaults(); shared_data = (struct SharedData *)create_shared_memory( sizeof(struct SharedData)); shared_data->end_child = false; shared_data->selected_tile = 1; /* set log level for raylib */ SetTraceLogLevel(LOG_ERROR); /* process arguments */ process_arguments(argc, argv, &options); /* set non-user defined options */ set_tileset_dimensions(&options); /* forking here */ child_process = fork(); if (child_process < 0) { fprintf(stderr, "ERROR: process couldn't fork"); return EXIT_FAILURE; } else if (child_process == 0) { /* child process, start the tile picker */ return tile_picker_main(options, shared_data); } else { /* main process, start the editing area */ editing_area_main(options, shared_data); return EXIT_SUCCESS; } } static void *create_shared_memory(size_t size) { /* shared memory will be read & write */ int protection = PROT_READ | PROT_WRITE; /* anonymous and shared, only child process will be able to * access it */ int visibility = MAP_SHARED | MAP_ANONYMOUS; return mmap(NULL, size, protection, visibility, -1, 0); } static void overflow_prevention(void) { /* make sure arguments won't overflow */ if (strlen(optarg) > BUFFER_SIZE) { fprintf(stderr, "ERROR: option size %lu is " "bigger than maximum %d\n", strlen(optarg), BUFFER_SIZE); exit(EXIT_FAILURE); } } static void process_arguments(int argc, char **argv, struct Options *options) { int opt; while (1) { const struct option long_options[] = { {"verbose", no_argument, &verbose, 1}, {"create", no_argument, &options->level_create, 1}, {"level", required_argument, 0, 'l'}, {"tileset", required_argument, 0, 't'}, {"tile-width", required_argument, 0, 'w'}, {"tile-height", required_argument, 0, 'h'}, {"tile-first", required_argument, 0, 'I'}, {"level-width", required_argument, 0, 'i'}, {"level-height", required_argument, 0, 'e'}, {"editor-scale", required_argument, 0, 's'}, {"editor-width", required_argument, 0, 'W'}, {"editor-height", required_argument, 0, 'H'}, {"editor-fps", required_argument, 0, 'f'}, {"editor-off-x", required_argument, 0, 'o'}, {"editor-off-y", required_argument, 0, 'O'}, {"editor-bg-color", required_argument, 0, 'b'}, {"picker-scale", required_argument, 0, 'S'}, {"picker-fps", required_argument, 0, 'F'}, {"picker-padding", required_argument, 0, 'p'}, {"picker-bg-color", required_argument, 0, 'B'}, {0, 0, 0, 0}, }; /* getopt_long stores the option index here */ int option_index = 0; opt = getopt_long_only(argc, argv, "", long_options, &option_index); /* detect the end of the options */ if (opt == -1) break; switch (opt) { case 'l': /* level */ overflow_prevention(); strcpy(options->level_path, optarg); break; case 't': /* tileset */ overflow_prevention(); strcpy(options->tileset_path, optarg); break; case 'w': /* tile width */ options->tile_width = int_arg_min(opt, 1); break; case 'h': /* tile height */ options->tile_height = int_arg_min(opt, 1); break; case 'I': /* first tile index */ options->tile_first = int_arg_min(opt, 0); break; case 'i': /* new level width */ options->new_level_width = int_arg_min(opt, 1); break; case 'e': /* new level height */ options->new_level_height = int_arg_min(opt, 1); break; case 's': /* editor scale */ options->editor_scale = int_arg_min(opt, 1); break; case 'W': /* editor width */ options->editor_width = int_arg_min(opt, 1); break; case 'H': /* editor height */ options->editor_height = int_arg_min(opt, 1); break; case 'f': /* editor target FPS */ options->editor_target_fps = int_arg_min(opt, 1); break; case 'o': /* editor draw offset x */ options->editor_draw_offset_x = strtoint(optarg); break; case 'O': /* editor draw offset y */ options->editor_draw_offset_y = strtoint(optarg); break; case 'b': /* editor background color */ options->editor_bg_color = strtocolor(optarg); break; case 'S': /* picker scale */ options->picker_scale = int_arg_min(opt, 1); break; case 'F': /* picker target FPS */ options->picker_target_fps = int_arg_min(opt, 1); break; case 'p': /* picker padding */ options->picker_padding = int_arg_min(opt, 0); break; case 'B': /* picker background color */ options->picker_bg_color = strtocolor(optarg); break; case '?': /* getopt_long already printed an error message */ exit(EXIT_FAILURE); break; default: break; } } /* missing arguments? */ { int error = 0; if (options->level_path[0] == '\0') { fprintf(stderr, "ERROR: no level path " "specified (-level )\n"); error = 1; } if (options->tileset_path[0] == '\0') { fprintf(stderr, "ERROR: no tileset path specified " "(-tileset )\n"); error = 1; } if (error) exit(EXIT_FAILURE); } } static int int_arg_min(int opt, int min) { const int value = strtoint(optarg); if (value < min) { fprintf(stderr, "ERROR: minimum value for option -%c is %d, " "got %d\n", opt, min, value); exit(EXIT_FAILURE); } return value; } static void set_tileset_dimensions(struct Options *options) { const Image tileset = LoadImage(options->tileset_path); if (tileset.width < 0 || tileset.height < 0) { fprintf(stderr, "ERROR: can't load tileset '%s'\n", options->tileset_path); exit(EXIT_FAILURE); } /* set options */ /* tileset_width */ options->tileset_width = tileset.width / options->tile_width; /* tileset_height */ options->tileset_height = tileset.height / options->tile_height; /* picker_window_width */ options->picker_window_width = (options->tile_width + options->picker_padding) * options->tileset_width + options->picker_padding; /* picker_window_height */ options->picker_window_height = (options->tile_height + options->picker_padding) * options->tileset_height + options->picker_padding; /* free memory */ UnloadImage(tileset); }