Skip to content

Commit

Permalink
Merge pull request #1 from wows-tools/unpack
Browse files Browse the repository at this point in the history
Unpack
  • Loading branch information
kakwa authored Mar 15, 2024
2 parents 1bd110a + 931af02 commit 09e6f74
Show file tree
Hide file tree
Showing 16 changed files with 424 additions and 369 deletions.
39 changes: 13 additions & 26 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,15 @@ set(wows-depack_VERSION_PATCH 0)

set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/")

if(STATIC)
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
set(ZLIB_USE_STATIC_LIBS ON)
set(BUILD_SHARED_LIBRARIES OFF)
set(CMAKE_EXE_LINKER_FLAGS "-static")
else(STATIC)
set(SHARED "SHARED")
endif(STATIC)

find_package(ZLIB REQUIRED)
find_package(PCRE REQUIRED)
find_library(CUNIT_LIBRARY cunit)
Expand All @@ -32,41 +41,15 @@ set(CMAKE_CXX_FLAGS
# Options
option(DEBUG "compile with debug symbol" OFF)
option(STATIC "compile statically" OFF)
option(USE_CLANG "build application with clang" OFF)
option(USE_GCC "build application with gcc" OFF)
option(FORCELE "force little endian architecture" OFF)
option(COVERAGE "Enable code coverage" OFF)
option(BUILD_DOC "Build documentation" OFF)
option(BUILD_TESTS "Build tests" OFF)

if(USE_CLANG)
set(CMAKE_CXX_COMPILER "clang++")
set(CMAKE_CC_COMPILER "clang")
endif(USE_CLANG)

if(USE_GCC)
set(CMAKE_CXX_COMPILER "g++")
set(CMAKE_CC_COMPILER "gcc")
endif(USE_GCC)

if(DEBUG)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0 -g")
set(CMAKE_BUILD_TYPE Debug)
endif(DEBUG)

if(STATIC)
set(SHARED "")
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
set(BUILD_SHARED_LIBRARIES OFF)
set(CMAKE_EXE_LINKER_FLAGS "-static")
else(STATIC)
set(SHARED "SHARED")
endif(STATIC)

if(UNIX)
link_libraries(m)
endif(UNIX)

# Build external dependancies if we are on OSX
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
# Mac OS X specific code
Expand All @@ -75,6 +58,10 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
add_definitions(-DDARWIN)
endif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")

if(UNIX)
link_libraries(m)
endif(UNIX)

# linking directories
link_directories(${CMAKE_BINARY_DIR}/ /usr/local/lib /usr/lib/)

Expand Down
4 changes: 2 additions & 2 deletions FORMAT.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ The index file is composed of 5 sections:
+====+====+====+====++====+====+====+====++====+====+====+====++====+====+====+====+
| MA | MA | MA | MA || 00 | 00 | 00 | 02 || ID | ID | ID | ID || 40 | 00 | 00 | 00 |
+====+====+====+====++====+====+====+====++====+====+====+====++====+====+====+====+
|<----- magic ----->||<--- unknown_1 --->||<------- id ------>||<--- unknown_2 --->|
|<----- magic ----->||<--- endianess --->||<------- id ------>||<--- unknown_2 --->|
| 32 bits || 32 bits || 32 bits || 32 bits |
+====+====+====+====++====+====+====+====++====+====+====+====++====+====+====+====+
Expand All @@ -121,7 +121,7 @@ The index file is composed of 5 sections:
| Field | size | Description |
|----------------------------|---------|-------------------------------------------------------------------------------------------------------------------------------------------------|
| `magic` | 32 bits | Magic bytes, always "ISFP" |
| `unknown_1` | 32 bits | Unknown, always 0x2000000 |
| `endianess` | 32 bits | Endianess marker, always 0x2000000 if LE |
| `id` | 32 bits | Unsure, unique per index file, not referenced anywhere else |
| `unknown_2` | 32 bits | Unknown, always 0x40, maybe some offset |
| `file_dir_count` | 32 bits | Number of files + directories (Nfd), also number of entries in the metadata section and the file names section |
Expand Down
2 changes: 2 additions & 0 deletions inc/wows-depack.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#define WOWS_ERROR_NOT_A_DIR 13 /**< path is not a directory */
#define WOWS_ERROR_NOT_FOUND 14 /**< file or directory not found */
#define WOWS_ERROR_FILE_WRITE 15 /**< file write error */
#define WOWS_ERROR_MAX_FILE 16 /**< maximum number of file/dir */

/* ---------- */

Expand Down Expand Up @@ -61,6 +62,7 @@ typedef struct {
void **indexes; // Array of structures representing each index file (WOWS_INDEX
// **indexes;)
uint32_t index_count; // Size of the array
bool is_le; // Flag for endianess (true if LE, false if BE)
char *err_msg; // Last error message
} WOWS_CONTEXT;

Expand Down
41 changes: 9 additions & 32 deletions lib/debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

int print_header(WOWS_INDEX_HEADER *header) {
printf("* magic: %.4s\n", (char *)&header->magic);
printf("* unknown_1: 0x%x\n", header->unknown_1);
printf("* endianess: 0x%x\n", header->endianess);
printf("* id: 0x%x\n", header->id);
printf("* unknown_2: 0x%x\n", header->unknown_2);
printf("* file_dir_count: %u\n", header->file_dir_count);
Expand Down Expand Up @@ -63,13 +63,8 @@ int print_debug_raw(WOWS_INDEX *index) {
WOWS_INDEX_METADATA_ENTRY *entry = &metadatas[i];
printf("Metadata entry [%d]:\n", i);
print_metadata_entry(entry);
char *filename;
int ret = get_metadata_filename(entry, index, &filename);
if (ret == 0) {
printf("* filename: %.*s\n", (int)entry->file_name_size, filename);
} else {
printf("* pkg filename: Error %d\n", ret);
}
char *filename = entry->_file_name;
printf("* filename: %.*s\n", (int)entry->file_name_size, filename);
printf("\n");
}

Expand All @@ -85,13 +80,8 @@ int print_debug_raw(WOWS_INDEX *index) {

printf("Index Footer Content:\n");
print_footer(footer);
char *pkg_filename;
int ret = get_footer_filename(footer, index, &pkg_filename);
if (ret == 0) {
printf("* pkg filename: %.*s\n", (int)footer->pkg_file_name_size, pkg_filename);
} else {
printf("* pkg filename: Error %d\n", ret);
}
char *pkg_filename = footer->_file_name;
printf("* pkg filename: %.*s\n", (int)footer->pkg_file_name_size, pkg_filename);
printf("\n");
return 0;
}
Expand All @@ -113,30 +103,17 @@ int print_debug_files(WOWS_INDEX *index, struct hashmap *map) {
printf("File entry [%d]:\n", i);
print_data_file_entry(fentry);
print_metadata_entry(mentry);
char *filename;
int ret = get_metadata_filename(mentry, index, &filename);

if (ret == 0) {
printf("* filename: %.*s\n", (int)mentry->file_name_size, filename);
} else {
printf("* pkg filename: Error %d\n", ret);
}
char *filename = mentry->_file_name;
printf("* filename: %.*s\n", (int)mentry->file_name_size, filename);
WOWS_INDEX_METADATA_ENTRY *m_parent_entry_search = &(WOWS_INDEX_METADATA_ENTRY){.id = mentry->parent_id};
WOWS_INDEX_METADATA_ENTRY **mparent_entry =
(WOWS_INDEX_METADATA_ENTRY **)hashmap_get(map, &m_parent_entry_search);
int level = 1;
while (mparent_entry != NULL && level < WOWS_DIR_MAX_LEVEL) {
printf("parent [%d]:\n", level);
print_metadata_entry(*mparent_entry);
char *filename;
int ret = get_metadata_filename(*mparent_entry, index, &filename);

if (ret == 0) {
printf("* filename: %.*s\n", (int)mentry->file_name_size, filename);
} else {
printf("* pkg filename: Error %d\n", ret);
}

char *filename = (*mparent_entry)->_file_name;
printf("* filename: %.*s\n", (int)mentry->file_name_size, filename);
mentry_search = &(WOWS_INDEX_METADATA_ENTRY){.id = (*mparent_entry)->parent_id};
mparent_entry = NULL;
mparent_entry = (WOWS_INDEX_METADATA_ENTRY **)hashmap_get(map, &mentry_search);
Expand Down
99 changes: 40 additions & 59 deletions lib/deflate.c
Original file line number Diff line number Diff line change
Expand Up @@ -47,22 +47,20 @@ uint64_t random_uint64() {

/**
Appends a file name to the input buffer at the given offset.
@param input_buffer A pointer to a pointer to the input buffer.
@param offset A pointer to the offset in the input buffer where the file name should be written.
@param name The file name to write to the input buffer.
@param current_size A pointer to the current size of the input buffer.
@return Returns 0 on success, or a non-zero value on failure.
*/
int write_file_name(char **input_buffer, size_t *offset, char *name, size_t *current_size) {
int write_file_name(wows_writer *writer, char *name) {
char *input_buffer = writer->filename_section;
size_t offset = writer->filename_section_offset;
size_t current_size = writer->filename_section_size;
size_t len = strlen(name) + 1;
if ((*current_size - *offset) < len) {
*input_buffer = realloc(*input_buffer, *current_size + CHUNK_NAME_SECTION);
*current_size += CHUNK_NAME_SECTION;
}
memcpy(*input_buffer + *offset, name, len);
*offset += len;

char *new_buffer = calloc(sizeof(char), current_size + len);
memcpy(new_buffer, input_buffer, current_size);
free(writer->filename_section);
writer->filename_section = new_buffer;
writer->filename_section_size = current_size + len;
memcpy(writer->filename_section + offset, name, len);
writer->filename_section_offset += len;
return 0;
}

Expand Down Expand Up @@ -183,36 +181,28 @@ int write_data_blob(char *file_path, FILE *pkg_fp, uint64_t *offset, uint32_t *s
}

/**
Writes an entry for metadata in a World of Warships (WoWS) package metadata section.
*/

@param metadata A pointer to a pointer to an array of WOWS_INDEX_METADATA_ENTRY structs representing the metadata
section.
@param metadata_section_size A pointer to a 64-bit unsigned integer representing the size of the metadata section
array.
@param metadata_id A 64-bit unsigned integer representing the ID for the metadata entry.
@param file_name_size A 64-bit unsigned integer representing the size of the file name for the metadata entry.
@param offset_idx_file_name A 64-bit unsigned integer representing the offset of the file name in the file name
section.
@param parent_id A 64-bit unsigned integer representing the ID of the parent directory for the metadata entry.
@param file_plus_dir_count A pointer to a 64-bit unsigned integer representing the number of files and directories
in the metadata section array.
int write_metadata_entry(wows_writer *writer, uint64_t metadata_id, uint64_t parent_id, uint64_t offset_idx_file_name,
char *file_name) {
uint64_t metadata_section_size = writer->metadata_section_size;
uint64_t file_plus_dir_count = writer->file_plus_dir_count;

@return Returns 0 on success, or a non-zero value on failure.
*/
int write_metadata_entry(WOWS_INDEX_METADATA_ENTRY **metadata, uint64_t *metadata_section_size, uint64_t metadata_id,
uint64_t file_name_size, uint64_t offset_idx_file_name, uint64_t parent_id,
uint64_t *file_plus_dir_count) {
// If the metadata section array is full, allocate more space for it.
if ((*metadata_section_size - *file_plus_dir_count) == 0) {
*metadata = realloc(*metadata, sizeof(WOWS_INDEX_METADATA_ENTRY) * (*file_plus_dir_count + CHUNK_FILE));
(*metadata_section_size) += CHUNK_FILE;
if ((metadata_section_size - file_plus_dir_count) == 0) {
writer->index->metadata =
realloc(writer->index->metadata, sizeof(WOWS_INDEX_METADATA_ENTRY) * (file_plus_dir_count + CHUNK_FILE));
writer->metadata_section_size += CHUNK_FILE;
}
(*metadata)[*file_plus_dir_count].id = metadata_id;
(*metadata)[*file_plus_dir_count].file_name_size = file_name_size;
(*metadata)[*file_plus_dir_count].offset_idx_file_name = offset_idx_file_name;
(*metadata)[*file_plus_dir_count].parent_id = parent_id;
(*file_plus_dir_count)++;
(writer->index->metadata)[file_plus_dir_count].id = metadata_id;
(writer->index->metadata)[file_plus_dir_count].file_name_size = strlen(file_name);
(writer->index->metadata)[file_plus_dir_count].offset_idx_file_name = offset_idx_file_name;
(writer->index->metadata)[file_plus_dir_count].parent_id = parent_id;
char *file_name_cpy = calloc(sizeof(char), strlen(file_name) + 1);
strcpy(file_name_cpy, file_name);
(writer->index->metadata)[file_plus_dir_count]._file_name = file_name_cpy;
writer->file_plus_dir_count++;
return 0;
}

Expand All @@ -232,6 +222,8 @@ int wows_write_pkg(WOWS_CONTEXT *context, char *in_path, char *name_pkg, FILE *p
index->footer->pkg_file_name_size = strlen(name_pkg) + 1;
index->footer->id = writer->footer_id;
index->footer->unknown_7 = random_uint64(); // FIXME dubious, but we don't know what this field does.
index->footer->_file_name = calloc(sizeof(char), strlen(name_pkg) + 1);
memcpy(index->footer->_file_name, name_pkg, strlen(name_pkg) + 1);
memcpy((char *)(index->footer) + sizeof(WOWS_INDEX_FOOTER), name_pkg, strlen(name_pkg) + 1);

uint64_t parent_id = random_uint64();
Expand All @@ -254,7 +246,7 @@ int wows_write_pkg(WOWS_CONTEXT *context, char *in_path, char *name_pkg, FILE *p

// Setup header
memcpy(index->header->magic, "ISFP", 4);
index->header->unknown_1 = 0x2000000;
index->header->endianess = 0x2000000;
index->header->id = (uint32_t)rand();
index->header->unknown_2 = 0x40;
index->header->file_dir_count = writer->file_plus_dir_count;
Expand All @@ -264,11 +256,7 @@ int wows_write_pkg(WOWS_CONTEXT *context, char *in_path, char *name_pkg, FILE *p
index->header->offset_idx_data_section = 0; // Recomputed by wows_dump_index_to_file
index->header->offset_idx_footer_section = 0; // Same
wows_dump_index_to_file(index, idx_fp);
free(index->header);
free(index->metadata);
free(index->data_file_entry);
free(index->footer);
free(index);
wows_free_index(index, 0);
free(writer);
return 0;
}
Expand Down Expand Up @@ -301,12 +289,8 @@ int recursive_writer(wows_writer *writer, char *path, uint64_t parent_id) {
uint64_t offset_idx_file_name = writer->filename_section_offset;
// TODO use the context to check if this entry already exists (if so we must reuse the same ID)
uint64_t metadata_id = random_uint64();

write_file_name(&(writer->filename_section), &(writer->filename_section_offset), entry->d_name,
&(writer->filename_section_size));
write_metadata_entry(&(writer->index->metadata), &(writer->metadata_section_size), metadata_id,
(strlen(entry->d_name) + 1), offset_idx_file_name, parent_id,
&(writer->file_plus_dir_count));
write_file_name(writer, entry->d_name);
write_metadata_entry(writer, metadata_id, parent_id, offset_idx_file_name, entry->d_name);
recursive_writer(writer, full_path, metadata_id);
} else if (S_ISREG(info.st_mode)) {
uint64_t offset_idx_file_name = writer->filename_section_offset;
Expand All @@ -316,11 +300,8 @@ int recursive_writer(wows_writer *writer, char *path, uint64_t parent_id) {
// Shift by 16 bits to mimic wows IDs
uint64_t pkg_id = random_uint64() >> 16;
write_data_blob(full_path, writer->pkg_fp, &(writer->pkg_cur_offset), &size, pkg_id);
write_file_name(&(writer->filename_section), &(writer->filename_section_offset), entry->d_name,
&(writer->filename_section_size));
write_metadata_entry(&(writer->index->metadata), &(writer->metadata_section_size), metadata_id,
(strlen(entry->d_name) + 1), offset_idx_file_name, parent_id,
&(writer->file_plus_dir_count));
write_file_name(writer, entry->d_name);
write_metadata_entry(writer, metadata_id, parent_id, offset_idx_file_name, entry->d_name);
write_file_pkg_entry(&(writer->index->data_file_entry), &(writer->file_section_size), metadata_id,
writer->footer_id, start_offset, size, pkg_id, &(writer->file_count));
}
Expand Down Expand Up @@ -356,9 +337,9 @@ int wows_dump_index_to_file(WOWS_INDEX *index, FILE *f) {
// Write the file name strings
for (size_t i = 0; i < index->header->file_dir_count; i++) {
WOWS_INDEX_METADATA_ENTRY *metadata_entry = &index->metadata[i];
char *file_name;
get_metadata_filename_unsafe(metadata_entry, index, &file_name);
fwrite(file_name, metadata_entry->file_name_size, 1, f);
char *file_name = metadata_entry->_file_name;
if (file_name != NULL && metadata_entry->file_name_size != 0)
fwrite(file_name, metadata_entry->file_name_size, 1, f);
general_offset += metadata_entry->file_name_size;
}

Expand Down
21 changes: 12 additions & 9 deletions lib/error-helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,31 +14,31 @@ char *wows_error_string(int error_code, WOWS_CONTEXT *context) {

switch (error_code) {
case WOWS_ERROR_CORRUPTED_FILE:
error_string = "The index is corrupted";
error_string = "index is corrupted";
break;
case WOWS_ERROR_BAD_MAGIC:
error_string = "The index has an invalid magic number";
error_string = "index has an invalid magic number";
break;
case WOWS_ERROR_MISSING_METADATA_ENTRY:
error_string = "The file is missing a required metadata entry";
error_string = "file is missing a required metadata entry";
break;
case WOWS_ERROR_MAX_LEVEL_REACHED:
error_string = "The maximum level has been reached";
error_string = "maximum depth level has been reached";
break;
case WOWS_ERROR_NON_ZERO_TERMINATED_STRING:
error_string = "A string in the index is not null-terminated";
error_string = "string in index file not null-terminated";
break;
case WOWS_ERROR_PATH_TOO_LONG:
error_string = "The file path is too long";
error_string = "file path too long";
break;
case WOWS_ERROR_UNKNOWN:
error_string = "An unknown error occurred";
error_string = "unknown error occurred";
break;
case WOWS_ERROR_ID_COLLISION_FILE_DIR:
error_string = "There is an ID collision between a file and a directory";
error_string = "ID collision between a file and a directory";
break;
case WOWS_ERROR_FILE_OPEN_FAILURE:
error_string = "The index could not be opened";
error_string = "index file could not be opened";
break;
case WOWS_ERROR_DECOMPOSE_PATH:
error_string = "failed to decompose path into [dir1, dir2, etc] + file";
Expand All @@ -58,6 +58,9 @@ char *wows_error_string(int error_code, WOWS_CONTEXT *context) {
case WOWS_ERROR_FILE_WRITE:
error_string = "file write error";
break;
case WOWS_ERROR_MAX_FILE:
error_string = "maximum number of file/dir in index reached";
break;
case 0:
error_string = "no recent error";
break;
Expand Down
Loading

0 comments on commit 09e6f74

Please sign in to comment.