From 7d141c7e79bb9688a475b88722f6101ad01ef782 Mon Sep 17 00:00:00 2001 From: jakob Date: Sun, 8 Jan 2017 20:22:46 -0500 Subject: Large commit. Filenames are now converted from UTF-16LE and placed into a linked list. Parsers also exist for every entry in the XP3 format. --- src/extract.c | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 src/extract.c (limited to 'src/extract.c') diff --git a/src/extract.c b/src/extract.c new file mode 100644 index 0000000..8a1eec0 --- /dev/null +++ b/src/extract.c @@ -0,0 +1,134 @@ +/* This file is part of Nekopack. + + Copyright (C) 2017 Jakob Tsar-Fox, All Rights Reserved. + + 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 // Needed for debugging at this point. +#include +#include +#include +#include + +#include +#include + +#include "defs.h" +#include "decompress.h" +#include "extract.h" +#include "file.h" +#include "write.h" + +#define ELIF_MAGIC 0x46696c65 +#define FILE_MAGIC 0x656c6946 +#define HNFN_MAGIC 0x6e666e68 + +void read_stream(void *destination, Bytef **source, size_t size); +void read_file_entry(memory_stream *data_stream, Bytef *section_end); +node *read_elif_entry(memory_stream *data_stream); + + +/* Handles decompression of the archive, as well as + parsing, decrypting and writing the table entries. */ +void extract(FILE *archive, uint64_t table_offset) { + /* Filenames are stored in a linked list as they're seen. + calloc is used to prevent the next pointer from being junk. */ + node *root = calloc(sizeof(node), 1); + + uint8_t compressed; + fseek(archive, table_offset, SEEK_SET); + fread(&compressed, sizeof(uint8_t), 1, archive); + + int stream_ended = 0; + memory_stream data_stream; + if (compressed) { + data_stream = decompress_file(archive, ftell(archive)); + } else { + fprintf(stderr, "This archive is not supported.\n"); + exit(EXIT_FAILURE); + } + + /* The header for every entry in the XP3 archive format contains + a magic number, followed by the size of the entry. */ + uint32_t entry_magic; + uint64_t entry_size; + /* Implement "last filename" variable, which gets freed + and reallocated every time an elif entry is read. */ + do { + read_stream(&entry_magic, &data_stream.data, sizeof(uint32_t)); + read_stream(&entry_size, &data_stream.data, sizeof(uint64_t)); + printf("entry at 0x%lx\n", data_stream.data - data_stream.start - 12); + printf("Magic: %" PRIx32 " Size: %" PRIx64 "\n", entry_magic, entry_size); + + switch (entry_magic) { + /* hnfn and eliF entries are identical. The size given by + the entry header doesn't match the actual entry size, + so it isn't passed to the function. */ + case HNFN_MAGIC: + case ELIF_MAGIC: + defer_node(read_elif_entry(&data_stream), root); + break; + case FILE_MAGIC: + read_file_entry(&data_stream, data_stream.data + entry_size); + break; + default: + printf("End of archive reached.\n"); + stream_ended = 1; + } + } while (!stream_ended); + + test_linked_list(root); + free_node(root); + + free(data_stream.start); +} + + +/* Wrapper for memcpy which increments the source operand by + the amount of bytes read to simulate a file stream. */ +void read_stream(void *destination, Bytef **source, size_t size) { + memcpy(destination, *source, size); + *source += size; +} + + +/* Document */ +node *read_elif_entry(memory_stream *data_stream) { + uint16_t name_size; + uint32_t name_hash; + read_stream(&name_hash, &data_stream->data, sizeof(uint32_t)); + read_stream(&name_size, &data_stream->data, sizeof(uint16_t)); + + /* Strings are terminated by null bytes, + which aren't counted in the name size. */ + char *input_buffer = malloc(name_size * 2 + 2); + char *file_name = malloc(name_size + 1); + read_stream(input_buffer, &data_stream->data, name_size * 2 + 2); + + /* iconv is the non-portable glibc way of doing it. */ + char *in_start = input_buffer, *out_start = file_name; + size_t in_size = name_size * 2 + 2, out_size = name_size + 1; + iconv_t conversion = iconv_open("UTF-8", "UTF-16LE"); + iconv(conversion, &in_start, &in_size, &out_start, &out_size); + iconv_close(conversion); + + node *current = malloc(sizeof(node)); + current->key = name_hash; + current->file_name = file_name; + current->next = NULL; + + free(input_buffer); + + return current; +} -- cgit v1.3