Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

patterns: Add Unreal Engine 3 UPK pattern #352

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi
| TGA | `image/tga` | [`patterns/tga.hexpat`](patterns/tga.hexpat) | Truevision TGA/TARGA image |
| TTF | `font/ttf`, `font/otf` | [`patterns/ttf.hexpat`](patterns/ttf.hexpat) | TrueType and OpenType font format |
| Ubiquiti | | [`patterns/ubiquiti.hexpat`](patterns/ubiquiti.hexpat) | Ubiquiti Firmware (update) image |
| UPK | | [`patterns/upk-ue3.hexpat`](patterns/upk-ue3.hexpat) | Unreal Engine 3 UPK file |
| UEFI | | [`patterns/uefi.hexpat`](patterns/uefi.hexpat)` | UEFI structs for parsing efivars |
| UEFI Boot Entry | | [`patterns/uefi_boot_entry.hexpat`](patterns/uefi_boot_entry.hexpat) | UEFI Boot Entry (Load option) |
| UF2 | | [`patterns/uf2.hexpat`](patterns/uf2.hexpat) | [USB Flashing Format](https://github.com/microsoft/uf2) |
Expand Down
155 changes: 155 additions & 0 deletions patterns/upk-ue3.hexpat
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
#pragma description Unreal Engine 3 UPK
#pragma endian little

import std.io;

#define HEADER_LOCATION 0x00
#define FILE_SIGNATURE 0x9E2A83C1

#define VER_GIGANTIC 867
#define VER_BATMAN_CITY 805

using offset = u32;
bool isCompressed = false;

// Generations Info
struct FGenerationInfo {
u32 exportcount;
u32 namecount;
u32 netobjectcount;
};

// Compressed Chunk Info
struct CompressedChunkInfo {
offset uncompressedoffset;
u32 uncompressedsize;
offset compressedoffset;
u32 compressedsize;
};

// Name Table Entry
struct FNameEntry {
s32 namelength;
char name[];
u64 nameflags;
};

// Import Table Entry
struct FObjectImport {
u64 packageid;
u64 typeid;
u32 ownerref;
u64 nameid;
};

// Export Table Entry
struct FObjectExport {
u32 typeref;
u32 parentclassref;
u32 ownerref;
u64 nameid;
u32 archetyperef;
u32 objectflagsh;
u32 objectflagsl;
u32 serialsize;
offset serialoffset;
u32 exportflags;
u32 netobjectcount;
u128 guid;
u32 unknown1;
u8 unknown2[netobjectcount * 4];
};

// Main File Header
struct FileHeader {
// File signature
u32 signature;
// Version
u16 version;
// Licensee
u16 licensee;
// Header Size
u32 headersize;

// FolderName
u32 foldername ;

// PackageInfo
char packagegroup[];
u32 packageflags; // todo: properly expand this

// GigUnknown
if (version == VER_GIGANTIC) {
u32 gigunknown1; // this seems to be unique to gig's upk format
}

// Name Table Info
u32 namecount;
offset nameoffset;

// Export Table Info
u32 exportcount;
offset exportoffset;

// Import Table Info
u32 importcount;
offset importoffset;

// Depends Info
offset dependsoffset;

// Unknown
if (version >= VER_BATMAN_CITY) {
offset serialoffset;
u32 ueunknown1; // these are present in other ue3 upks
u32 ueunknown2;
u32 ueunknown3;
}

// GUID
u128 packageguid;

// Generations Info
u32 generationscount;
FGenerationInfo generations[generationscount];

// Engine Info
u32 engineversion;
u32 cookerversion;

// Compression Info
u32 compressionflags;
u32 compressedchunks;
CompressedChunkInfo chunks[compressedchunks];
};

// Print Info
fn print_info(FileHeader header) {
if(header.signature == FILE_SIGNATURE){
std::print("UPK: Yes");
} else {
std::print("UPK: No");
}

std::print("Version: {} ",header.version);
std::print("Licensee: {} ",header.licensee);
std::print("Compressed: {} ",isCompressed);
std::print("Name Count: {} ",header.namecount);
std::print("Import Count: {} ",header.importcount);
std::print("Export Count: {} ",header.exportcount);
};

FileHeader header @ HEADER_LOCATION;

// Check Compression
if (header.compressionflags != 0 || header.compressedchunks > 0) {
isCompressed = true;
print_info(header);
return;
}

FNameEntry NameTable[header.namecount] @ header.nameoffset;
FObjectImport ImportTable[header.importcount] @ header.importoffset;
FObjectExport ExportTable[header.exportcount] @ header.exportoffset;

print_info(header);