From b3560b1005d3bc78a4f00a2279eb791209801a98 Mon Sep 17 00:00:00 2001 From: jakob Date: Sat, 7 Jan 2017 16:01:01 -0500 Subject: Minimal debugging information about the stream is printed. --- src/parse.c | 264 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 264 insertions(+) create mode 100644 src/parse.c (limited to 'src/parse.c') diff --git a/src/parse.c b/src/parse.c new file mode 100644 index 0000000..c53a627 --- /dev/null +++ b/src/parse.c @@ -0,0 +1,264 @@ +/* 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" + +#define ELIF_MAGIC 0x46696c65 +#define FILE_MAGIC 0x656c6946 +#define ADLR_MAGIC 0x726c6461 +#define SEGM_MAGIC 0x6d676573 +#define INFO_MAGIC 0x6f666e69 +#define TIME_MAGIC 0x656d6974 + +/* A pointer to the start of the memory region + has to be kept for freeing purposes. */ +typedef struct { + uint64_t stream_length; + Bytef *start; + Bytef *data; +} memory_stream; + +/* typedef struct { */ +/* uint64_t timestamp; */ +/* uint32_t hash_key; */ +/* } file_entry; */ + +memory_stream decompress_stream(FILE *archive, uint64_t sizes_offset); +void read_stream(void *destination, Bytef **source, size_t size); +void read_file_entry(memory_stream *data_stream, Bytef *section_end); +void read_elif_entry(memory_stream *data_stream); +void read_info_chunk(memory_stream *data_stream); + + +/* Parses and extracts entries from the archive. */ +void extract(FILE *archive, uint64_t table_offset) { + /* An 8-bit unsigned integers at the table offset indicates + whether or not the archive has to be decompressed. */ + uint8_t compressed; + fseek(archive, table_offset, SEEK_SET); + fread(&compressed, sizeof(uint8_t), 1, archive); + + memory_stream data_stream; + if (compressed) { + data_stream = decompress_stream(archive, ftell(archive)); + } else { + fprintf(stderr, "Uncompressed archives not yet 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; + for (;;) { + 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); // Debug. + + switch (entry_magic) { + case ELIF_MAGIC: + /* The size given by the entry header doesn't match the + actual entry size, so it isn't passed to the function. */ + read_elif_entry(&data_stream); + break; + case FILE_MAGIC: + // printf("[File entry]\n"); + // data_stream.data += entry_size; + read_file_entry(&data_stream, data_stream.data + entry_size); + break; + default: // Debug. + printf("Unknown magic: %x\n", entry_magic); + exit(EXIT_FAILURE); + } + } + + free(data_stream.start); +} + + +/* Inflates the file pointer and returns a struct containing the + size and a pointer to the decompressed data in memory. */ +memory_stream decompress_stream(FILE *archive, uint64_t sizes_offset) { + uint64_t compressed_size, decompressed_size; + fseek(archive, sizes_offset, SEEK_SET); + fread(&compressed_size, sizeof(uint64_t), 1, archive); + fread(&decompressed_size, sizeof(uint64_t), 1, archive); + + /* Decompression is done in memory because it's $CURRENT_YEAR. */ + Bytef *compressed_data = malloc(compressed_size); + Bytef *decompressed_data = malloc(decompressed_size); + + /* This is a pretty shitty way of handling it, though. */ + if (compressed_data == NULL || decompressed_data == NULL) { + if (compressed_data != NULL) + free(compressed_data); + fprintf(stderr, "Insufficient memory to decompress archive.\n"); + fclose(archive); + exit(EXIT_FAILURE); + } + + z_stream data_stream; + data_stream.zalloc = Z_NULL; + data_stream.zfree = Z_NULL; + data_stream.opaque = Z_NULL; + data_stream.avail_in = 0; + data_stream.next_in = Z_NULL; + + if (inflateInit(&data_stream) != Z_OK) { + fprintf(stderr, "Could not initialize zlib.\n"); + free(compressed_data); + free(decompressed_data); + fclose(archive); + exit(EXIT_FAILURE); + } + + int status_code; + do { + fread(compressed_data, compressed_size, 1, archive); + data_stream.avail_in = compressed_size; + + /* This really shouldn't happen. */ + if (ferror(archive)) { + fprintf(stderr, "File corrupt.\n"); + inflateEnd(&data_stream); + free(compressed_data); + free(decompressed_data); + exit(EXIT_FAILURE); + } + + if (data_stream.avail_in == 0) + break; + data_stream.next_in = compressed_data; + // Flushing probably isn't required here. + do { + data_stream.avail_out = decompressed_size; + data_stream.next_out = decompressed_data; + status_code = inflate(&data_stream, Z_NO_FLUSH); + } while (data_stream.avail_out == 0); + } while (status_code != Z_STREAM_END); + + /* The compressed data is irrelevant at this point. */ + free(compressed_data); + + return (memory_stream) {decompressed_size, decompressed_data, + decompressed_data}; +} + + +/* 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 */ +void read_file_entry(memory_stream *data_stream, Bytef *section_end) { + uint32_t entry_magic; + uint64_t entry_size; + while (data_stream->data < section_end) { + read_stream(&entry_magic, &data_stream->data, sizeof(uint32_t)); + read_stream(&entry_size, &data_stream->data, sizeof(uint64_t)); + switch (entry_magic) { + case ADLR_MAGIC: + printf("[ADLR found at 0x%lx]\n", data_stream->data - data_stream->start); + data_stream->data += entry_size; + break; + case SEGM_MAGIC: + printf("[SEGM found at 0x%lx]\n", data_stream->data - data_stream->start); + data_stream->data += entry_size; + break; + case INFO_MAGIC: + printf("[INFO found at 0x%lx]\n", data_stream->data - data_stream->start); + read_info_chunk(data_stream); + /* data_stream->data += entry_size; */ + break; + case TIME_MAGIC: + printf("[TIME found at 0x%lx]\n", data_stream->data - data_stream->start); + data_stream->data += entry_size; + break; + default: + printf("New magic discovered: %" PRIx32 " size: %" PRIx64 "\n", entry_magic, entry_size); // Debug. + } + } + printf("\n\n\n\n"); // Debug. +} + + +/* Document */ +void read_elif_entry(memory_stream *data_stream) { + uint16_t name_size; + /* The first part of an ELIF entry is a 32-bit file name hash. */ + data_stream->data += sizeof(uint32_t); + read_stream(&name_size, &data_stream->data, sizeof(uint16_t)); + + char *input_buffer = malloc(name_size * 2); + char *file_name = malloc(name_size); + read_stream(input_buffer, &data_stream->data, name_size * 2); + + /* There seems to be an extra UTF-16 byte at the end of the entry. */ + data_stream->data += 2; + + printf("Filename (ASCII): "); + for (int i = 0; i < name_size * 2; i++) { + if (input_buffer[i] >= 0x20 && input_buffer[i] < 0x7f) + printf("%c", input_buffer[i]); + } + printf("\n"); + + free(input_buffer); + free(file_name); +} + + +/* Document */ +void read_info_chunk(memory_stream *data_stream) { + uint32_t flags; + uint64_t decompressed_size, compressed_size; + uint16_t file_name_size; + read_stream(&flags, &data_stream->data, sizeof(uint32_t)); + read_stream(&decompressed_size, &data_stream->data, sizeof(uint64_t)); + read_stream(&compressed_size, &data_stream->data, sizeof(uint64_t)); + read_stream(&file_name_size, &data_stream->data, sizeof(uint16_t)); + + char *file_name = malloc(file_name_size * 2); + read_stream(file_name, &data_stream->data, file_name_size * 2); + printf("\nINFO SEGMENT\n"); + printf("------------\n"); + printf("FLAGS: %" PRIx32 "\n", flags); + printf("DECOMPRESSED_SIZE: %" PRIx64 "\n", decompressed_size); + printf("COMPRESSED_SIZE: %" PRIx64 "\n", compressed_size); + printf("FILENAME (HASH?): "); + for (int i = 0; i < file_name_size * 2; i++) { + if (file_name[i] >= 0x20 && file_name[i] < 0x7f) + printf("%c", file_name[i]); + } + printf("\n"); + + data_stream->data += 2; +} -- cgit v1.3