diff --git a/README.md b/README.md index d95b834..aa6e87a 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,19 @@ Only OS X and Linux are tested. You'll need a decent compiler that supports C11 (gcc, clang) and SDL 2. Using Homebrew, you can `brew install sdl2`; using apt, you can `apt-get install libsdl2-dev`. -Run `make` and then `./crater`. To build the development version with debug -symbols (they can exist simultaneously), run `make DEBUG=1` and then +Run `make` to create `./crater`. To build the development version with debug +symbols (they can exist simultaneously), run `make DEBUG=1`, which creates `./crater-dev`. + +Usage +----- + +Running `./crater` without arguments will display a list of ROM images located +in the `roms/` directory, and then ask the user to pick one, or enter their own +ROM path. You can provide a path directly with `./crater path/to/rom`. + +Add or symlink ROMs to `roms/` at your leisure. Note that they should end in +`.gg` or `.bin`. + +`./crater -h` gives (fairly basic) command-line usage, and `./crater -v` gives +the current version. diff --git a/crater.c b/crater.c index 94ff8e6..b877833 100644 --- a/crater.c +++ b/crater.c @@ -1,10 +1,160 @@ /* Copyright (C) 2014 Ben Kurtovic Released under the terms of the MIT License. See LICENSE for details. */ +#include +#include +#include #include -#include +#include +#include +#include -int main(int argc, char* argv[]) { - printf("Hello, world!\n"); +#include "src/errors.h" +#include "src/rom.h" +#include "src/version.h" + +#define ROMS_DIR "roms" + +/* Print command-line help/usage. */ +static void print_help(char *arg1) +{ + printf("%s [--help|-h] [--version|-v] [rom_path]\n", arg1); +} + +/* Print crater's version. */ +static void print_version() +{ + printf("crater %s\n", CRATER_VERSION); +} + +/* Parse the command-line arguments for any special flags. */ +static void parse_args(int argc, char *argv[]) +{ + char *arg; + int i; + + for (i = 1; i < argc; i++) { + arg = argv[i]; + if (arg[0] != '-') + continue; + do + arg++; + while (arg[0] == '-'); + + if (!strcmp(arg, "h") || !strcmp(arg, "help")) { + print_help(argv[0]); + exit(0); + } else if (!strcmp(arg, "v") || !strcmp(arg, "version")) { + print_version(); + exit(0); + } else { + printf("Error: unknown argument: %s\n", argv[i]); + exit(1); + } + } +} + +/* Return whether the given string ends with the given suffix. */ +static bool ends_with(char *input, char *suffix) +{ + size_t ilen = strlen(input), slen = strlen(suffix); + + if (ilen < slen) + return false; + return strcmp(input + (ilen - slen), suffix) == 0; +} + +/* Load all potential ROM files in roms/ into a data structure. */ +static int load_rom_paths(char ***path_ptr) +{ + DIR *dirp; + struct dirent *entry; + char **paths = NULL, *path; + int psize = 8, npaths = 0; + + dirp = opendir(ROMS_DIR); + if (dirp) { + paths = malloc(sizeof(char*) * psize); + if (!paths) + out_of_memory(); + while ((entry = readdir(dirp))) { + path = entry->d_name; + if (ends_with(path, ".gg") || ends_with(path, ".bin")) { + if (npaths >= psize) { + paths = realloc(paths, sizeof(char*) * (psize *= 2)); + if (!paths) + out_of_memory(); + } + paths[npaths] = malloc(sizeof(char*) * + (strlen(path) + strlen(ROMS_DIR) + 1)); + if (!paths[npaths]) + out_of_memory(); + strcpy(paths[npaths], ROMS_DIR "/"); + strcat(paths[npaths], path); + npaths++; + } + } + closedir(dirp); + } else { + if (errno == ENOENT) + printf("Warning: couldn't find roms/ directory.\n"); + else + perror("Warning: couldn't open roms/ directory"); + } + *path_ptr = paths; + return npaths; +} + +/* Find all potential ROM files in the roms/ directory, then ask the user which + * one they want to run. + */ +static char* get_rom_path_from_user() +{ + char **paths, *path, *input = NULL; + int npaths, i; + long int index; + size_t size = 0; + ssize_t len; + + npaths = load_rom_paths(&paths); + for (i = 0; i < npaths; i++) + printf("[%2d] %s\n", i + 1, paths[i]); + if (npaths) + printf("Enter a ROM number from above, or the path to a ROM image: "); + else + printf("Enter the path to a ROM image: "); + + len = getline(&input, &size, stdin); + if (!input) + out_of_memory(); + if (len > 0 && input[len - 1] == '\n') + input[len - 1] = '\0'; + index = strtol(input, NULL, 10); + if (index < 1 || index > npaths) + path = input; + else + path = paths[index - 1]; + + for (i = 0; i < npaths; i++) { + if (paths[i] != path) + free(paths[i]); + } + if (paths) + free(paths); + return path; +} + +int main(int argc, char *argv[]) +{ + char *rom_path; + + parse_args(argc, argv); + rom_path = argc > 1 ? argv[1] : get_rom_path_from_user(); + + // TODO: main logic hook here + printf("Loading ROM: %s\n", rom_path); + + if (argc <= 1) + free(rom_path); return 0; } diff --git a/src/errors.c b/src/errors.c new file mode 100644 index 0000000..4d26d34 --- /dev/null +++ b/src/errors.c @@ -0,0 +1,13 @@ +/* Copyright (C) 2014 Ben Kurtovic + Released under the terms of the MIT License. See LICENSE for details. */ + +#include +#include + +#include "errors.h" + +/* Called after an out-of-memory error. Prints a message and dies. */ +void out_of_memory() { + printf("Error: couldn't allocate memory.\n"); + exit(1); +} diff --git a/src/errors.h b/src/errors.h new file mode 100644 index 0000000..a61d03e --- /dev/null +++ b/src/errors.h @@ -0,0 +1,6 @@ +/* Copyright (C) 2014 Ben Kurtovic + Released under the terms of the MIT License. See LICENSE for details. */ + +#pragma once + +void out_of_memory(); diff --git a/src/rom.c b/src/rom.c new file mode 100644 index 0000000..473c1e9 --- /dev/null +++ b/src/rom.c @@ -0,0 +1,22 @@ +/* Copyright (C) 2014 Ben Kurtovic + Released under the terms of the MIT License. See LICENSE for details. */ + +#include + +#include "rom.h" +#include "errors.h" + +rom_type* load_rom(char *path) +{ + rom_type *rom; + + rom = malloc(sizeof(rom_type)); + if (!rom) + out_of_memory(); + return rom; +} + +void unload_rom(rom_type *rom) +{ + free(rom); +} diff --git a/src/rom.h b/src/rom.h new file mode 100644 index 0000000..8c5867c --- /dev/null +++ b/src/rom.h @@ -0,0 +1,9 @@ +/* Copyright (C) 2014 Ben Kurtovic + Released under the terms of the MIT License. See LICENSE for details. */ + +#pragma once + +typedef struct { + char* name; + char* data; +} rom_type; diff --git a/src/version.h b/src/version.h new file mode 100644 index 0000000..aa85b52 --- /dev/null +++ b/src/version.h @@ -0,0 +1,6 @@ +/* Copyright (C) 2014 Ben Kurtovic + Released under the terms of the MIT License. See LICENSE for details. */ + +#pragma once + +#define CRATER_VERSION "0.1.dev" diff --git a/src/z80.h b/src/z80.h index 9916cad..afae3a2 100644 --- a/src/z80.h +++ b/src/z80.h @@ -3,6 +3,6 @@ #pragma once -struct register_t { +typedef struct { int pc; -}; +} register_type;