From b38a284ffc56e292b79610bbd94ebaf086fb4421 Mon Sep 17 00:00:00 2001 From: jakob Date: Tue, 9 May 2017 18:53:41 -0400 Subject: Began work on a 100%-compatible repacker --- README.md | 9 +++------ src/cli.c | 6 +++++- src/cli.h | 1 + src/header.c | 27 +++++++++++++++++++++++++++ src/header.h | 8 +++++++- src/main.c | 20 +++++++++++++++++++- test/test_header.c | 8 ++++++++ test/test_header.h | 1 + test/test_run.c | 1 + 9 files changed, 72 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 432a9d9..98beaf0 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -![Nekopack](https://raw.github.com/TsarFox/nekopack/master/Nekopack_Logo.png "Nekopack") -======== +# ![Nekopack](https://raw.github.com/TsarFox/nekopack/master/Nekopack_Logo.png "Nekopack") ## Software programmed and maintained by [Jakob.](http://jakob.space/) Nekopack is a work-in-progress attempt at reverse engineering the archive format used by Nekopara. @@ -8,8 +7,7 @@ Please use this software responsibly. I chose to develop and release this becaus Nekopack is free software, licensed under the [GNU General Public License.](http://gnu.org/licenses/gpl.html) -Compilation ------------ +## Compilation Nekopack can be compiled on Linux by running `make` from the repository's root directory. *BSD users will have to install and run `gmake` to build Nekopack. @@ -17,7 +15,6 @@ Nekopack can be compiled on Linux by running `make` from the repository's root d The test suite is run with `make test` or `gmake test`, depending on your platform. -TODO ----- +## TODO * Full unit testing. * 100%-compatible repacker. diff --git a/src/cli.c b/src/cli.c index 2e335af..78e0d88 100644 --- a/src/cli.c +++ b/src/cli.c @@ -76,6 +76,7 @@ struct params parse_args(int argc, char **argv) { {"verbose", no_argument, NULL, 'v'}, {"extract", no_argument, NULL, 'e'}, {"list", no_argument, NULL, 'l'}, + {"create", no_argument, NULL, 'c'}, {"output", no_argument, NULL, 'o'}, {"game", no_argument, NULL, 'g'}, {NULL, 0, NULL, 0} @@ -83,7 +84,7 @@ struct params parse_args(int argc, char **argv) { do { count++; - cur = getopt_long(argc, argv, "hVvelqo:g:", long_opts, &opt_index); + cur = getopt_long(argc, argv, "hVvelcqo:g:", long_opts, &opt_index); switch (cur) { case 'h': p.mode = HELP; @@ -106,6 +107,9 @@ struct params parse_args(int argc, char **argv) { break; case 'l': p.mode = LIST; + break; + case 'c': + p.mode = CREATE; } } while (cur >= 0); diff --git a/src/cli.h b/src/cli.h index b813ec6..73693d9 100644 --- a/src/cli.h +++ b/src/cli.h @@ -29,6 +29,7 @@ enum { VERSION, LIST, EXTRACT, + CREATE, }; /* Structure for storing options set from the command-line. */ diff --git a/src/header.c b/src/header.c index 82a181c..e2f09b2 100644 --- a/src/header.c +++ b/src/header.c @@ -18,6 +18,7 @@ along with Nekopack. If not, see . */ #include +#include #include #include @@ -60,3 +61,29 @@ struct header *read_header(struct stream *s) { } return h; } + + +/* Generates an XP3 header structure readable by Nekopara. */ +struct header *create_header(void) { + struct header *h = malloc(sizeof(struct header)); + if (h == NULL) return NULL; + + memcpy(h->magic, XP3_MAGIC, 11); + h->info_offset = 17; + h->version = 1; + h->flags = 0x80; + h->table_size = 0; + h->table_offset = 0; + return h; +} + + +/* Dumps the XP3 header specified by `h` into `fp`. */ +void dump_header(FILE *fp, struct header *h) { + fwrite(h->magic, 11, 1, fp); + fwrite(&h->info_offset, sizeof(uint64_t), 1, fp); + fwrite(&h->version, sizeof(uint32_t), 1, fp); + fwrite(&h->flags, sizeof(uint8_t), 1, fp); + fwrite(&h->table_size, sizeof(uint64_t), 1, fp); + fwrite(&h->table_offset, sizeof(uint64_t), 1, fp); +} diff --git a/src/header.h b/src/header.h index 8ccf930..ce72835 100644 --- a/src/header.h +++ b/src/header.h @@ -30,8 +30,8 @@ struct header { char magic[11]; /* Identifier for the archive. */ uint64_t info_offset; /* Offset to `table_size`. */ uint32_t version; /* Raw value containing the archive version. */ - uint64_t table_size; /* The size of the table section. (?) */ uint8_t flags; /* A flags variable for the archive. (?) */ + uint64_t table_size; /* The size of the table section. (?) */ uint64_t table_offset; /* Offset to the archive table. */ }; @@ -40,3 +40,9 @@ struct header { NULL is returned if the header contains an invalid magic number, or if the archive's version is not supported. */ struct header *read_header(struct stream *s); + +/* Generates an XP3 header structure readable by Nekopara. */ +struct header *create_header(void); + +/* Dumps the XP3 header specified by `h` into `fp`. */ +void dump_header(FILE *fp, struct header *h); diff --git a/src/main.c b/src/main.c index aaa1b63..566ea55 100644 --- a/src/main.c +++ b/src/main.c @@ -57,7 +57,8 @@ static void print_help(void) { "exit.\n\n" " -e, --extract\tExtract the contents of the archive. This is " "the default\n\t\t\taction if no mode argument is provided.\n" - " -l, --list\t\tList the contents of the archive.\n\n" + " -l, --list\t\tList the contents of the archive.\n" + " -c, --create\t\tCreate a new XP3 archive.\n\n" " -o, --output\t\tPath to extract files to.\n" " -g, --game\t\tGame the archive is from. Required for file " "decryption.\n" @@ -197,6 +198,20 @@ static void map_entries(char *path, struct params p) { } +/* Creates a new XP3 archive at the first path specified by `paths`, and + flattens files specified by any following paths into the archive. */ +static void create_archive(char **paths, struct params p) { + FILE *fp = fopen(paths[0], "wb+"); + if (fp == NULL) { + perror(paths[0]); + return; + } + + struct header *h = create_header(); + dump_header(fp, h); +} + + int main(int argc, char **argv) { struct params p = parse_args(argc, argv); switch (p.mode) { @@ -214,6 +229,9 @@ int main(int argc, char **argv) { case EXTRACT: for (int i = p.vararg_index; i < argc; i++) map_entries(argv[i], p); + break; + case CREATE: + create_archive(argv + p.vararg_index, p); } params_free(p); return EXIT_SUCCESS; diff --git a/test/test_header.c b/test/test_header.c index 0525614..9a1e680 100644 --- a/test/test_header.c +++ b/test/test_header.c @@ -45,3 +45,11 @@ char *test_header_read(void) { stream_free(s); return NULL; } + + +char *test_header_creation(void) { + struct header *h = create_header(); + mu_assert("Header allocation failed", h != NULL); + mu_assert("Incorrect XP3 header", !memcmp(h->magic, XP3_MAGIC, 11)); + return NULL; +} diff --git a/test/test_header.h b/test/test_header.h index 8a6eeb0..4a4574c 100644 --- a/test/test_header.h +++ b/test/test_header.h @@ -20,3 +20,4 @@ #pragma once char *test_header_read(void); +char *test_header_creation(void); diff --git a/test/test_run.c b/test/test_run.c index b63bcb5..edccb10 100644 --- a/test/test_run.c +++ b/test/test_run.c @@ -44,6 +44,7 @@ static char *run_all_tests(void) { mu_run_test(test_stream_xor); mu_run_test(test_stream_nav); mu_run_test(test_header_read); + mu_run_test(test_header_creation); mu_run_test(test_table_list); mu_run_test(test_table_elif); mu_run_test(test_table_file); -- cgit v1.3