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 --- src/cli.c | 6 +++++- src/cli.h | 1 + src/header.c | 27 +++++++++++++++++++++++++++ src/header.h | 8 +++++++- src/main.c | 20 +++++++++++++++++++- 5 files changed, 59 insertions(+), 3 deletions(-) (limited to 'src') 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; -- cgit v1.3