diff options
| -rw-r--r-- | src/compress.c | 57 | ||||
| -rw-r--r-- | src/compress.h | 25 | ||||
| -rw-r--r-- | src/header.c | 25 | ||||
| -rw-r--r-- | src/io.c | 29 | ||||
| -rw-r--r-- | src/io.h | 15 | ||||
| -rw-r--r-- | src/table.c | 73 | ||||
| -rw-r--r-- | src/table.h | 56 | ||||
| -rw-r--r-- | test/minunit.h | 1 | ||||
| -rw-r--r-- | test/test_header.c | 9 | ||||
| -rw-r--r-- | test/test_header.h | 1 | ||||
| -rw-r--r-- | test/test_io.c | 33 | ||||
| -rw-r--r-- | test/test_io.h | 3 | ||||
| -rw-r--r-- | test/test_run.c | 5 | ||||
| -rw-r--r-- | test/test_table.c | 36 | ||||
| -rw-r--r-- | test/test_table.h | 22 |
15 files changed, 380 insertions, 10 deletions
diff --git a/src/compress.c b/src/compress.c new file mode 100644 index 0000000..c27937b --- /dev/null +++ b/src/compress.c @@ -0,0 +1,57 @@ +/* compress.c -- Wrappers for compression and decompression with zlib. + + Copyright (C) 2017 Jakob Tsar-Fox, All Rights Reserved. + + This file is part of Nekopack. + + Nekopack is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation, either version 3 of the License, or (at + your option) any later version. + + Nekopack is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Nekopack. If not, see <http://www.gnu.org/licenses/>. */ + +#include <zlib.h> + +#include "io.h" + + +/* Inflates `s` into a newly allocated stream structure. */ +struct stream *inflate_stream(struct stream *s, size_t inflated_len) { + z_stream strm; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.avail_in = 0; + strm.next_in = Z_NULL; + + if (inflateInit(&strm) != Z_OK) + return NULL; + + int ret; + struct stream *n = stream_new(inflated_len); + + do { + strm.avail_in = s->len; + strm.next_in = (Bytef *) s->_cur; + do { + strm.avail_out = inflated_len; + strm.next_out = (Bytef *) n->_cur; + ret = inflate(&strm, Z_NO_FLUSH); + if (ret != Z_OK) { + stream_free(n); + inflateEnd(&strm); + return NULL; + } + } while (strm.avail_out == 0); + } while (ret != Z_STREAM_END); + + inflateEnd(&strm); + return n; +} diff --git a/src/compress.h b/src/compress.h new file mode 100644 index 0000000..9d56e74 --- /dev/null +++ b/src/compress.h @@ -0,0 +1,25 @@ +/* compress.h -- Wrappers for compression and decompression with zlib. + + Copyright (C) 2017 Jakob Tsar-Fox, All Rights Reserved. + + This file is part of Nekopack. + + Nekopack is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation, either version 3 of the License, or (at + your option) any later version. + + Nekopack is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Nekopack. If not, see <http://www.gnu.org/licenses/>. */ + +#pragma once + +#include "io.h" + +/* Decompresses `s` into a newly allocated stream structure. */ +struct stream *inflate_stream(struct stream *s, size_t inflated_len); diff --git a/src/header.c b/src/header.c index e039447..0161775 100644 --- a/src/header.c +++ b/src/header.c @@ -28,15 +28,26 @@ static bool is_xp3(struct xp3_header *h); static bool is_supported(struct xp3_header *h); -/* Reads data from the given stream into a newly allocated xp3_header - structure. The stream is assumed to be at the header's beginning. - NULL is returned if the header contains an invalid magic number, or - if the archive's version is not supported. */ +/* Reads from the given stream into a newly allocated xp3_header + structure. NULL is returned if the header contains an invalid magic + number, or if the archive's version is not supported. */ struct xp3_header *read_header(struct stream *s) { struct xp3_header *h = malloc(sizeof(struct xp3_header)); if (h == NULL) return NULL; - stream_read(h, s, sizeof(struct xp3_header)); - if (!is_xp3(h) || !is_supported(h)) {free(h); return NULL;} + + /* The header structure can't be read into directly because of + potential alignment issues. */ + stream_read(h->magic, s, 11); + stream_read(&h->info_offset, s, sizeof(uint64_t)); + stream_read(&h->version, s, sizeof(uint32_t)); + stream_read(&h->table_size, s, sizeof(uint64_t)); + stream_read(&h->flags, s, sizeof(uint8_t)); + stream_read(&h->table_offset, s, sizeof(uint64_t)); + + if (!is_xp3(h) || !is_supported(h)) { + free(h); + return NULL; + } return h; } @@ -47,7 +58,7 @@ static bool is_xp3(struct xp3_header *h) { } -/* Checks that the archive is of XP3 version 2. */ +/* Checks that the archive's version is supported. */ static bool is_supported(struct xp3_header *h) { return h->version == 1; } @@ -62,10 +62,37 @@ void stream_write(struct stream *s, void *src, size_t n) { ptrdiff_t dist = (uintptr_t) s->_cur - (uintptr_t) s->_start; switch(s->_loc) { case HEAP: - s->_start = realloc(s->_start, s->len * 2); + s->len *= 2; + s->_start = realloc(s->_start, s->len); s->_cur = s->_start + dist; } } memcpy(s->_cur, src, n); s->_cur += n; } + + +/* Applies an initial and primary key to the given stream, effectively + encrypting or decrypting it. */ +void stream_xor(struct stream *s, uint8_t initial, uint8_t primary) { + *s->_start ^= initial; + for (char *p = s->_start; p < s->_start + s->len; *p++ ^= primary); +} + + +/* Obtains the current value of the stream's position indicator. */ +size_t stream_tell(struct stream *s) { + return s->_cur - s->_start; +} + + +/* Sets the stream's position indicator to the given `pos`. */ +void stream_seek(struct stream *s, size_t pos) { + s->_cur = s->_start + pos; +} + + +/* Sets the stream's position indicator to the beginning. */ +void stream_rewind(struct stream *s) { + s->_cur = s->_start; +} @@ -19,7 +19,7 @@ #pragma once -#include <stdbool.h> +#include <stdint.h> #include <stddef.h> /* Internal enumerable type for representing the location of a memory @@ -53,3 +53,16 @@ void stream_read(void *dest, struct stream *s, size_t n); /* Copies `n` bytes into the given stream from the memory area specified by `src`. The stream's cursor is advanced appropriately. */ void stream_write(struct stream *s, void *src, size_t n); + +/* Applies an initial and primary key to the given stream, effectively + encrypting or decrypting it. */ +void stream_xor(struct stream *s, uint8_t initial, uint8_t primary); + +/* Obtains the current value of the stream's position indicator. */ +size_t stream_tell(struct stream *s); + +/* Sets the stream's position indicator to the given `pos`. */ +void stream_seek(struct stream *s, size_t pos); + +/* Sets the stream's position indicator to the beginning. */ +void stream_rewind(struct stream *s); diff --git a/src/table.c b/src/table.c new file mode 100644 index 0000000..84ab77f --- /dev/null +++ b/src/table.c @@ -0,0 +1,73 @@ +/* table.c -- Code for handling the archive's table section. + + Copyright (C) 2017 Jakob Tsar-Fox, All Rights Reserved. + + This file is part of Nekopack. + + Nekopack is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation, either version 3 of the License, or (at + your option) any later version. + + Nekopack is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Nekopack. If not, see <http://www.gnu.org/licenses/>. */ + +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> + +#include "io.h" +#include "table.h" + +#define ELIF_MAGIC 0x46696c65 +#define FILE_MAGIC 0x656c6946 +#define HNFN_MAGIC 0x6e666e68 +#define NEKO_MAGIC 0x6f6b656e + + +/* Returns the root of a linked list containing all of the files listed + in the archive's table section. */ +struct table_entry *parse_table(struct stream *s) { + struct table_entry *cur, *root = calloc(sizeof(struct table_entry), 1); + + bool ended = false; + uint32_t magic; + uint64_t size; + + do { + stream_read(&magic, s, sizeof(uint32_t)); + stream_read(&size, s, sizeof(uint64_t)); + + switch (magic) { + case ELIF_MAGIC: + case HNFN_MAGIC: + case NEKO_MAGIC: + break; + case FILE_MAGIC: + break; + default: + ended = 1; + } + } while (!ended); +} + + +/* Inserts `e` to the end of the linked list specified by `root`. */ +void entry_append(struct table_entry *root, struct table_entry *e) { + struct table_entry *cur; + for (cur = root; cur->next != NULL; cur = cur->next); + cur->next = e; +} + + +/* Frees every entry in the linked list specified by `cur`. */ +void entry_free(struct table_entry *cur) { + if (cur->next != NULL) + entry_free(cur->next); + free(cur); +} diff --git a/src/table.h b/src/table.h new file mode 100644 index 0000000..488e67f --- /dev/null +++ b/src/table.h @@ -0,0 +1,56 @@ +/* table.h -- Code for handling the archive's table section. + + Copyright (C) 2017 Jakob Tsar-Fox, All Rights Reserved. + + This file is part of Nekopack. + + Nekopack is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation, either version 3 of the License, or (at + your option) any later version. + + Nekopack is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Nekopack. If not, see <http://www.gnu.org/licenses/>. */ + +#pragma once + +#include <stdbool.h> +#include <stdint.h> + +#include "io.h" + + +/* Structure representing one segment associated with a File entry. */ +struct segment { + bool compressed; /* Whether or not the segment is compressed. */ + uint64_t offset; /* Offset to the segment's beginning. */ + uint64_t compressed_size; /* Size of compressed segment. */ + uint64_t decompressed_size; /* Size of decompressed segment. */ +}; + +/* Structure representing an entry in the archive's table section. */ +struct table_entry { + bool encrypted; /* Whether or not it's encrypted. */ + bool compressed; /* Whether or not it's compressed. */ + uint32_t key; /* File-specific key for encryption. */ + uint64_t ctime; /* Timestamp of creation time. */ + uint64_t segment_count; /* Number of segments. */ + struct segment **segments; /* Array of associated segments. */ + struct table_entry *next; /* Pointer to the next entry. */ +}; + + +/* Returns the root of a linked list containing all of the files listed + in the archive's table section. */ +struct table_entry *parse_table(struct stream *s); + +/* Inserts `e` to the end of the linked list specified by `root`. */ +void entry_append(struct table_entry *root, struct table_entry *e); + +/* Frees every entry in the linked list specified by `cur`. */ +void entry_free(struct table_entry *cur); diff --git a/test/minunit.h b/test/minunit.h index 7422998..80ef040 100644 --- a/test/minunit.h +++ b/test/minunit.h @@ -1,3 +1,4 @@ +#pragma once #define mu_assert(message, test) do { if (!(test)) return message; } while (0) #define mu_run_test(test) do { char *message = test(); tests_run++; if (message) return message; } while (0) extern int tests_run; diff --git a/test/test_header.c b/test/test_header.c index 1ae4e19..15aa18c 100644 --- a/test/test_header.c +++ b/test/test_header.c @@ -30,9 +30,18 @@ extern int tests_run; char *test_header_read(void) { struct stream *s = stream_new(sizeof(struct xp3_header)); + memset(s->_start, '\x00', sizeof(struct xp3_header)); struct xp3_header *h = read_header(s); mu_assert("Header assertions failed", h == NULL); + + stream_rewind(s); + memcpy(s->_start, "\x58\x50\x33\x0d\x0a\x20\x0a\x1a\x8b\x67\x01\x17\x00" + "\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x80\x00\x00\x00\x00\x00" + "\x00\x00\x00\xd1\xfe\x56\x0b\x00\x00\x00\x00", 42); + h = read_header(s); + mu_assert("Header read as invalid", h != NULL); + stream_free(s); return NULL; } diff --git a/test/test_header.h b/test/test_header.h index 83613ce..5a23235 100644 --- a/test/test_header.h +++ b/test/test_header.h @@ -20,4 +20,3 @@ #pragma once char *test_header_read(void); - diff --git a/test/test_io.c b/test/test_io.c index fd1e483..86dde8e 100644 --- a/test/test_io.c +++ b/test/test_io.c @@ -17,6 +17,7 @@ You should have received a copy of the GNU General Public License along with Nekopack. If not, see <http://www.gnu.org/licenses/>. */ +#include <stdint.h> #include <string.h> #include "minunit.h" @@ -54,3 +55,35 @@ char *test_stream_rw(void) { stream_free(s); return NULL; } + + +char *test_stream_realloc(void) { + struct stream *s = stream_new(0x2); + stream_write(s, "\x00\x01\x02\x03", 4); + mu_assert("`len` not modified", s->len > 0x2); + return NULL; +} + + +char *test_stream_xor(void) { + struct stream *s = stream_new(2); + stream_write(s, "\x01\x01", 2); + stream_xor(s, 1, 0); + mu_assert("Initial key failure", !memcmp(s->_start, "\x00\x01", 2)); + stream_xor(s, 0, 1); + mu_assert("Primary key failure", !memcmp(s->_start, "\x01\x00", 2)); + stream_free(s); + return NULL; +} + + +char *test_stream_nav(void) { + struct stream *s = stream_new(2); + mu_assert("Cursor not at beginning", stream_tell(s) == 0); + stream_seek(s, 2); + mu_assert("Cursor not advanced", stream_tell(s) == 2); + stream_rewind(s); + mu_assert("Cursor not rewinded", stream_tell(s) == 0); + stream_free(s); + return NULL; +} diff --git a/test/test_io.h b/test/test_io.h index c750214..ab3547f 100644 --- a/test/test_io.h +++ b/test/test_io.h @@ -21,3 +21,6 @@ char *test_stream_obj(void); char *test_stream_rw(void); +char *test_stream_realloc(void); +char *test_stream_xor(void); +char *test_stream_nav(void); diff --git a/test/test_run.c b/test/test_run.c index 8728b81..7ec1403 100644 --- a/test/test_run.c +++ b/test/test_run.c @@ -23,6 +23,7 @@ #include "test_header.h" #include "test_io.h" +#include "test_table.h" int tests_run = 0; @@ -30,7 +31,11 @@ int tests_run = 0; static char *run_all_tests(void) { mu_run_test(test_stream_obj); mu_run_test(test_stream_rw); + mu_run_test(test_stream_realloc); + mu_run_test(test_stream_xor); + mu_run_test(test_stream_nav); mu_run_test(test_header_read); + mu_run_test(test_table_list); return NULL; } diff --git a/test/test_table.c b/test/test_table.c new file mode 100644 index 0000000..efdac3d --- /dev/null +++ b/test/test_table.c @@ -0,0 +1,36 @@ +/* test_table.c -- MinUnit test cases for table.c + + Copyright (C) 2017 Jakob Tsar-Fox, All Rights Reserved. + + This file is part of Nekopack. + + Nekopack is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation, either version 3 of the License, or (at + your option) any later version. + + Nekopack is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Nekopack. If not, see <http://www.gnu.org/licenses/>. */ + +#include <stdlib.h> + +#include "minunit.h" + +#include "table.h" + + +char *test_table_list(void) { + struct table_entry *root = calloc(sizeof(struct table_entry), 1); + mu_assert("Structure not zeroed", root->next == NULL); + struct table_entry *next = calloc(sizeof(struct table_entry), 1); + next->key = 0xffffffff; + entry_append(root, next); + mu_assert("Entry not inserted", root->next->key == 0xffffffff); + entry_free(root); + return NULL; +} diff --git a/test/test_table.h b/test/test_table.h new file mode 100644 index 0000000..1c6fe03 --- /dev/null +++ b/test/test_table.h @@ -0,0 +1,22 @@ +/* test_table.h -- MinUnit test cases for table.c + + Copyright (C) 2017 Jakob Tsar-Fox, All Rights Reserved. + + This file is part of Nekopack. + + Nekopack is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation, either version 3 of the License, or (at + your option) any later version. + + Nekopack is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Nekopack. If not, see <http://www.gnu.org/licenses/>. */ + +#pragma once + +char *test_table_list(void); |