diff --git a/README.md b/README.md
index 4821b26..4b2dcf8 100644
--- a/README.md
+++ b/README.md
@@ -52,6 +52,9 @@ detailed information about emulation state while running, including register
values and memory contents. You can also pause emulation to set breakpoints and
change state.
-`--assemble ` (`-a`) converts z80 assembly source code into a
-`.gg` binary that can be run by crater. `--disassemble ` (`-d`)
-executes the opposite operation.
+`--assemble []` (`-a`) converts z80 assembly source code into a
+`.gg` binary that can be run by crater. `--disassemble []`
+(`-d`) executes the opposite operation. If no output file is given, crater will
+use the name of the input file, with the extension replaced with `.gg` for `-a`
+and `.s` for `-d`. By default, this will never overwrite the original filename;
+pass `--overwrite` (`-r`) to let crater do so.
diff --git a/crater.c b/crater.c
index 87de115..4e50b5c 100644
--- a/crater.c
+++ b/crater.c
@@ -24,7 +24,7 @@ int main(int argc, char *argv[])
#endif
if (config->assemble) {
- printf("Running assembler: %s -> %s.\n",config->src_path, config->dst_path);
+ printf("Running assembler: %s -> %s.\n", config->src_path, config->dst_path);
} else if (config->disassemble) {
printf("Running disassembler: %s -> %s.\n", config->src_path, config->dst_path);
} else {
diff --git a/src/config.c b/src/config.c
index 25197be..8b18090 100644
--- a/src/config.c
+++ b/src/config.c
@@ -34,9 +34,12 @@ static void print_help(const char *arg1)
" -g, --debug display information about emulation state while running,\n"
" including register and memory values; can also pause\n"
" emulation, set breakpoints, and change state\n"
-" -a, --assemble convert z80 assembly source code into a\n"
-" binary file that can be run by crater\n"
-" -d, --disassemble convert a binary file into z80 assembly code\n",
+" -a, --assemble [] convert z80 assembly source code into a\n"
+" binary file that can be run by crater\n"
+" -d, --disassemble [] convert a binary file into z80 assembly\n"
+" source code\n"
+" -r, --overwrite allow crater to write assembler output to the same\n"
+" filename as the input\n",
arg1);
}
@@ -160,6 +163,8 @@ static int parse_args(Config *config, int argc, char *argv[])
}
path = malloc(sizeof(char) * (strlen(arg) + 1));
+ if (!path)
+ OUT_OF_MEMORY()
strcpy(path, arg);
if (paths_read == 1) {
@@ -220,6 +225,8 @@ static int parse_args(Config *config, int argc, char *argv[])
config->rom_path = NULL;
}
config->disassemble = true;
+ } else if (!strcmp(arg, "r") || !strcmp(arg, "overwrite")) {
+ config->overwrite = true;
} else {
ERROR("unknown argument: %s", argv[i])
return CONFIG_EXIT_FAILURE;
@@ -243,30 +250,69 @@ static int parse_args(Config *config, int argc, char *argv[])
}
/*
+ If no output file is specified for the assembler, this function picks a
+ filename based on the input file, replacing its extension with '.gg' or
+ '.s' (or adding it, if none is present).
+*/
+static void guess_assembler_output_file(Config* config)
+{
+ char *src = config->src_path, *ptr = src + strlen(src) - 1,
+ *ext = config->assemble ? ".gg" : ".s";
+ size_t until_ext = ptr - src + 1;
+
+ do {
+ if (*ptr == '.') {
+ until_ext = ptr - src;
+ break;
+ }
+ } while (ptr-- >= src);
+
+ config->dst_path = malloc(sizeof(char) * (until_ext + 4));
+ if (!config->dst_path)
+ OUT_OF_MEMORY()
+ strcpy(stpncpy(config->dst_path, src, until_ext), ext);
+}
+
+/*
Ensure that the combination of arguments in a config object are valid.
+
+ Some modifications are made in the case of missing arguments, like guessing
+ the filenames for assembler output files.
*/
-bool sanity_check(Config* config)
+static bool sanity_check(Config* config)
{
+ bool assembler = config->assemble || config->disassemble;
+
if (config->fullscreen && config->scale > 1) {
ERROR("cannot specify a scale in fullscreen mode")
return false;
} else if (config->assemble && config->disassemble) {
ERROR("cannot assemble and disassemble at the same time")
return false;
- } else if ((config->assemble || config->disassemble) &&
- (config->debug || config->fullscreen || config->scale > 1)) {
+ } else if (assembler && (config->debug || config->fullscreen || config->scale > 1)) {
ERROR("cannot specify emulator options in assembler mode")
return false;
- } else if ((config->assemble || config->disassemble) &&
- !(config->src_path && config->dst_path)) {
- ERROR("assembler mode requires both an input and an output file")
+ } else if (assembler && !config->src_path) {
+ ERROR("assembler mode requires an input file")
+ return false;
+ }
+
+ if (assembler && !config->dst_path) {
+ guess_assembler_output_file(config);
+ }
+ if (assembler && !config->overwrite && !strcmp(config->src_path, config->dst_path)) {
+ ERROR("refusing to overwrite the assembler input file; pass -r to override")
return false;
}
+
return true;
}
/*
Create a new config object and load default values into it.
+
+ Return value is one of CONFIG_OK, CONFIG_EXIT_SUCCESS, CONFIG_EXIT_FAILURE
+ and indicates how the caller should proceed.
*/
int config_create(Config** config_ptr, int argc, char* argv[])
{
@@ -286,6 +332,7 @@ int config_create(Config** config_ptr, int argc, char* argv[])
config->rom_path = NULL;
config->src_path = NULL;
config->dst_path = NULL;
+ config->overwrite = false;
retval = parse_args(config, argc, argv);
if (retval == CONFIG_OK && !sanity_check(config))
@@ -337,5 +384,6 @@ void config_dump_args(Config* config)
DEBUG("- dst_path: %s", config->dst_path)
else
DEBUG("- dst_path: (null)")
+ DEBUG("- overwrite: %s", config->overwrite ? "true" : "false")
}
#endif
diff --git a/src/config.h b/src/config.h
index d8067dc..47fbff2 100644
--- a/src/config.h
+++ b/src/config.h
@@ -26,6 +26,7 @@ typedef struct {
char *rom_path;
char *src_path;
char *dst_path;
+ bool overwrite;
} Config;
/* Functions */
diff --git a/src/rom.c b/src/rom.c
index 109047a..2ba40fe 100644
--- a/src/rom.c
+++ b/src/rom.c
@@ -38,6 +38,10 @@ ROM* rom_open(const char *path)
return NULL;
}
rom->name = malloc(sizeof(char) * (strlen(path) + 1));
+ if (!rom->name) {
+ fclose(fp);
+ return NULL;
+ }
strcpy(rom->name, path);
// TODO: load data from file into a buffer