/* 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;
}