From 8eaa3f0526d380afe422152fada2bea27b8d8f70 Mon Sep 17 00:00:00 2001 From: jakob Date: Mon, 9 Jan 2017 20:51:41 -0500 Subject: Significant progress on implementing file decryption. Offsets as of now are trying to go from within the table, which is incorrect. A working commit should come tomorrow. --- src/cli.c | 33 +++++++++++++++-- src/cli.h | 12 ++++++ src/decompress.c | 20 +++------- src/decompress.h | 5 +++ src/extract.c | 109 ++++++++++++++++++++++++++++++++++--------------------- src/extract.h | 1 + src/file.c | 99 ++++++++++++++++++++++++++++++-------------------- src/file.h | 27 ++++++++++---- src/main.c | 1 - src/write.c | 104 +++++++++++++++++++++++++++++++++++++++++----------- src/write.h | 14 ++++--- 11 files changed, 293 insertions(+), 132 deletions(-) (limited to 'src') diff --git a/src/cli.c b/src/cli.c index 481aaea..2713fed 100644 --- a/src/cli.c +++ b/src/cli.c @@ -24,13 +24,20 @@ #include "cli.h" #include "defs.h" -#define VERSION "0.1.0" +#define VERSION "1.0.0.rc1" #define HELP_TEXT "A tool for decompressing the XP3 archives used by " \ "Nekopara.\n\n" \ " -h, --help\t\tDisplay this help page and " \ "exit.\n" \ " -v, --version\tDisplay the currently installed " \ - "version and exit.\n\n" + "version and exit.\n\n" \ + " -l, --list\t\tList games supported by Nekopack.\n" \ + " -a, --archive\tAlternate way of specifying archive" \ + "to extract.\n" \ + " -g, --game\t\tGame the archive is from. Required for " \ + "file decryption" +#define GAME_CONSTANTS "none, nekopara_volume_0, nekopara_volume_0_steam, " \ + "nekopara_volume_1, nekopara_volume_1_steam" /* General subroutine for parsing command-line arguments. Returns a @@ -57,7 +64,8 @@ struct configuration parse_args(int argc, char *argv[]) { }; do { - current = getopt_long(argc, argv, "hv", long_options, &option_index); + current = getopt_long(argc, argv, "hvla:g:", long_options, + &option_index); switch (current) { case 'h': printf("Usage: %s [OPTIONS] (ARCHIVE PATH)\n\n", argv[0]); @@ -67,6 +75,25 @@ struct configuration parse_args(int argc, char *argv[]) { printf("Nekopack, version %s\nProgrammed by " "Jakob. \n", VERSION); exit(EXIT_SUCCESS); + case 'l': + printf("%s\n", GAME_CONSTANTS); + exit(EXIT_SUCCESS); + case 'a': + count++; + parsed.archive_path = optarg; + break; + case 'g': + count++; + if (!strcmp(optarg, "nekopara_volume_0")) + parsed.source = NEKOPARA_VOLUME_0; + else if (!strcmp(optarg, "nekopara_volume_0_steam")) + parsed.source = NEKOPARA_VOLUME_0_STEAM; + else if (!strcmp(optarg, "nekopara_volume_1")) + parsed.source = NEKOPARA_VOLUME_1; + else if (!strcmp(optarg, "nekopara_volume_1_steam")) + parsed.source = NEKOPARA_VOLUME_1_STEAM; + else + parsed.source = NO_CRYPTO; } count++; } while (current >= 0); diff --git a/src/cli.h b/src/cli.h index 0008d63..43d9465 100644 --- a/src/cli.h +++ b/src/cli.h @@ -17,8 +17,20 @@ #pragma once +/* Enumerable type representing Nekopara games, as they have different + encryption keys. It's stored as an enum because multiple strcmp calls + to figure out how to decrypt is a waste of processor cycles. */ +typedef enum game { + NO_CRYPTO, + NEKOPARA_VOLUME_0, + NEKOPARA_VOLUME_0_STEAM, + NEKOPARA_VOLUME_1, + NEKOPARA_VOLUME_1_STEAM, +} game; + /* Binary structure for storing command-line options. */ struct configuration { + game source; /* Which decryption key to use. */ const char *archive_path; /* Path to archive to extract. */ }; diff --git a/src/decompress.c b/src/decompress.c index dba637f..0301cc8 100644 --- a/src/decompress.c +++ b/src/decompress.c @@ -24,7 +24,8 @@ #include "extract.h" -/* Document and put in header. */ +/* Returns a pointer to a buffer containing the + inflated contents of a given memory chunk. */ Bytef *inflate_chunk(Bytef *chunk, uint64_t chunk_size, uint64_t decompressed_size) { Bytef *decompressed_data = malloc(decompressed_size); @@ -77,7 +78,7 @@ memory_stream decompress_file(FILE *archive, uint64_t sizes_offset) { 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, *compressed_data = malloc(compressed_size); /* This is a pretty shitty way of handling it, though. */ if (compressed_data == NULL) { @@ -88,22 +89,13 @@ memory_stream decompress_file(FILE *archive, uint64_t sizes_offset) { } fread(compressed_data, compressed_size, 1, archive); - /* if (ferror(archive)) { */ - /* fprintf(stderr, "File corrupt.\n"); */ - /* inflateEnd(&data_stream); */ - /* free(compressed_data); */ - /* free(decompressed_data); */ - /* exit(EXIT_FAILURE); */ - /* } */ - Bytef *decompressed_data = inflate_chunk(compressed_data, compressed_size, - decompressed_size); - + decompressed_data = inflate_chunk(compressed_data, compressed_size, + decompressed_size); /* The compressed data is irrelevant at this point. */ free(compressed_data); if (decompressed_data == NULL) exit(EXIT_FAILURE); - return (memory_stream) {decompressed_size, - decompressed_data, + return (memory_stream) {decompressed_size, decompressed_data, decompressed_data}; } diff --git a/src/decompress.h b/src/decompress.h index 184b0f8..a625f5e 100644 --- a/src/decompress.h +++ b/src/decompress.h @@ -24,3 +24,8 @@ /* Wrapper for inflate_chunk which operates on FILE pointers. The file pointer's contents are inflated into a memory stream and returned. */ memory_stream decompress_file(FILE *archive, uint64_t sizes_offset); + +/* Returns a pointer to a buffer containing the + inflated contents of a given memory chunk. */ +Bytef *inflate_chunk(Bytef *chunk, uint64_t chunk_size, + uint64_t decompressed_size); 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 #include +#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); - - /* 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; + 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 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); + + 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; } diff --git a/src/extract.h b/src/extract.h index 1972af2..0247ea9 100644 --- a/src/extract.h +++ b/src/extract.h @@ -17,6 +17,7 @@ #pragma once +#include #include #include diff --git a/src/file.c b/src/file.c index 1d4dc74..aa7e00d 100644 --- a/src/file.c +++ b/src/file.c @@ -21,56 +21,60 @@ #include #include "extract.h" +#include "file.h" #define ADLR_MAGIC 0x726c6461 #define SEGM_MAGIC 0x6d676573 #define INFO_MAGIC 0x6f666e69 #define TIME_MAGIC 0x656d6974 -void read_info_chunk(memory_stream *data_stream); -void read_segm_chunk(memory_stream *data_stream); -void read_adlr_chunk(memory_stream *data_stream); -void read_time_chunk(memory_stream *data_stream); +void read_info_chunk(memory_stream *data_stream, file_node *parsed); +void read_adlr_chunk(memory_stream *data_stream, file_node *parsed); +void read_time_chunk(memory_stream *data_stream, file_node *parsed); +void read_segm_chunk(memory_stream *data_stream, file_node *parsed, + uint64_t segment_count); -/* Document and update documentation in header file. */ -void read_file_entry(memory_stream *data_stream, Bytef *section_end) { +/* Creates a file node by parsing a file entry. */ +file_node *read_file_entry(memory_stream *data_stream, Bytef *section_end) { uint32_t entry_magic; uint64_t entry_size; + file_node *parsed = calloc(sizeof(file_node), 1); 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); - read_adlr_chunk(data_stream); + printf("[ADLR found at 0x%lx]\n", data_stream->data - data_stream->start - 12); + read_adlr_chunk(data_stream, parsed); break; case SEGM_MAGIC: - printf("[SEGM found at 0x%lx]\n", data_stream->data - data_stream->start); - read_segm_chunk(data_stream); + printf("[SEGM found at 0x%lx]\n", data_stream->data - data_stream->start - 12); + read_segm_chunk(data_stream, parsed, entry_size); break; case INFO_MAGIC: - printf("[INFO found at 0x%lx]\n", data_stream->data - data_stream->start); - read_info_chunk(data_stream); + printf("[INFO found at 0x%lx]\n", data_stream->data - data_stream->start - 12); + read_info_chunk(data_stream, parsed); break; case TIME_MAGIC: - printf("[TIME found at 0x%lx]\n", data_stream->data - data_stream->start); - read_time_chunk(data_stream); + printf("[TIME found at 0x%lx]\n", data_stream->data - data_stream->start - 12); + read_time_chunk(data_stream, parsed); break; default: - printf("New magic discovered: %" PRIx32 " size: %" PRIx64 "\n", entry_magic, entry_size); // Debug. + printf("Unknown magic: %" PRIx32 " size: %" PRIx64 "\n", entry_magic, entry_size); // Debug. } } printf("\n\n\n\n"); // Debug. + return parsed; } /* Document */ -void read_info_chunk(memory_stream *data_stream) { - uint32_t flags; +void read_info_chunk(memory_stream *data_stream, file_node *parsed) { + uint32_t encrypted; uint64_t decompressed_size, compressed_size; uint16_t file_name_size; - read_stream(&flags, &data_stream->data, sizeof(uint32_t)); + read_stream(&encrypted, &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)); @@ -79,7 +83,7 @@ void read_info_chunk(memory_stream *data_stream) { read_stream(file_name, &data_stream->data, file_name_size * 2); printf("\nINFO SEGMENT\n"); printf("------------\n"); - printf("FLAGS: %" PRIx32 "\n", flags); + printf("%s\n", encrypted ? "ENCRYPTED": "PLAINTEXT"); printf("COMPRESSED_SIZE: %" PRIx64 "\n", compressed_size); printf("DECOMPRESSED_SIZE: %" PRIx64 "\n", decompressed_size); printf("MD5: "); @@ -94,36 +98,53 @@ void read_info_chunk(memory_stream *data_stream) { /* Document */ -void read_segm_chunk(memory_stream *data_stream) { - uint32_t flags; - uint64_t offset, compressed_size, decompressed_size; - read_stream(&flags, &data_stream->data, sizeof(uint32_t)); - read_stream(&offset, &data_stream->data, sizeof(uint64_t)); - read_stream(&compressed_size, &data_stream->data, sizeof(uint64_t)); - read_stream(&decompressed_size, &data_stream->data, sizeof(uint64_t)); - printf("\nSEGM SEGMENT\n"); - printf("------------\n"); - printf("FLAGS: %" PRIx32 "\n", flags); - printf("MEM_OFFSET: %" PRIx64 "\n", offset); - printf("DECOMPRESSED_SIZE: %" PRIx64 "\n", decompressed_size); - printf("COMPRESSED_SIZE: %" PRIx64 "\n\n", compressed_size); - /* Entries can be between the segment chunk and the actual file - data, so processing the file has to be deferred for later. */ +void read_segm_chunk(memory_stream *data_stream, file_node *parsed, + uint64_t segment_count) { + /* The segment_count is in bytes, with each + segment being 28 bytes in length. */ + segment_count /= 28; + + /* Segments are stored in an array of segment pointers. */ + segment **segments = malloc(sizeof(segment *) * segment_count); + for (int i = 0; i < segment_count; i++) { + segments[i] = malloc(sizeof(segment)); + read_stream(&segments[i]->compressed, + &data_stream->data, + sizeof(uint32_t)); + read_stream(&segments[i]->offset, + &data_stream->data, + sizeof(uint64_t)); + read_stream(&segments[i]->decompressed_size, + &data_stream->data, + sizeof(uint64_t)); + read_stream(&segments[i]->compressed_size, + &data_stream->data, + sizeof(uint64_t)); + } + printf("\nSEGMENT SECTION\n---------------\n\n"); + printf("SEGMENT_COUNT: %" PRIu64 "\n", segment_count); + for (int i = 0; i < segment_count; i++) { + printf("\SEGMENT %d\n----------\n", i); + printf("%s\n", segments[i]->compressed ? "COMPRESSED" : "DECOMPRESSED"); + printf("FILE_OFFSET: %" PRIu64 "\n", segments[i]->offset); + printf("COMPRESSED_SIZE: %" PRIu64 "\n", segments[i]->compressed_size); + printf("DECOMPRESSED_SIZE: %" PRIu64 "\n", segments[i]->decompressed_size); + } + parsed->segment_count = segment_count; + parsed->segments = segments; } /* Document */ -void read_adlr_chunk(memory_stream *data_stream) { +void read_adlr_chunk(memory_stream *data_stream, file_node *parsed) { uint32_t key; read_stream(&key, &data_stream->data, sizeof(uint32_t)); - printf("\nADLR SEGMENT\n"); - printf("------------\n"); - printf("KEY: %" PRIx32 "\n\n", key); + parsed->key = key; } /* Document */ -void read_time_chunk(memory_stream *data_stream) { +void read_time_chunk(memory_stream *data_stream, file_node *parsed) { uint64_t timestamp; read_stream(×tamp, &data_stream->data, sizeof(uint64_t)); printf("\nTIME SEGMENT\n"); diff --git a/src/file.h b/src/file.h index c8dcc1d..dc96154 100644 --- a/src/file.h +++ b/src/file.h @@ -19,11 +19,24 @@ #include +/* Structure representing a 28-byte segment in a file. */ typedef struct { - uint32_t filename_key; - uint64_t timestamp, compressed_size, decompressed_size, offset; -} file_entry; -// Decompressed/compressed size same in info/segm? - -/* Document and update documentation in header file. */ -void read_file_entry(memory_stream *data_stream, Bytef *section_end); + uint32_t compressed; /* Whether or not the chunk is compressed. */ + uint64_t offset; /* Chunk's position in the file as an offset. */ + uint64_t compressed_size; /* Chunk's compressed size. */ + uint64_t decompressed_size; /* Chunk's decompressed size. */ +} segment; + +/* Node in a linked list of file entries to write to disk. */ +typedef struct file_node { + int compressed; /* Whether or not the archive is compressed. */ + uint32_t key; /* Key associated with matching eliF entry. */ + uint64_t compressed_size; /* Size of compressed chunk. */ + uint64_t decompressed_size; /* Size of decompressed data. */ + uint64_t segment_count; /* Number of segments in File entry. */ + segment **segments; /* Data segments associated with the entry. */ + struct file_node *next; /* Pointer to the next node. */ +} file_node; + +/* Creates a file node by parsing a file entry. */ +file_node *read_file_node(memory_stream *data_stream, Bytef *section_end); diff --git a/src/main.c b/src/main.c index efda3bc..9effc9e 100644 --- a/src/main.c +++ b/src/main.c @@ -16,7 +16,6 @@ along with Nekopack. If not, see . */ #include -#include // Needed for debugging at this point. #include #include #include diff --git a/src/write.c b/src/write.c index 3cf04ca..1bd83da 100644 --- a/src/write.c +++ b/src/write.c @@ -19,23 +19,23 @@ #include #include #include +#include +#include "decompress.h" +#include "extract.h" +#include "file.h" #include "write.h" -/* Document */ -char *pop_file_name(uint32_t key, node *root) { - /* The root node shouldn't contain anything. */ - if (root->next == NULL) - return NULL; - - const char *file_name = NULL; - node *current = root->next, *last = root; +/* Finds an eliF entry with the given key in a linked list and returns + the associated filename, removing it from the linked list. */ +char *pop_file_name(uint32_t key, elif_node *root) { + char *file_name = NULL; + elif_node *current = root->next, *last = root; for (;;) { if (current->key == key) { file_name = current->file_name; last->next = current->next; - free(current->file_name); free(current); break; } else if (current->next == NULL) { @@ -49,9 +49,42 @@ char *pop_file_name(uint32_t key, node *root) { } -/* Document */ -void defer_node(node *new, node *root) { - node *current = root; +/* Iterates through the linked list of file entries and writes every + entry to disk, according to information specified by the node. */ +void write_files(file_node *file_root, elif_node *elif_root, Bytef *start) { + FILE *output; + file_node *current; + Bytef *compressed_buffer, *decompressed_buffer; + for (current = file_root->next; current != NULL; current = current->next) { + char *file_name = pop_file_name(current->key, elif_root); + if (file_name == NULL) { + fprintf(stderr, "File found without matching eliF entry.\n"); + continue; + } + compressed_buffer = malloc(current->compressed_size); + for (int i = 0; i < current->segment_count; i++) + free(current->segments[i]); + free(current->segments); + /* memcpy(compressed_buffer, start + current->offset, */ + /* current->decompressed_size); */ + /* if (current->compressed) { */ + /* fprintf(stderr, "Not implemented :^)\n"); // don't leave this in lol */ + /* continue; */ + /* } else { */ + /* output = fopen(file_name, "wb+"); */ + /* fwrite(compressed_buffer, current->compressed_size, 1, output); */ + /* fclose(output); */ + /* } */ + + free(compressed_buffer); + break; + } +} + + +/* Inserts an eliF entry at the end of a linked list. */ +void defer_elif_node(elif_node *new, elif_node *root) { + elif_node *current = root; for (;;) { if (current->next == NULL) { current->next = new; @@ -63,20 +96,49 @@ void defer_node(node *new, node *root) { } -void test_linked_list(node *root) { - for (node *current = root; current != NULL; current = current->next) { - printf("NODE\n----\n"); +/* Inserts a File entry node at the end of a linked list. */ +void defer_file_node(file_node *new, file_node *root) { + file_node *current = root; + for (;;) { + if (current->next == NULL) { + current->next = new; + break; + } else { + current = current->next; + } + } +} + + +/* Iterates through the linked list and frees all entries. */ +void free_elif_nodes(elif_node *base) { + if (base->next != NULL) + free_elif_nodes(base->next); + free(base->file_name); + free(base); +} + + +/* Iterates through the linked list and frees all entries. */ +void free_file_nodes(file_node *base) { + if (base->next != NULL) + free_file_nodes(base->next); + free(base); +} + + +void test_elif_linked_list(elif_node *root) { + for (elif_node *current = root; current != NULL; current = current->next) { + printf("ELIF NODE\n---------\n"); printf("KEY: %" PRIx32 "\n", current->key); printf("FILE_NAME: %s\n\n", current->file_name); } } -/* Document */ -void free_node(node *base) { - if (base->next != NULL) { - free_node(base->next); - return; +void test_file_linked_list(file_node *root) { + for (file_node *current = root; current != NULL; current = current->next) { + printf("FILE NODE\n---------\n"); + printf("KEY: %" PRIx32 "\n\n", current->key); } - free(base); } diff --git a/src/write.h b/src/write.h index 4b8c717..d37adda 100644 --- a/src/write.h +++ b/src/write.h @@ -17,8 +17,12 @@ /* Because File entries won't necessarily follow the associated eliF entry, filenames and hashes are stored in a linked list. */ -typedef struct node { - uint32_t key; - char *file_name; - struct node *next; -} node; +typedef struct elif_node { + uint32_t key; /* Key associated with matching File entry. */ + char *file_name; /* Name of file. */ + struct elif_node *next; /* Pointer to the next node. */ +} elif_node; + +/* Iterates through the linked list of file entries and writes every + entry to disk, according to information specified by the node. */ +void write_files(file_node *file_root, elif_node *elif_root, Bytef *start); -- cgit v1.3