Jessica James
8 years ago
1 changed files with 952 additions and 0 deletions
@ -0,0 +1,952 @@ |
|||||
|
/**
|
||||
|
* Copyright (C) 2016 Jessica James. |
||||
|
* |
||||
|
* Permission to use, copy, modify, and/or distribute this software for any |
||||
|
* purpose with or without fee is hereby granted, provided that the above |
||||
|
* copyright notice and this permission notice appear in all copies. |
||||
|
* |
||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
||||
|
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
||||
|
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
||||
|
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
||||
|
* |
||||
|
* Written by Jessica James <jessica.aj@outlook.com> |
||||
|
*/ |
||||
|
|
||||
|
#define _CRT_NONSTDC_NO_DEPRECATE |
||||
|
|
||||
|
#include <stdio.h> |
||||
|
#include <stdint.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <stdbool.h> |
||||
|
|
||||
|
#if defined _WIN32 |
||||
|
#include <Windows.h> |
||||
|
#endif // _WIN32
|
||||
|
|
||||
|
enum UDKPackage_Extension |
||||
|
{ |
||||
|
ext_UNKNOWN, |
||||
|
ext_UDK, |
||||
|
ext_UPK, |
||||
|
ext_U |
||||
|
}; |
||||
|
|
||||
|
/** Package */ |
||||
|
const char *package_filename = NULL; |
||||
|
uint32_t package_name = 0; |
||||
|
uint32_t package_GUID[4]; |
||||
|
enum UDKPackage_Extension package_extension = ext_UNKNOWN; |
||||
|
|
||||
|
/** Name table */ |
||||
|
uint32_t name_table_size = 0; |
||||
|
char **name_table = NULL; |
||||
|
|
||||
|
#define INVALID_NAME UINT32_MAX |
||||
|
|
||||
|
/** Import table */ |
||||
|
struct UDKImport |
||||
|
{ |
||||
|
uint32_t package_name_index; |
||||
|
uint32_t class_name_index; |
||||
|
int32_t package_reference; |
||||
|
uint32_t object_name_index; |
||||
|
}; |
||||
|
uint32_t import_table_size = 0; |
||||
|
struct UDKImport *import_table = NULL; |
||||
|
size_t packages_imported = 0; |
||||
|
|
||||
|
/** Against list */ |
||||
|
uint32_t against_list_size = 0; |
||||
|
uint32_t **against_list = NULL; |
||||
|
|
||||
|
/** Package table */ |
||||
|
struct UDKPackage |
||||
|
{ |
||||
|
uint32_t name_index; |
||||
|
uint32_t GUID[4]; |
||||
|
char *filename; |
||||
|
enum UDKPackage_Extension extension; |
||||
|
}; |
||||
|
struct UDKPackage *package_table; |
||||
|
|
||||
|
/** Dependency table */ |
||||
|
struct UDKPackage_Dependency |
||||
|
{ |
||||
|
struct UDKPackage *package; |
||||
|
struct UDKPackage_Dependency *next; |
||||
|
}; |
||||
|
uint32_t dependency_list_size = 0; |
||||
|
struct UDKPackage_Dependency *dependency_list_head = NULL; |
||||
|
struct UDKPackage_Dependency *dependency_list_last = NULL; |
||||
|
|
||||
|
/** Game package table */ |
||||
|
struct UDKPackage_Game |
||||
|
{ |
||||
|
char *name; |
||||
|
uint32_t GUID[4]; |
||||
|
|
||||
|
struct UDKPackage_Game *next; |
||||
|
}; |
||||
|
size_t game_package_table_size = 0; |
||||
|
struct UDKPackage_Game *game_package_table_head = NULL; |
||||
|
struct UDKPackage_Game *game_package_table_last = NULL; |
||||
|
|
||||
|
/** Utility Functions */ |
||||
|
|
||||
|
const char *str_find_suffix(const char *str, const char *suffix) |
||||
|
{ |
||||
|
const char *str_end = str; |
||||
|
const char *suffix_end = suffix; |
||||
|
|
||||
|
while (*str_end != '\0') |
||||
|
++str_end; |
||||
|
|
||||
|
while (*suffix_end != '\0') |
||||
|
++suffix_end; |
||||
|
|
||||
|
if (str_end - str < suffix_end - suffix) // Too short to contain suffix
|
||||
|
return NULL; |
||||
|
|
||||
|
while (suffix_end != suffix) |
||||
|
{ |
||||
|
if (*--str_end != *--suffix_end) |
||||
|
return NULL; |
||||
|
} |
||||
|
|
||||
|
return str_end; |
||||
|
} |
||||
|
|
||||
|
bool streql_2ptr(const char *filename, const char *filename_end, const char *package_name) |
||||
|
{ |
||||
|
while (filename != filename_end) |
||||
|
{ |
||||
|
if (toupper(*filename) != toupper(*package_name)) // Names are case-insensitive
|
||||
|
return false; |
||||
|
++filename, ++package_name; |
||||
|
} |
||||
|
return *package_name == '\0'; |
||||
|
} |
||||
|
|
||||
|
void read_guid(uint32_t *GUID, FILE *in_file) |
||||
|
{ |
||||
|
// seek to string size
|
||||
|
fseek(in_file, 0x0C, SEEK_SET); |
||||
|
|
||||
|
// read string size (repurpose GUID[0])
|
||||
|
fread(GUID, sizeof(uint32_t), 1, in_file); |
||||
|
|
||||
|
// seek to GUID
|
||||
|
fseek(in_file, *GUID + 0x30, SEEK_CUR); |
||||
|
|
||||
|
// read GUID
|
||||
|
fread(GUID, sizeof(uint32_t), 4, in_file); |
||||
|
} |
||||
|
|
||||
|
void free_UDKPackage_Game(struct UDKPackage_Game *package) |
||||
|
{ |
||||
|
free(package->name); |
||||
|
free(package); |
||||
|
} |
||||
|
|
||||
|
enum UDKPackage_Extension get_extension_from_filename(const char *filename, size_t filename_length) |
||||
|
{ |
||||
|
filename += filename_length; |
||||
|
while (filename_length != 0) |
||||
|
{ |
||||
|
if (*--filename == '.') // start of extension
|
||||
|
{ |
||||
|
++filename; |
||||
|
|
||||
|
if (strcmpi(filename, "udk")) |
||||
|
return ext_UDK; |
||||
|
if (strcmpi(filename, "upk")) |
||||
|
return ext_UPK; |
||||
|
if (strcmpi(filename, "u")) |
||||
|
return ext_U; |
||||
|
|
||||
|
return ext_UNKNOWN; |
||||
|
} |
||||
|
--filename_length; |
||||
|
} |
||||
|
return ext_UNKNOWN; |
||||
|
} |
||||
|
|
||||
|
const char *extension_as_string(enum UDKPackage_Extension extension) |
||||
|
{ |
||||
|
switch (extension) |
||||
|
{ |
||||
|
case ext_UDK: |
||||
|
return "udk"; |
||||
|
case ext_UPK: |
||||
|
return "upk"; |
||||
|
case ext_U: |
||||
|
return "u"; |
||||
|
default: |
||||
|
return ""; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** Name Table Functions */ |
||||
|
|
||||
|
void read_name_table(FILE *file) |
||||
|
{ |
||||
|
uint32_t tmp; |
||||
|
size_t index; |
||||
|
|
||||
|
// seek to string size
|
||||
|
fseek(file, 0x0C, SEEK_SET); |
||||
|
|
||||
|
// read string size
|
||||
|
fread(&tmp, sizeof(tmp), 1, file); |
||||
|
|
||||
|
// seek to name count
|
||||
|
fseek(file, tmp + 0x04, SEEK_CUR); |
||||
|
|
||||
|
// read name count
|
||||
|
fread(&name_table_size, sizeof(name_table_size), 1, file); |
||||
|
|
||||
|
// read name table offset
|
||||
|
fread(&tmp, sizeof(tmp), 1, file); |
||||
|
|
||||
|
// seek to name table
|
||||
|
fseek(file, tmp, SEEK_SET); |
||||
|
|
||||
|
// allocate array of char pointers
|
||||
|
if (name_table != NULL) |
||||
|
free(name_table); |
||||
|
name_table = (char **) malloc(sizeof(char *) * name_table_size); |
||||
|
|
||||
|
// read name table
|
||||
|
for (index = 0; index != name_table_size; ++index) |
||||
|
{ |
||||
|
// read name length (includes null term)
|
||||
|
fread(&tmp, sizeof(tmp), 1, file); |
||||
|
|
||||
|
// allocate string buffer & copy string from file
|
||||
|
name_table[index] = (char *) malloc(sizeof(char) * tmp); |
||||
|
fread(name_table[index], sizeof(char), tmp, file); |
||||
|
|
||||
|
// skip Object Flags
|
||||
|
fseek(file, 0x08, SEEK_CUR); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void print_name_table(FILE *out) |
||||
|
{ |
||||
|
size_t index; |
||||
|
|
||||
|
for (index = 0; index != name_table_size; ++index) |
||||
|
fprintf(out, "%u: %s\r\n", index, name_table[index]); |
||||
|
} |
||||
|
|
||||
|
uint32_t find_name(const char *name) |
||||
|
{ |
||||
|
uint32_t index; |
||||
|
|
||||
|
for (index = 0; index != name_table_size; ++index) |
||||
|
if (strcmp(name, name_table[index]) == 0) |
||||
|
return index; |
||||
|
|
||||
|
return INVALID_NAME; |
||||
|
} |
||||
|
|
||||
|
uint32_t find_name_2ptr(const char *name_start, const char *name_end) |
||||
|
{ |
||||
|
uint32_t index; |
||||
|
|
||||
|
for (index = 0; index != name_table_size; ++index) |
||||
|
if (streql_2ptr(name_start, name_end, name_table[index])) |
||||
|
return index; |
||||
|
|
||||
|
return INVALID_NAME; |
||||
|
} |
||||
|
|
||||
|
uint32_t name_from_filename(const char *filename, size_t filename_length) |
||||
|
{ |
||||
|
const char *start_name = NULL; |
||||
|
const char *end_name = NULL; |
||||
|
|
||||
|
filename += filename_length; |
||||
|
while (filename_length != 0) |
||||
|
{ |
||||
|
--filename; |
||||
|
|
||||
|
if (*filename == '.' && end_name == NULL) |
||||
|
end_name = filename; |
||||
|
|
||||
|
if (*filename == '\\' || *filename == '/') |
||||
|
{ |
||||
|
start_name = ++filename; |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
--filename_length; |
||||
|
} |
||||
|
|
||||
|
if (start_name == NULL) |
||||
|
start_name = filename; |
||||
|
|
||||
|
if (end_name == NULL) |
||||
|
return find_name(start_name); |
||||
|
|
||||
|
return find_name_2ptr(start_name, end_name); |
||||
|
} |
||||
|
|
||||
|
/** Import Table Functions */ |
||||
|
|
||||
|
void read_import_table(FILE *file) |
||||
|
{ |
||||
|
uint32_t tmp; |
||||
|
|
||||
|
// seek to string size
|
||||
|
fseek(file, 0x0C, SEEK_SET); |
||||
|
|
||||
|
// read string size
|
||||
|
fread(&tmp, sizeof(tmp), 1, file); |
||||
|
|
||||
|
// seek to import count
|
||||
|
fseek(file, tmp + 0x14, SEEK_CUR); |
||||
|
|
||||
|
// read import count
|
||||
|
fread(&import_table_size, sizeof(import_table_size), 1, file); |
||||
|
|
||||
|
// read import table offset
|
||||
|
fread(&tmp, sizeof(tmp), 1, file); |
||||
|
|
||||
|
// seek to import table
|
||||
|
fseek(file, tmp, SEEK_SET); |
||||
|
|
||||
|
// allocate array of UDKImport objects
|
||||
|
if (import_table != NULL) |
||||
|
free(import_table); |
||||
|
import_table = (struct UDKImport *) malloc(sizeof(struct UDKImport) * import_table_size); |
||||
|
|
||||
|
// read import table
|
||||
|
// fread(import_table + tmp, sizeof(struct UDKImport), import_table_size, file);
|
||||
|
for (tmp = 0; tmp != import_table_size; ++tmp) |
||||
|
{ |
||||
|
// Seek after read because:
|
||||
|
// "Name indexes work the same way as #Index but since Unreal Engine 3 indexes referencing a name, have a another Int32 followed after the index."
|
||||
|
// Source: http://eliotvu.com/page/unreal-package-file-format
|
||||
|
|
||||
|
fread(&import_table[tmp].package_name_index, sizeof(uint32_t), 1, file); |
||||
|
fseek(file, 0x04, SEEK_CUR); |
||||
|
|
||||
|
fread(&import_table[tmp].class_name_index, sizeof(uint32_t), 1, file); |
||||
|
fseek(file, 0x04, SEEK_CUR); |
||||
|
|
||||
|
fread(&import_table[tmp].package_reference, sizeof(int32_t), 1, file); |
||||
|
|
||||
|
fread(&import_table[tmp].object_name_index, sizeof(uint32_t), 1, file); |
||||
|
fseek(file, 0x04, SEEK_CUR); |
||||
|
|
||||
|
if (import_table[tmp].package_reference == 0) |
||||
|
++packages_imported; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void print_import_table(FILE *out) |
||||
|
{ |
||||
|
size_t index; |
||||
|
struct UDKImport *itr = import_table; |
||||
|
|
||||
|
for (index = 0; index != import_table_size; ++index, ++itr) |
||||
|
fprintf(out, "%u | Package: %s | Class: %s | Object: %s | Reference: %d\r\n", index, name_table[itr->package_name_index], name_table[itr->class_name_index], name_table[itr->object_name_index], itr->package_reference); |
||||
|
} |
||||
|
|
||||
|
/** Against list */ |
||||
|
|
||||
|
void read_against_list(FILE *against_file) |
||||
|
{ |
||||
|
uint32_t tmp; |
||||
|
|
||||
|
fread(&against_list_size, sizeof(against_list_size), 1, against_file); |
||||
|
against_list = (uint32_t **) malloc(sizeof(uint32_t *) * against_list_size); |
||||
|
|
||||
|
for (tmp = 0; tmp != against_list_size; ++tmp) |
||||
|
{ |
||||
|
against_list[tmp] = (uint32_t *) malloc(sizeof(uint32_t) * 4); |
||||
|
fread(against_list[tmp], sizeof(uint32_t), 4, against_file); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void build_against_list() |
||||
|
{ |
||||
|
struct UDKPackage_Game *package = game_package_table_head; |
||||
|
uint32_t **itr; |
||||
|
|
||||
|
against_list_size = game_package_table_size; |
||||
|
against_list = (uint32_t **) malloc(sizeof(uint32_t *) * against_list_size); |
||||
|
itr = against_list; |
||||
|
|
||||
|
while (package != NULL) |
||||
|
{ |
||||
|
*itr = (uint32_t *) malloc(sizeof(uint32_t) * 4); |
||||
|
|
||||
|
memcpy(*itr, package->GUID, sizeof(uint32_t) * 4); |
||||
|
|
||||
|
++itr; |
||||
|
package = package->next; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void write_against_list(FILE *against_file) |
||||
|
{ |
||||
|
uint32_t tmp; |
||||
|
|
||||
|
fwrite(&against_list_size, sizeof(against_list_size), 1, against_file); |
||||
|
|
||||
|
for (tmp = 0; tmp != against_list_size; ++tmp) |
||||
|
fwrite(against_list[tmp], sizeof(uint32_t), 4, against_file); |
||||
|
} |
||||
|
|
||||
|
bool is_in_against_list(uint32_t *GUID) |
||||
|
{ |
||||
|
uint32_t tmp; |
||||
|
|
||||
|
for (tmp = 0; tmp != against_list_size; ++tmp) |
||||
|
if (memcmp(GUID, against_list[tmp], sizeof(uint32_t) * 4) == 0) |
||||
|
return true; |
||||
|
|
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
/** Package Table Functions */ |
||||
|
|
||||
|
void init_package_table() |
||||
|
{ |
||||
|
struct UDKPackage *itr; |
||||
|
struct UDKPackage *end; |
||||
|
size_t index = 0; |
||||
|
|
||||
|
package_table = (struct UDKPackage *) malloc(sizeof(struct UDKPackage) * packages_imported); |
||||
|
itr = package_table; |
||||
|
end = package_table + packages_imported; |
||||
|
|
||||
|
while (itr != end) |
||||
|
{ |
||||
|
if (import_table[index].package_reference == 0) |
||||
|
{ |
||||
|
memset(itr->GUID, 0, sizeof(itr->GUID)); |
||||
|
itr->name_index = import_table[index].object_name_index; |
||||
|
itr->filename = NULL; |
||||
|
itr->extension = ext_UNKNOWN; |
||||
|
++itr; |
||||
|
} |
||||
|
++index; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
#if defined _WIN32 |
||||
|
|
||||
|
bool build_package_table(const char *directory) |
||||
|
{ |
||||
|
WIN32_FIND_DATA file_data; |
||||
|
HANDLE find_handle; |
||||
|
size_t directory_length = 0; |
||||
|
char *tmp; |
||||
|
size_t tmp_length; |
||||
|
size_t tmp_index; |
||||
|
FILE *tmp_file; |
||||
|
enum UDKPackage_Extension extension; |
||||
|
|
||||
|
find_handle = FindFirstFile(directory, &file_data); |
||||
|
|
||||
|
if (find_handle == INVALID_HANDLE_VALUE) |
||||
|
return false; // Error: Bad handle
|
||||
|
|
||||
|
do |
||||
|
{ |
||||
|
if (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) |
||||
|
{ |
||||
|
if (file_data.cFileName[0] != '.') |
||||
|
{ |
||||
|
// Calculate string lengths
|
||||
|
if (directory_length == 0) |
||||
|
directory_length = strlen(directory) - 1; |
||||
|
tmp_length = strlen(file_data.cFileName); |
||||
|
|
||||
|
tmp = (char *)malloc(sizeof(char) * (directory_length + tmp_length + 3)); |
||||
|
|
||||
|
memcpy(tmp, directory, directory_length); |
||||
|
memcpy(tmp + directory_length, file_data.cFileName, tmp_length); |
||||
|
|
||||
|
// Append wildcard and NULL terminator
|
||||
|
tmp_length += directory_length; |
||||
|
tmp[tmp_length] = '\\'; |
||||
|
tmp[tmp_length + 1] = '*'; |
||||
|
tmp[tmp_length + 2] = '\0'; |
||||
|
|
||||
|
build_package_table(tmp); |
||||
|
free(tmp); |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
tmp = (char *) str_find_suffix(file_data.cFileName, ".upk"); |
||||
|
if (tmp != NULL) |
||||
|
extension = ext_UPK; |
||||
|
else |
||||
|
{ |
||||
|
tmp = (char *) str_find_suffix(file_data.cFileName, ".udk"); |
||||
|
if (tmp != NULL) |
||||
|
extension = ext_UDK; |
||||
|
else |
||||
|
{ |
||||
|
tmp = (char *) str_find_suffix(file_data.cFileName, ".u"); |
||||
|
if (tmp != NULL) |
||||
|
extension = ext_U; |
||||
|
else |
||||
|
extension = ext_UNKNOWN; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (tmp != NULL) |
||||
|
{ |
||||
|
// check if package name matches a package in the table
|
||||
|
for (tmp_index = 0; tmp_index != packages_imported; ++tmp_index) |
||||
|
{ |
||||
|
if (streql_2ptr(file_data.cFileName, tmp, name_table[package_table[tmp_index].name_index])) |
||||
|
{ |
||||
|
// Calculate string lengths
|
||||
|
if (directory_length == 0) |
||||
|
directory_length = strlen(directory) - 1; |
||||
|
tmp_length = strlen(file_data.cFileName); |
||||
|
|
||||
|
package_table[tmp_index].extension = extension; |
||||
|
|
||||
|
package_table[tmp_index].filename = (char *) malloc(sizeof(char) * (directory_length + tmp_length + 1)); |
||||
|
|
||||
|
memcpy(package_table[tmp_index].filename, directory, directory_length); |
||||
|
memcpy(package_table[tmp_index].filename + directory_length, file_data.cFileName, tmp_length); |
||||
|
package_table[tmp_index].filename[directory_length + tmp_length] = '\0'; |
||||
|
|
||||
|
tmp_file = fopen(package_table[tmp_index].filename, "rb"); |
||||
|
if (tmp_file != NULL) |
||||
|
{ |
||||
|
read_guid(package_table[tmp_index].GUID, tmp_file); |
||||
|
fclose(tmp_file); |
||||
|
} |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
while (FindNextFile(find_handle, &file_data)); |
||||
|
|
||||
|
FindClose(find_handle); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
#endif // _WIN32
|
||||
|
|
||||
|
void print_package_table(FILE *out) |
||||
|
{ |
||||
|
size_t index; |
||||
|
struct UDKPackage *itr = package_table; |
||||
|
|
||||
|
for (index = 0; index != packages_imported; ++index, ++itr) |
||||
|
{ |
||||
|
fprintf(out, "%.8X%.8X%.8X%.8X | ", itr->GUID[0], itr->GUID[1], itr->GUID[2], itr->GUID[3]); |
||||
|
fputs(name_table[itr->name_index], out); |
||||
|
fputc('\n', out); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** Dependency Table Functions */ |
||||
|
|
||||
|
void build_dependency_list() |
||||
|
{ |
||||
|
size_t index; |
||||
|
struct UDKPackage_Dependency *dependency; |
||||
|
|
||||
|
for (index = 0; index != packages_imported; ++index) |
||||
|
if (is_in_against_list(package_table[index].GUID) == false) |
||||
|
{ |
||||
|
dependency = (struct UDKPackage_Dependency *) malloc(sizeof(struct UDKPackage_Dependency)); |
||||
|
dependency->next = NULL; |
||||
|
dependency->package = &package_table[index]; |
||||
|
|
||||
|
if (dependency_list_last != NULL) |
||||
|
dependency_list_last->next = dependency; |
||||
|
else |
||||
|
dependency_list_head = dependency; |
||||
|
|
||||
|
dependency_list_last = dependency; |
||||
|
++dependency_list_size; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
void write_dependency_list(FILE *out) |
||||
|
{ |
||||
|
struct UDKPackage_Dependency *itr; |
||||
|
|
||||
|
fwrite(&dependency_list_size, sizeof(uint32_t), 1, out); |
||||
|
for (itr = dependency_list_head; itr != NULL; itr = itr->next) |
||||
|
fwrite(itr->package->GUID, sizeof(uint32_t), 4, out); |
||||
|
} |
||||
|
|
||||
|
void print_dependency_list(FILE *out) |
||||
|
{ |
||||
|
struct UDKPackage_Dependency *itr = dependency_list_head; |
||||
|
|
||||
|
fprintf(out, "%u dependencies:\n", dependency_list_size); |
||||
|
while (itr != NULL) |
||||
|
{ |
||||
|
fprintf(out, "%.8X%.8X%.8X%.8X | ", itr->package->GUID[0], itr->package->GUID[1], itr->package->GUID[2], itr->package->GUID[3]); |
||||
|
fputs(name_table[itr->package->name_index], out); |
||||
|
fputs(" | ", out); |
||||
|
fputs(itr->package->filename, out); |
||||
|
fputc('\n', out); |
||||
|
|
||||
|
itr = itr->next; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** Game Package Table Functions */ |
||||
|
|
||||
|
#if defined _WIN32 |
||||
|
|
||||
|
struct UDKPackage_Game *add_UDKPackage_Game(const char *name, const char *name_end) |
||||
|
{ |
||||
|
struct UDKPackage_Game *ret; |
||||
|
|
||||
|
ret = (struct UDKPackage_Game *) malloc(sizeof(struct UDKPackage_Game)); |
||||
|
ret->next = NULL; |
||||
|
ret->name = (char *) malloc(sizeof(char) * (name_end - name + 1)); |
||||
|
|
||||
|
ret->name += name_end - name; |
||||
|
*ret->name = '\0'; |
||||
|
while (name_end != name) |
||||
|
*--ret->name = *--name_end; |
||||
|
|
||||
|
if (game_package_table_last != NULL) |
||||
|
game_package_table_last->next = ret; |
||||
|
else |
||||
|
game_package_table_head = ret; |
||||
|
|
||||
|
game_package_table_last = ret; |
||||
|
++game_package_table_size; |
||||
|
|
||||
|
return ret; |
||||
|
} |
||||
|
|
||||
|
bool build_game_package_table(const char *directory) |
||||
|
{ |
||||
|
WIN32_FIND_DATA file_data; |
||||
|
HANDLE find_handle; |
||||
|
size_t directory_length = 0; |
||||
|
char *tmp; |
||||
|
size_t tmp_length; |
||||
|
FILE *tmp_file; |
||||
|
struct UDKPackage_Game *package; |
||||
|
|
||||
|
find_handle = FindFirstFile(directory, &file_data); |
||||
|
|
||||
|
if (find_handle == INVALID_HANDLE_VALUE) |
||||
|
return false; // Error: Bad handle
|
||||
|
|
||||
|
do |
||||
|
{ |
||||
|
if (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) |
||||
|
{ |
||||
|
if (file_data.cFileName[0] != '.') |
||||
|
{ |
||||
|
// Calculate string lengths
|
||||
|
if (directory_length == 0) |
||||
|
directory_length = strlen(directory) - 1; |
||||
|
tmp_length = strlen(file_data.cFileName); |
||||
|
|
||||
|
tmp = (char *)malloc(sizeof(char) * (directory_length + tmp_length + 3)); |
||||
|
|
||||
|
memcpy(tmp, directory, directory_length); |
||||
|
memcpy(tmp + directory_length, file_data.cFileName, tmp_length); |
||||
|
|
||||
|
// Append wildcard and NULL terminator
|
||||
|
tmp_length += directory_length; |
||||
|
tmp[tmp_length] = '\\'; |
||||
|
tmp[tmp_length + 1] = '*'; |
||||
|
tmp[tmp_length + 2] = '\0'; |
||||
|
|
||||
|
build_game_package_table(tmp); |
||||
|
free(tmp); |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
tmp = (char *)str_find_suffix(file_data.cFileName, ".upk"); |
||||
|
if (tmp == NULL) |
||||
|
{ |
||||
|
tmp = (char *)str_find_suffix(file_data.cFileName, ".udk"); |
||||
|
if (tmp == NULL) |
||||
|
tmp = (char *)str_find_suffix(file_data.cFileName, ".u"); |
||||
|
} |
||||
|
|
||||
|
if (tmp != NULL) |
||||
|
{ |
||||
|
package = add_UDKPackage_Game(file_data.cFileName, tmp); |
||||
|
|
||||
|
// Calculate string lengths
|
||||
|
if (directory_length == 0) |
||||
|
directory_length = strlen(directory) - 1; |
||||
|
tmp_length = strlen(file_data.cFileName); |
||||
|
|
||||
|
tmp = (char *)malloc(sizeof(char) * (directory_length + tmp_length + 1)); |
||||
|
|
||||
|
memcpy(tmp, directory, directory_length); |
||||
|
memcpy(tmp + directory_length, file_data.cFileName, tmp_length); |
||||
|
tmp[directory_length + tmp_length] = '\0'; |
||||
|
|
||||
|
tmp_file = fopen(tmp, "rb"); |
||||
|
if (tmp_file != NULL) |
||||
|
{ |
||||
|
read_guid(package->GUID, tmp_file); |
||||
|
fclose(tmp_file); |
||||
|
} |
||||
|
|
||||
|
free(tmp); |
||||
|
} |
||||
|
} |
||||
|
} while (FindNextFile(find_handle, &file_data)); |
||||
|
|
||||
|
FindClose(find_handle); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
#endif // _WIN32
|
||||
|
|
||||
|
void print_game_package_table(FILE *out) |
||||
|
{ |
||||
|
struct UDKPackage_Game *itr = game_package_table_head; |
||||
|
|
||||
|
while (itr != NULL) |
||||
|
{ |
||||
|
fprintf(out, "%.8X%.8X%.8X%.8X | ", itr->GUID[0], itr->GUID[1], itr->GUID[2], itr->GUID[3]); |
||||
|
fputs(itr->name, out); |
||||
|
fputc('\n', out); |
||||
|
itr = itr->next; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** Packager */ |
||||
|
|
||||
|
void generate_package(const char *game_path) |
||||
|
{ |
||||
|
struct UDKPackage_Dependency *itr = dependency_list_head; |
||||
|
char tmp[1024]; // 32 (directory) + 1 ('\') + 32 (filename) + 4 (".uxx") + 1 ('\0') = 70
|
||||
|
char tmp2[1024]; |
||||
|
size_t tmp_length = 0; |
||||
|
|
||||
|
tmp_length = sprintf(tmp, "%.8X%.8X%.8X%.8X", package_GUID[0], package_GUID[1], package_GUID[2], package_GUID[3]); |
||||
|
CreateDirectory(tmp, NULL); |
||||
|
|
||||
|
tmp_length += sprintf(tmp + tmp_length, "\\UDKGame"); |
||||
|
CreateDirectory(tmp, NULL); |
||||
|
|
||||
|
// Copy config file
|
||||
|
sprintf(tmp + tmp_length, "\\Config"); |
||||
|
CreateDirectory(tmp, NULL); |
||||
|
sprintf(tmp + tmp_length + 7, "\\%s.ini", name_table[package_name]); |
||||
|
sprintf(tmp2, "%s\\Config\\%s.ini", game_path, name_table[package_name]); |
||||
|
CopyFile(tmp2, tmp, false); |
||||
|
|
||||
|
tmp_length += sprintf(tmp + tmp_length, "\\CookedPC"); |
||||
|
CreateDirectory(tmp, NULL); |
||||
|
|
||||
|
tmp_length += sprintf(tmp + tmp_length, "\\Custom_Content"); |
||||
|
CreateDirectory(tmp, NULL); |
||||
|
|
||||
|
// Copy base package
|
||||
|
sprintf(tmp + tmp_length, "\\%s.%s", name_table[package_name], extension_as_string(package_extension)); |
||||
|
CopyFile(package_filename, tmp, false); |
||||
|
|
||||
|
while (itr != NULL) // Copy dependencies
|
||||
|
{ |
||||
|
sprintf(tmp + tmp_length, "\\%s.%s", name_table[itr->package->name_index], extension_as_string(itr->package->extension)); |
||||
|
CopyFile(itr->package->filename, tmp, false); |
||||
|
itr = itr->next; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** Main (Entry Point) */ |
||||
|
|
||||
|
int main(int argc, const char **args) |
||||
|
{ |
||||
|
const char *game_path = ""; |
||||
|
const char *names_out = NULL; |
||||
|
const char *imports_out = NULL; |
||||
|
const char *dependencies_out = NULL; |
||||
|
const char *against_in = NULL; |
||||
|
const char *packages_out = NULL; |
||||
|
const char *game_packages_out = NULL; |
||||
|
const char *against_out = NULL; |
||||
|
char *search_path = NULL; |
||||
|
bool build_package = false; |
||||
|
FILE *base_package = NULL; |
||||
|
FILE *tmp_file = NULL; |
||||
|
size_t index; |
||||
|
|
||||
|
if (argc < 2 || strcmp(args[1], "-help") == 0 || strcmp(args[1], "/?") == 0) |
||||
|
{ |
||||
|
puts("[-in=\"\"] [-game-path=\"*\"] [-package] [-names=\"\"] [-imports=\"\"] [-dependencies=\"\"] [-against=\"\"] [-packages=\"\"] [-game-packages=\"\"] [-build-against=\"\"]"); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
for (index = 1; index != argc; ++index) |
||||
|
{ |
||||
|
if (strcmp(args[index], "-in") == 0 || strcmp(args[index], "-level") == 0 || strcmp(args[index], "-map") == 0 || strcmp(args[index], "-file") == 0 || strcmp(args[index], "-filename") == 0) |
||||
|
package_filename = args[++index]; |
||||
|
else if (strcmp(args[index], "-game-path") == 0) |
||||
|
game_path = args[++index]; |
||||
|
else if (strcmp(args[index], "-names") == 0) |
||||
|
names_out = args[++index]; |
||||
|
else if (strcmp(args[index], "-imports") == 0) |
||||
|
imports_out = args[++index]; |
||||
|
else if (strcmp(args[index], "-dependencies") == 0) |
||||
|
dependencies_out = args[++index]; |
||||
|
else if (strcmp(args[index], "-against") == 0) |
||||
|
against_in = args[++index]; |
||||
|
else if (strcmp(args[index], "-packages") == 0) |
||||
|
packages_out = args[++index]; |
||||
|
else if (strcmp(args[index], "-game-packages") == 0) |
||||
|
game_packages_out = args[++index]; |
||||
|
else if (strcmp(args[index], "-build-against") == 0) |
||||
|
against_out = args[++index]; |
||||
|
else if (strcmp(args[index], "-package") == 0) |
||||
|
build_package = true; |
||||
|
} |
||||
|
|
||||
|
if (against_in != NULL) |
||||
|
{ |
||||
|
tmp_file = fopen(against_in, "rb"); |
||||
|
if (tmp_file != NULL) |
||||
|
{ |
||||
|
read_against_list(tmp_file); |
||||
|
fclose(tmp_file); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (game_path != NULL) |
||||
|
{ |
||||
|
search_path = (char *) malloc(sizeof(char) * strlen(game_path) + 3); |
||||
|
sprintf(search_path, "%s\\*", game_path); |
||||
|
} |
||||
|
|
||||
|
if (package_filename != NULL) |
||||
|
{ |
||||
|
base_package = fopen(package_filename, "rb"); |
||||
|
if (base_package == NULL) |
||||
|
{ |
||||
|
puts("ERROR: UNABLE TO OPEN FILE."); |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
package_extension = get_extension_from_filename(package_filename, strlen(package_filename)); |
||||
|
|
||||
|
read_guid(package_GUID, base_package); |
||||
|
read_name_table(base_package); |
||||
|
read_import_table(base_package); |
||||
|
|
||||
|
package_name = name_from_filename(package_filename, strlen(package_filename)); |
||||
|
|
||||
|
fclose(base_package); |
||||
|
|
||||
|
init_package_table(); |
||||
|
build_package_table(search_path == NULL ? "*": search_path); |
||||
|
|
||||
|
if (build_package || dependencies_out != NULL) |
||||
|
build_dependency_list(); |
||||
|
|
||||
|
if (build_package) |
||||
|
generate_package(game_path); |
||||
|
} |
||||
|
|
||||
|
if (game_packages_out != NULL || against_out != NULL) |
||||
|
build_game_package_table(search_path == NULL ? "*" : search_path); |
||||
|
|
||||
|
if (against_out != NULL) |
||||
|
build_against_list(); |
||||
|
|
||||
|
/** Write requested data */ |
||||
|
|
||||
|
if (names_out != NULL) |
||||
|
{ |
||||
|
tmp_file = fopen(names_out, "wb"); |
||||
|
if (tmp_file != NULL) |
||||
|
{ |
||||
|
print_name_table(tmp_file); |
||||
|
fclose(tmp_file); |
||||
|
} |
||||
|
else |
||||
|
puts("ERROR: Unable to write name table."); |
||||
|
} |
||||
|
|
||||
|
if (imports_out != NULL) |
||||
|
{ |
||||
|
tmp_file = fopen(imports_out, "wb"); |
||||
|
if (tmp_file != NULL) |
||||
|
{ |
||||
|
print_import_table(tmp_file); |
||||
|
fclose(tmp_file); |
||||
|
|
||||
|
printf("%u import table entries written.\n", import_table_size); |
||||
|
} |
||||
|
else |
||||
|
puts("ERROR: Unable to write import table."); |
||||
|
} |
||||
|
|
||||
|
if (dependencies_out != NULL) |
||||
|
{ |
||||
|
tmp_file = fopen(dependencies_out, "wb"); |
||||
|
if (tmp_file != NULL) |
||||
|
{ |
||||
|
print_dependency_list(tmp_file); |
||||
|
fclose(tmp_file); |
||||
|
} |
||||
|
else |
||||
|
puts("ERROR: Unable to write dependency list."); |
||||
|
} |
||||
|
|
||||
|
if (packages_out != NULL) |
||||
|
{ |
||||
|
tmp_file = fopen(packages_out, "wb"); |
||||
|
if (tmp_file != NULL) |
||||
|
{ |
||||
|
print_package_table(tmp_file); |
||||
|
fclose(tmp_file); |
||||
|
} |
||||
|
else |
||||
|
puts("ERROR: Unable to write package table"); |
||||
|
} |
||||
|
|
||||
|
if (game_packages_out != NULL) |
||||
|
{ |
||||
|
tmp_file = fopen(game_packages_out, "wb"); |
||||
|
if (tmp_file != NULL) |
||||
|
{ |
||||
|
print_game_package_table(tmp_file); |
||||
|
fclose(tmp_file); |
||||
|
} |
||||
|
else |
||||
|
puts("ERROR: Unable to write game package table"); |
||||
|
} |
||||
|
|
||||
|
if (against_out != NULL) |
||||
|
{ |
||||
|
tmp_file = fopen(against_out, "wb"); |
||||
|
if (tmp_file != NULL) |
||||
|
{ |
||||
|
write_against_list(tmp_file); |
||||
|
fclose(tmp_file); |
||||
|
} |
||||
|
else |
||||
|
puts("ERROR: Unable to write against list"); |
||||
|
} |
||||
|
|
||||
|
return 0; |
||||
|
} |
Loading…
Reference in new issue