From 32fb7b11d83138cc288d62c1f1674d95cc94e8de Mon Sep 17 00:00:00 2001 From: jakob Date: Sun, 19 Feb 2017 10:57:47 -0500 Subject: Implemented table linked list, decompression and encryption. Tests have been improved. --- src/compress.c | 57 +++++++++++++++++++++++++++++++++++++++++++++ src/compress.h | 25 ++++++++++++++++++++ src/header.c | 25 ++++++++++++++------ src/io.c | 29 ++++++++++++++++++++++- src/io.h | 15 +++++++++++- src/table.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/table.h | 56 ++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 271 insertions(+), 9 deletions(-) create mode 100644 src/compress.c create mode 100644 src/compress.h create mode 100644 src/table.c create mode 100644 src/table.h (limited to 'src') 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 . */ + +#include + +#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 . */ + +#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; } diff --git a/src/io.c b/src/io.c index 1775f6b..be894dd 100644 --- a/src/io.c +++ b/src/io.c @@ -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; +} diff --git a/src/io.h b/src/io.h index 0914c9f..87f93b2 100644 --- a/src/io.h +++ b/src/io.h @@ -19,7 +19,7 @@ #pragma once -#include +#include #include /* 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 . */ + +#include +#include +#include + +#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 . */ + +#pragma once + +#include +#include + +#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); -- cgit v1.3