/* 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
#include
#include
#include "crypto.h"
#include "decompress.h"
#include "extract.h"
#include "file.h"
#include "write.h"
/* Global instance of the command-line configuration structure. */
extern struct configuration arguments;
/* 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);
break;
} else if (current->next == NULL) {
break;
} else {
last = current;
current = current->next;
}
}
return file_name;
}
/* 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, FILE *archive) {
file_node *current;
key encryption_key = get_encryption_key(arguments.source);
Bytef *compressed_buffer, *decompressed_buffer, *out_buffer, *out_start;
for (current = file_root->next; current != NULL; current = current->next) {
char *file_name = pop_file_name(current->key, elif_root);
if (file_name == NULL)
continue;
/* out_start is kept so out_buffer can be incremented. */
out_buffer = malloc(current->file_size);
out_start = out_buffer;
for (uint64_t i = 0; i < current->segment_count; i++) {
segment *chunk = current->segments[i];
fseek(archive, chunk->offset, SEEK_SET);
compressed_buffer = malloc(chunk->compressed_size);
fread(compressed_buffer, chunk->compressed_size, 1, archive);
if (chunk->compressed) {
decompressed_buffer = inflate_chunk(compressed_buffer,
chunk->compressed_size,
chunk->decompressed_size);
free(compressed_buffer);
} else {
decompressed_buffer = compressed_buffer;
}
memcpy(out_buffer, decompressed_buffer, chunk->decompressed_size);
out_buffer += chunk->decompressed_size;
free(decompressed_buffer);
}
if (arguments.source != NO_CRYPTO) {
decrypt_buffer(out_start, current->file_size,
encryption_key, current->key);
}
FILE *output = fopen(file_name, "wb+");
if (output == NULL) {
perror(file_name);
break;
}
fwrite(out_start, current->file_size, 1, output);
fclose(output);
free(out_start);
}
}
/* 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;
break;
} else {
current = current->next;
}
}
}
/* 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);
}
if (base->file_name)
printf("%s\n", base->file_name);
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);
}