diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt new file mode 100644 index 0000000..946eed7 --- /dev/null +++ b/tools/CMakeLists.txt @@ -0,0 +1,4 @@ +cmake_minimum_required(VERSION 3.0) +project(utils) +add_executable(container "container.c") +target_compile_options(container PUBLIC -g3) diff --git a/tools/container.c b/tools/container.c new file mode 100644 index 0000000..254eee3 --- /dev/null +++ b/tools/container.c @@ -0,0 +1,129 @@ +#include +#include +#include +#include +#include + +static const char *filename(const char *const path) +{ + const char *ret = &path[strlen(path)]; + + while (ret != path && *(ret - 1) != '\\' && *(ret - 1) != '/') + ret--; + + return ret; +} + +static int dump_file(const char *const path, const char *const outpath, + FILE *const out) +{ + int ret = -1; + FILE *const f = fopen(path, "rb"); + + if (!f) + { + fprintf(stderr, "%s: could not open %s: %s\n", + __func__, path, strerror(errno)); + goto end; + } + + const char *const bname = filename(path); + + if (fprintf(out, "%s", bname) < 0) + { + fprintf(stderr, "fprintf(f, %s): %s\n", path, strerror(errno)); + goto end; + } + else if (fputc('\0', out) == EOF) + { + perror("could not write null character"); + goto end; + } + else if (fseek(f, 0, SEEK_END)) + { + perror("fseek(SEEK_END)"); + goto end; + } + + /* This won't work on files larger than 2 GiB if sizeof sz == 4, + * but this is acceptable for a PS1 video game. */ + const long sz = ftell(f); + + if (fprintf(out, "%ld", sz) < 0) + { + perror("could not write file size"); + goto end; + } + else if (fputc('\0', out) == EOF) + { + perror("could not write null character"); + goto end; + } + else if (fseek(f, 0, SEEK_SET)) + { + fprintf(stderr, "%s: fseek(SEEK_SET): %s\n", __func__, strerror(errno)); + goto end; + } + + while (!feof(f)) + { + char c; + + if (!fread(&c, sizeof c, 1, f)) + { + if (ferror(f)) + { + fprintf(stderr, "%s: ferror\n", path); + goto end; + } + } + else if (fputc(c, out) == EOF) + { + fprintf(stderr, "could not dump %s to %s", path, outpath); + goto end; + } + } + + ret = 0; + +end: + if (f) + fclose(f); + + return ret; +} + +int main(const int argc, const char *const *const argv) +{ + int ret = EXIT_FAILURE; + size_t n_in = 0; + FILE *out = NULL; + + if (argc < 3) + { + fprintf(stderr, "%s [ ...] \n", *argv); + goto end; + } + + const char *const outpath = argv[argc - 1]; + + n_in = argc - 2; + + if (!(out = fopen(outpath, "wb"))) + { + fprintf(stderr, "could not open %s: %s\n", outpath, strerror(errno)); + goto end; + } + + for (size_t i = 0; i < n_in; i++) + if (dump_file(argv[i + 1], outpath, out)) + goto end; + + ret = EXIT_SUCCESS; + +end: + if (out) + fclose(out); + + return ret; +}