sle/src/main.c

258 lines
7.2 KiB
C

/* 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 <getopt.h>
#include <raylib.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>
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)
{
int error;
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 <path>)\n");
error = 1;
}
if (options->tileset_path[0] == '\0') {
fprintf(stderr,
"ERROR: no tileset path specified "
"(-tileset <path>)\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);
}