home *** CD-ROM | disk | FTP | other *** search
- #include "packed_file.h"
- #include "string_btree.h"
- #include "fileid_btree.h"
- #include "fileinfo_btree.h"
- #include "volume.h"
- #include "crypt.h"
- #include "hash.h"
- #include "compression.h"
- #include "file.h"
- #include "memory_file.h"
- #include "path.h"
- #include "util.h"
-
- PackedFile::PackedFile(const char* const file_path, const void* const data, const uint32_t data_size, const uint32_t segment_size)
- : file_path_(nullptr)
- , file_data_(nullptr)
- , segment_size_(0)
- , name_table_offset_(0)
- , extension_table_offset_(0)
- , file_info_table_offset_(0)
- , num_file_id_trees_(0)
- {
- const uint32_t magic = data_at<uint32_t>(data, 0x00);
- if (magic == kMAGIC) {
- file_path_ = file_path;
- file_data_ = new char[data_size];
- memcpy(file_data_, data, data_size);
- segment_size_ = segment_size;
- name_table_offset_ = data_at<uint32_t>(file_data_, 0x04);
- extension_table_offset_ = data_at<uint32_t>(file_data_, 0x08);
- file_info_table_offset_ = data_at<uint32_t>(file_data_, 0x0C);
- num_file_id_trees_ = data_at<uint32_t>(file_data_, 0x10);
- }
- }
-
- PackedFile::~PackedFile()
- {
- delete[] file_data_;
- }
-
- bool PackedFile::get_file_info(const char* const file_path, FileInfo* const file_info) const
- {
- StringBTree name_btree(advance_pointer(file_data_, name_table_offset_));
- StringBTree extension_btree(advance_pointer(file_data_, extension_table_offset_));
-
- FileInfoBTree file_info_btree(advance_pointer(file_data_, file_info_table_offset_));
- uint32_t file_info_id = BTreeBase::kINVALID_INDEX;
-
- const char* part = file_path;
- uint32_t index = 0;
- while (*part != '\0') {
- const char* const delimeter = path_get_delimeter(part);
- const char* extension = nullptr;
- if (delimeter > part) {
- if (*(delimeter - 1) != '.') {
- extension = delimeter - 1;
- do {
- extension--;
- } while (extension > part && *extension != '.');
- if (*extension != '.')
- extension = nullptr;
- }
- } else {
- assert(0);
- }
- const size_t part_length = extension ? (extension - part) : (delimeter - part);
-
- const uint32_t name_index = name_btree.index_by_string(part, part_length);
- const uint32_t extension_index = extension ? extension_btree.index_by_string(extension) : 0;
-
- if (name_index != BTreeBase::kINVALID_INDEX) {
- FileIdBTree file_id_btree(advance_pointer(file_data_, file_id_table_offset(index)));
- const void* const file_id_data = file_id_btree.search(name_index, extension_index);
- if (file_id_data) {
- FileId file_id;
- if (file_id.parse(file_id_data)) {
- if ((file_id.flag & FileId::kDIRECTORY_FLAG) != 0)
- index = file_id.entry_index;
- else
- file_info_id = file_id.entry_index;
- } else {
- assert(0);
- }
- }
- }
-
- part = path_skip_delimeter(delimeter);
- }
-
- if (file_info_id != BTreeBase::kINVALID_INDEX) {
- const void* const data = file_info_btree.search(file_info_id);
- if (data)
- return file_info->parse(data) != nullptr;
- }
-
- return false;
- }
-
- uint32_t PackedFile::file_id_table_offset(const uint32_t index) const
- {
- const uint32_t offset = data_at<uint32_t>(file_data_, kHEADER_SIZE + index * sizeof(uint32_t));
- return offset;
- }
-
- uint64_t PackedFile::data_offset() const
- {
- const uint64_t offset = align_up(kSEGMENT_SIZE + segment_size_, kSEGMENT_SIZE);
- return offset;
- }
-
- struct TraverseEntryArgs
- {
- const PackedFile* packed_file;
- const char* output_dir;
- unsigned int depth;
- char prefix[256];
- };
-
- bool build_entry_path(const PackedFile& packed_file, const FileId& file_id, const char* const prefix, const char** const path)
- {
- StringBTree name_btree(advance_pointer(packed_file.file_data(), packed_file.name_table_offset()));
- StringBTree extension_btree(advance_pointer(packed_file.file_data(), packed_file.extension_table_offset()));
-
- String name;
- String extension;
-
- const bool has_name = name_btree.search_by_index(file_id.name_index, &name);
- const bool has_extension = extension_btree.search_by_index(file_id.extension_index, &extension) && extension.length != 0;
-
- const unsigned int prefix_length = strlen(prefix);
-
- unsigned int path_length = prefix_length;
- if (has_name) {
- if (has_extension)
- path_length += name.length + extension.length;
- else
- path_length += name.length;
- } else if (has_extension) {
- assert(0);
- }
-
- char* path_buffer = new char[path_length + 1];
- memset(path_buffer, 0, path_length + 1);
- if (prefix_length != 0)
- memcpy(path_buffer, prefix, prefix_length);
- if (has_name) {
- memcpy(path_buffer + prefix_length, name.data, name.length);
- if (has_extension)
- memcpy(path_buffer + prefix_length + name.length, extension.data, extension.length);
- }
- path_buffer[path_length] = '\0';
-
- *path = path_buffer;
-
- return true;
- }
-
- int traverse_callback(const FileId* const file_id, void* const arg)
- {
- TraverseEntryArgs* const args = static_cast<TraverseEntryArgs*>(arg);
-
- const bool is_directory = (file_id->flag & FileId::kDIRECTORY_FLAG) != 0;
- const char* path;
- if (!build_entry_path(*args->packed_file, *file_id, args->prefix, &path))
- path = nullptr;
-
- if (is_directory) {
- assert(path != nullptr);
-
- TraverseEntryArgs new_args = { args->packed_file, args->output_dir, args->depth + 2 };
- strncpy(new_args.prefix, path, sizeof(new_args.prefix));
- strcat(new_args.prefix, "/");
-
- FileIdBTree child_id_btree(advance_pointer(args->packed_file->file_data(), args->packed_file->file_id_table_offset(file_id->entry_index)));
- child_id_btree.traverse(&traverse_callback, &new_args);
- } else {
- assert(path != nullptr);
-
- FileInfoBTree info_btree(advance_pointer(args->packed_file->file_data(), args->packed_file->file_info_table_offset()));
- const void* const data = info_btree.search(file_id->entry_index);
- FileInfo file_info;
- if (file_info.parse(data)) {
- char file_path[_MAX_PATH];
- if (generate_file_path(file_info.entry_index, file_path, sizeof(file_path))) {
- char* data = nullptr;
- uint64_t data_offset = args->packed_file->data_offset() + file_info.segment_index * uint64_t(PackedFile::kSEGMENT_SIZE);
- uint32_t data_size = file_info.size1;
-
- if (read_file(args->packed_file->file_path(), &data, &data_size, data_offset)) {
- crypt_segment(data, data_size, file_info.entry_index);
-
- char full_file_path[_MAX_PATH];
- _snprintf(full_file_path, sizeof(full_file_path), "%s%s", args->output_dir, path);
-
- char directory_path[_MAX_PATH];
- char* p = strrchr(full_file_path, '/');
- if (p) {
- const size_t length = p - full_file_path + 1;
- strncpy(directory_path, full_file_path, length);
- directory_path[length] = '\0';
- make_directories(directory_path);
- }
-
- if (data_at<uint32_t>(data, 0) == 0xC5EEF7FFu) {
- const uint32_t decompressed_size = -little_to_host(*((int32_t*)data + 1));
- char* out_data = new char[decompressed_size];
- size_t out_size = decompressed_size;
- if (inflate(data + 8, data_size - 8, out_data, &out_size, -15) == 0)
- write_file(full_file_path, out_data, decompressed_size);
- delete[] out_data;
- } else {
- write_file(full_file_path, data, data_size);
- }
- }
-
- delete[] data;
- }
- }
- }
-
- delete[] path;
-
- return FileIdBTree::kTRAVERSE_CONTINUE;
- }
-
- void PackedFile::extract_entries(const char* const output_dir)
- {
- TraverseEntryArgs args = { this, output_dir, 0 };
- memset(args.prefix, 0, sizeof(args.prefix));
-
- FileIdBTree id_btree(advance_pointer(file_data_, file_id_table_offset(0)));
- id_btree.traverse(&traverse_callback, &args);
- }
-