From d65a3ae8790ae28a90f0ebcf0bc4dc078c67dc35 Mon Sep 17 00:00:00 2001 From: jakob Date: Sun, 19 Feb 2017 20:49:43 -0500 Subject: Implemented eliF parsing. --- src/encoding.c | 36 ++++++++++++++++++++++++++++++++++++ src/encoding.h | 23 +++++++++++++++++++++++ src/io.c | 19 ++++++++++++++++--- src/io.h | 3 ++- src/table.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- src/table.h | 7 +++++++ test/test_io.c | 8 ++++++-- test/test_run.c | 1 + test/test_table.c | 17 ++++++++++++++++- test/test_table.h | 1 + 10 files changed, 156 insertions(+), 8 deletions(-) create mode 100644 src/encoding.c create mode 100644 src/encoding.h diff --git a/src/encoding.c b/src/encoding.c new file mode 100644 index 0000000..6ad4ea1 --- /dev/null +++ b/src/encoding.c @@ -0,0 +1,36 @@ +/* encoding.c -- Code for working with encoded strings. + + 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 + + +/* Wrapper for iconv, using the conversion specified by `conv`. */ +static void convert(char *in_buf, char *out_buf, size_t len, iconv_t conv) { + size_t in_size = len, out_size = len; + char *in_start = in_buf, *out_start = out_buf; + iconv(conv, &in_start, &in_size, &out_start, &out_size); +} + + +/* Decodes the UTF-16LE string specified by `in_buf` into `out_buf`. */ +void utf16le_decode(char *in_buf, char *out_buf, size_t len) { + iconv_t conv = iconv_open("UTF-8", "UTF-16LE"); + convert(in_buf, out_buf, len, conv); + iconv_close(conv); +} diff --git a/src/encoding.h b/src/encoding.h new file mode 100644 index 0000000..cfb49c8 --- /dev/null +++ b/src/encoding.h @@ -0,0 +1,23 @@ +/* encoding.h -- Code for working with encoded strings. + + 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 + +/* Decodes the UTF-16LE string specified by `in_buf` into `out_buf`. */ +void utf16le_decode(char *in_buf, char *out_buf, size_t len); diff --git a/src/io.c b/src/io.c index be894dd..c9627d3 100644 --- a/src/io.c +++ b/src/io.c @@ -18,6 +18,7 @@ along with Nekopack. If not, see . */ #include +#include #include #include @@ -86,9 +87,21 @@ 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) { - s->_cur = s->_start + pos; +/* Sets the stream's position indicator to the given `pos`. If `whence` + is set to SEEK_SET, SEEK_CUR, or SEEK_END, the offset is relative to + the start of the file, the current position indicator, or + end-of-file, respectively. */ +void stream_seek(struct stream *s, size_t pos, int whence) { + switch (whence) { + case SEEK_SET: + s->_cur = s->_start + pos; + break; + case SEEK_CUR: + s->_cur += pos; + break; + case SEEK_END: + s->_cur = s->_start + s->len - pos; + } } diff --git a/src/io.h b/src/io.h index 87f93b2..a543ae5 100644 --- a/src/io.h +++ b/src/io.h @@ -20,6 +20,7 @@ #pragma once #include +#include #include /* Internal enumerable type for representing the location of a memory @@ -62,7 +63,7 @@ void stream_xor(struct stream *s, uint8_t initial, uint8_t primary); 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); +void stream_seek(struct stream *s, size_t pos, int whence); /* 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 index 84ab77f..a541ab9 100644 --- a/src/table.c +++ b/src/table.c @@ -20,7 +20,9 @@ #include #include #include +#include +#include "encoding.h" #include "io.h" #include "table.h" @@ -47,7 +49,7 @@ struct table_entry *parse_table(struct stream *s) { case ELIF_MAGIC: case HNFN_MAGIC: case NEKO_MAGIC: - break; + read_elif(s, root); case FILE_MAGIC: break; default: @@ -57,6 +59,51 @@ struct table_entry *parse_table(struct stream *s) { } +/* Reads the contents of an eliF chunk. If there is an entry with a + matching key in the linked list specified by `root`, that structure + will be modified. Otherwise, a new entry will be created and appended + to the linked list. */ +void read_elif(struct stream *s, struct table_entry *root) { + char *name, *buf; + uint16_t name_size; + uint32_t key; + struct table_entry *cur; + + stream_read(&key, s, sizeof(uint32_t)); + stream_read(&name_size, s, sizeof(uint16_t)); + + /* The value provided by the archive represents the number of + UTF-16LE characters, not the number of bytes in the string. */ + name_size = name_size * 2 + 2; + + for (cur = root; cur != NULL && cur->key != key; cur = cur->next); + if (cur == NULL) { + cur = calloc(sizeof(struct table_entry), 1); + if (cur == NULL) return; + entry_append(root, cur); + } + + if (name_size < 0x100) { + buf = malloc(name_size); + name = malloc(name_size); + if (buf == NULL || name == NULL) return; + + stream_read(buf, s, name_size); + utf16le_decode(buf, name, name_size); + free(buf); + + name = realloc(name, strlen(name) + 1); + if (name == NULL) return; + } else { + /* strdup isn't defined in ISO/IEC 9899:1999 C. */ + name = malloc(14); + if (name == NULL) return; + strncpy(name, "COPYRIGHT.txt", 14); + } + cur->filename = name; +} + + /* 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; diff --git a/src/table.h b/src/table.h index 488e67f..c7c85bb 100644 --- a/src/table.h +++ b/src/table.h @@ -37,6 +37,7 @@ struct segment { struct table_entry { bool encrypted; /* Whether or not it's encrypted. */ bool compressed; /* Whether or not it's compressed. */ + char *filename; /* String containing file's name. */ uint32_t key; /* File-specific key for encryption. */ uint64_t ctime; /* Timestamp of creation time. */ uint64_t segment_count; /* Number of segments. */ @@ -49,6 +50,12 @@ struct table_entry { in the archive's table section. */ struct table_entry *parse_table(struct stream *s); +/* Reads the contents of an eliF chunk. If there is an entry with a + matching key in the linked list specified by `root`, that structure + will be modified. Otherwise, a new entry will be created and appended + to the linked list. */ +void read_elif(struct stream *s, struct table_entry *root); + /* Inserts `e` to the end of the linked list specified by `root`. */ void entry_append(struct table_entry *root, struct table_entry *e); diff --git a/test/test_io.c b/test/test_io.c index 86dde8e..f295182 100644 --- a/test/test_io.c +++ b/test/test_io.c @@ -80,8 +80,12 @@ char *test_stream_xor(void) { 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_seek(s, 2, SEEK_CUR); + mu_assert("Cursor not advanced (SEEK_CUR)", stream_tell(s) == 2); + stream_seek(s, 2, SEEK_SET); + mu_assert("Cursor not advanced (SEEK_SET)", stream_tell(s) == 2); + stream_seek(s, 0, SEEK_END); + mu_assert("Cursor not advanced (SEEK_END)", stream_tell(s) == 2); stream_rewind(s); mu_assert("Cursor not rewinded", stream_tell(s) == 0); stream_free(s); diff --git a/test/test_run.c b/test/test_run.c index 7ec1403..c6930dc 100644 --- a/test/test_run.c +++ b/test/test_run.c @@ -36,6 +36,7 @@ static char *run_all_tests(void) { mu_run_test(test_stream_nav); mu_run_test(test_header_read); mu_run_test(test_table_list); + mu_run_test(test_table_elif); return NULL; } diff --git a/test/test_table.c b/test/test_table.c index efdac3d..e6c18b0 100644 --- a/test/test_table.c +++ b/test/test_table.c @@ -18,15 +18,16 @@ along with Nekopack. If not, see . */ #include +#include #include "minunit.h" +#include "io.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); @@ -34,3 +35,17 @@ char *test_table_list(void) { entry_free(root); return NULL; } + + +char *test_table_elif(void) { + struct stream *s = stream_new(10); + struct table_entry *root = calloc(sizeof(struct table_entry), 1); + stream_write(s, "\xff\xff\xff\xff\x01\x00\x41\x00\x00\x00", 10); + stream_rewind(s); + read_elif(s, root); + mu_assert("Entry not inserted", root->next != NULL); + mu_assert("Filename handling failed", !strcmp(root->next->filename, "A")); + entry_free(root); + stream_free(s); + return NULL; +} diff --git a/test/test_table.h b/test/test_table.h index 1c6fe03..899c2ab 100644 --- a/test/test_table.h +++ b/test/test_table.h @@ -20,3 +20,4 @@ #pragma once char *test_table_list(void); +char *test_table_elif(void); -- cgit v1.3