summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjakob <jakob@memeware.net>2017-05-09 18:53:41 -0400
committerjakob <jakob@memeware.net>2017-05-09 18:53:41 -0400
commitb38a284ffc56e292b79610bbd94ebaf086fb4421 (patch)
treed996496a0d00caed5b16b34dcecb91f8c5b64778
parent07347dcaa361607a924178eff787177e8cca6d68 (diff)
Began work on a 100%-compatible repacker
-rw-r--r--README.md9
-rw-r--r--src/cli.c6
-rw-r--r--src/cli.h1
-rw-r--r--src/header.c27
-rw-r--r--src/header.h8
-rw-r--r--src/main.c20
-rw-r--r--test/test_header.c8
-rw-r--r--test/test_header.h1
-rw-r--r--test/test_run.c1
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 <http://www.gnu.org/licenses/>. */
#include <stdbool.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -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);