summaryrefslogtreecommitdiff
path: root/src/extract.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/extract.c')
-rw-r--r--src/extract.c105
1 files changed, 65 insertions, 40 deletions
diff --git a/src/extract.c b/src/extract.c
index 8a1eec0..f452eaf 100644
--- a/src/extract.c
+++ b/src/extract.c
@@ -24,6 +24,7 @@
#include <iconv.h>
#include <zlib.h>
+#include "cli.h"
#include "defs.h"
#include "decompress.h"
#include "extract.h"
@@ -33,18 +34,23 @@
#define ELIF_MAGIC 0x46696c65
#define FILE_MAGIC 0x656c6946
#define HNFN_MAGIC 0x6e666e68
+#define NEKO_MAGIC 0x6f6b656e
+elif_node *read_elif_entry(memory_stream *data_stream);
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);
+ /* eliF and File entries are stored in a linked list as they're
+ seen, because the order of entries in XP3 archives is not
+ guaranteed to be chronological. calloc is used to prevent the
+ "next" pointer from being garbage and causing a segfault. */
+ elif_node *elif_root = calloc(sizeof(elif_node), 1);
+ elif_node *elif_new;
+ file_node *file_root = calloc(sizeof(file_node), 1);
+ file_node *file_new;
uint8_t compressed;
fseek(archive, table_offset, SEEK_SET);
@@ -55,16 +61,19 @@ void extract(FILE *archive, uint64_t table_offset) {
if (compressed) {
data_stream = decompress_file(archive, ftell(archive));
} else {
- fprintf(stderr, "This archive is not supported.\n");
- exit(EXIT_FAILURE);
+ uint64_t decompressed_size;
+ fread(&decompressed_size, sizeof(uint64_t), 1, archive);
+ /* The second size is irrelevant and therefore ignored. */
+ fseek(archive, sizeof(uint64_t), SEEK_CUR);
+ data_stream.stream_length = decompressed_size;
+ data_stream.data = malloc(decompressed_size);
+ data_stream.start = data_stream.data;
+ fread(data_stream.data, decompressed_size, 1, archive);
+ fclose(archive);
}
- /* 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));
@@ -72,15 +81,20 @@ void extract(FILE *archive, uint64_t table_offset) {
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:
+ /* hnfn, neko, and eliF entries are all identical.
+
+ The size given by the entry header doesn't match the
+ actual entry size, so it isn't passed to the function. */
case ELIF_MAGIC:
- defer_node(read_elif_entry(&data_stream), root);
+ case HNFN_MAGIC:
+ case NEKO_MAGIC:
+ elif_new = read_elif_entry(&data_stream);
+ defer_elif_node(elif_new, elif_root);
break;
case FILE_MAGIC:
- read_file_entry(&data_stream, data_stream.data + entry_size);
+ file_new = read_file_entry(&data_stream,
+ data_stream.data + entry_size);
+ defer_file_node(file_new, file_root);
break;
default:
printf("End of archive reached.\n");
@@ -88,9 +102,11 @@ void extract(FILE *archive, uint64_t table_offset) {
}
} while (!stream_ended);
- test_linked_list(root);
- free_node(root);
-
+ write_files(file_root, elif_root, data_stream.start);
+ test_elif_linked_list(elif_root);
+ test_file_linked_list(file_root);
+ free_elif_nodes(elif_root);
+ free_file_nodes(file_root);
free(data_stream.start);
}
@@ -103,32 +119,41 @@ void read_stream(void *destination, Bytef **source, size_t size) {
}
-/* Document */
-node *read_elif_entry(memory_stream *data_stream) {
+/* Returns a pointer to a elif_node containing the filename
+ and key, which can be deferred in a linked list. */
+elif_node *read_elif_entry(memory_stream *data_stream) {
+ uint32_t file_key;
uint16_t name_size;
- uint32_t name_hash;
- read_stream(&name_hash, &data_stream->data, sizeof(uint32_t));
+ read_stream(&file_key, &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);
+ char *file_name;
+ if (name_size < 0x100) {
+ /* Strings are terminated by null bytes, but
+ they aren't counted in the name size. */
+ char *input_buffer = malloc(name_size * 2 + 2);
+ 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);
+ /* iconv is the less-portable glibc way of doing it. It seems to
+ be in the OpenBSD manpages, though, so I'm not worried. */
+ 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;
+ free(input_buffer);
+ } else {
+ /* It's pretty safe to assume anything
+ larger is the copyright notice. */
+ data_stream->data += name_size * 2 + 2;
+ file_name = strdup("COPYING.txt");
+ }
+
+ elif_node *current = malloc(sizeof(elif_node));
+ current->key = file_key;
current->file_name = file_name;
current->next = NULL;
-
- free(input_buffer);
-
return current;
}