[CPU] Move XEX2 code into XexModule class, autodetect XEX key
Code is mainly just copy/pasted from kernel/util/xex2.cc, I've tried fixing it up to work better in a class, but there's probably some things I missed. Also includes some minor improvements to the XEX loader, like being able to try both XEX keys (retail/devkit) automatically, and some fixes to how the base address is determined. (Previously there was code that would get base address from optional header, code that'd get it from xex_security_info, code that'd use a stored base address value... Now everything reads it from a single stored value instead, which is set either from the xex_security_info, or if it exists from the optional header instead. Maybe this can help improve compatibility with any weird XEX's that don't have a base address optional header?) Compressed XEX loader also has some extra checks to make sure the compressed data hash matches what's expected. Might increase loading times by a fraction, but could save reports from people unknowingly using corrupt XEXs. (still no checks for non-compressed data though, maybe need to compare data with xex_security_info->ImageHash?)
This commit is contained in:
parent
2fa7607547
commit
0b7f7e1657
|
@ -69,6 +69,8 @@ template <typename T>
|
|||
inline T byte_swap(T value) {
|
||||
if (sizeof(T) == 4) {
|
||||
return static_cast<T>(byte_swap(static_cast<uint32_t>(value)));
|
||||
} else if (sizeof(T) == 2) {
|
||||
return static_cast<T>(byte_swap(static_cast<uint16_t>(value)));
|
||||
} else {
|
||||
assert_always("not handled");
|
||||
}
|
||||
|
|
|
@ -21,7 +21,13 @@
|
|||
#include "xenia/kernel/kernel_state.h"
|
||||
#include "xenia/kernel/xmodule.h"
|
||||
|
||||
#include "third_party/crypto/TinySHA1.hpp"
|
||||
#include "third_party/crypto/rijndael-alg-fst.c"
|
||||
#include "third_party/crypto/rijndael-alg-fst.h"
|
||||
#include "third_party/mspack/lzx.h"
|
||||
#include "third_party/mspack/lzxd.c"
|
||||
#include "third_party/mspack/mspack.h"
|
||||
#include "third_party/pe/pe_image.h"
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
|
@ -35,9 +41,9 @@ void UndefinedImport(ppc::PPCContext* ppc_context, KernelState* kernel_state) {
|
|||
XexModule::XexModule(Processor* processor, KernelState* kernel_state)
|
||||
: Module(processor), processor_(processor), kernel_state_(kernel_state) {}
|
||||
|
||||
XexModule::~XexModule() { xe_xex2_dealloc(xex_); }
|
||||
XexModule::~XexModule() {}
|
||||
|
||||
bool XexModule::GetOptHeader(const xex2_header* header, xe_xex2_header_keys key,
|
||||
bool XexModule::GetOptHeader(const xex2_header* header, xex2_header_keys key,
|
||||
void** out_ptr) {
|
||||
assert_not_null(header);
|
||||
assert_not_null(out_ptr);
|
||||
|
@ -72,14 +78,103 @@ bool XexModule::GetOptHeader(const xex2_header* header, xe_xex2_header_keys key,
|
|||
return false;
|
||||
}
|
||||
|
||||
bool XexModule::GetOptHeader(xe_xex2_header_keys key, void** out_ptr) const {
|
||||
bool XexModule::GetOptHeader(xex2_header_keys key, void** out_ptr) const {
|
||||
return XexModule::GetOptHeader(xex_header(), key, out_ptr);
|
||||
}
|
||||
|
||||
const xex2_security_info* XexModule::GetSecurityInfo(
|
||||
const xex2_header* header) {
|
||||
return reinterpret_cast<const xex2_security_info*>(uintptr_t(header) +
|
||||
header->security_offset);
|
||||
const PESection* XexModule::GetPESection(const char* name) {
|
||||
for (std::vector<PESection>::iterator it = pe_sections_.begin();
|
||||
it != pe_sections_.end(); ++it) {
|
||||
if (!strcmp(it->name, name)) {
|
||||
return &(*it);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
typedef struct mspack_memory_file_t {
|
||||
struct mspack_system sys;
|
||||
void* buffer;
|
||||
off_t buffer_size;
|
||||
off_t offset;
|
||||
} mspack_memory_file;
|
||||
mspack_memory_file* mspack_memory_open(struct mspack_system* sys, void* buffer,
|
||||
const size_t buffer_size) {
|
||||
assert_true(buffer_size < INT_MAX);
|
||||
if (buffer_size >= INT_MAX) {
|
||||
return NULL;
|
||||
}
|
||||
mspack_memory_file* memfile =
|
||||
(mspack_memory_file*)calloc(1, sizeof(mspack_memory_file));
|
||||
if (!memfile) {
|
||||
return NULL;
|
||||
}
|
||||
memfile->buffer = buffer;
|
||||
memfile->buffer_size = (off_t)buffer_size;
|
||||
memfile->offset = 0;
|
||||
return memfile;
|
||||
}
|
||||
void mspack_memory_close(mspack_memory_file* file) {
|
||||
mspack_memory_file* memfile = (mspack_memory_file*)file;
|
||||
free(memfile);
|
||||
}
|
||||
int mspack_memory_read(struct mspack_file* file, void* buffer, int chars) {
|
||||
mspack_memory_file* memfile = (mspack_memory_file*)file;
|
||||
const off_t remaining = memfile->buffer_size - memfile->offset;
|
||||
const off_t total = std::min(static_cast<off_t>(chars), remaining);
|
||||
memcpy(buffer, (uint8_t*)memfile->buffer + memfile->offset, total);
|
||||
memfile->offset += total;
|
||||
return (int)total;
|
||||
}
|
||||
int mspack_memory_write(struct mspack_file* file, void* buffer, int chars) {
|
||||
mspack_memory_file* memfile = (mspack_memory_file*)file;
|
||||
const off_t remaining = memfile->buffer_size - memfile->offset;
|
||||
const off_t total = std::min(static_cast<off_t>(chars), remaining);
|
||||
memcpy((uint8_t*)memfile->buffer + memfile->offset, buffer, total);
|
||||
memfile->offset += total;
|
||||
return (int)total;
|
||||
}
|
||||
void* mspack_memory_alloc(struct mspack_system* sys, size_t chars) {
|
||||
return calloc(chars, 1);
|
||||
}
|
||||
void mspack_memory_free(void* ptr) { free(ptr); }
|
||||
void mspack_memory_copy(void* src, void* dest, size_t chars) {
|
||||
memcpy(dest, src, chars);
|
||||
}
|
||||
struct mspack_system* mspack_memory_sys_create() {
|
||||
struct mspack_system* sys =
|
||||
(struct mspack_system*)calloc(1, sizeof(struct mspack_system));
|
||||
if (!sys) {
|
||||
return NULL;
|
||||
}
|
||||
sys->read = mspack_memory_read;
|
||||
sys->write = mspack_memory_write;
|
||||
sys->alloc = mspack_memory_alloc;
|
||||
sys->free = mspack_memory_free;
|
||||
sys->copy = mspack_memory_copy;
|
||||
return sys;
|
||||
}
|
||||
void mspack_memory_sys_destroy(struct mspack_system* sys) { free(sys); }
|
||||
|
||||
void XexModule::DecryptBuffer(const uint8_t* session_key,
|
||||
const uint8_t* input_buffer,
|
||||
const size_t input_size, uint8_t* output_buffer,
|
||||
const size_t output_size) {
|
||||
uint32_t rk[4 * (MAXNR + 1)];
|
||||
uint8_t ivec[16] = {0};
|
||||
int32_t Nr = rijndaelKeySetupDec(rk, session_key, 128);
|
||||
const uint8_t* ct = input_buffer;
|
||||
uint8_t* pt = output_buffer;
|
||||
for (size_t n = 0; n < input_size; n += 16, ct += 16, pt += 16) {
|
||||
// Decrypt 16 uint8_ts from input -> output.
|
||||
rijndaelDecrypt(rk, Nr, ct, pt);
|
||||
for (size_t i = 0; i < 16; i++) {
|
||||
// XOR with previous.
|
||||
pt[i] ^= ivec[i];
|
||||
// Set previous.
|
||||
ivec[i] = ct[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t XexModule::GetProcAddress(uint16_t ordinal) const {
|
||||
|
@ -101,21 +196,19 @@ uint32_t XexModule::GetProcAddress(uint16_t ordinal) const {
|
|||
}
|
||||
|
||||
// Second: Check the PE exports.
|
||||
xe::be<uint32_t>* exe_address = nullptr;
|
||||
GetOptHeader(XEX_HEADER_IMAGE_BASE_ADDRESS, &exe_address);
|
||||
assert_not_null(exe_address);
|
||||
assert_not_zero(base_address_);
|
||||
|
||||
xex2_opt_data_directory* pe_export_directory = 0;
|
||||
if (GetOptHeader(XEX_HEADER_EXPORTS_BY_NAME, &pe_export_directory)) {
|
||||
auto e = memory()->TranslateVirtual<const X_IMAGE_EXPORT_DIRECTORY*>(
|
||||
*exe_address + pe_export_directory->offset);
|
||||
base_address_ + pe_export_directory->offset);
|
||||
assert_not_null(e);
|
||||
|
||||
uint32_t* function_table =
|
||||
reinterpret_cast<uint32_t*>(uintptr_t(e) + e->AddressOfFunctions);
|
||||
|
||||
if (ordinal < e->NumberOfFunctions) {
|
||||
return xex_security_info()->load_address + function_table[ordinal];
|
||||
return base_address_ + function_table[ordinal];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -123,18 +216,16 @@ uint32_t XexModule::GetProcAddress(uint16_t ordinal) const {
|
|||
}
|
||||
|
||||
uint32_t XexModule::GetProcAddress(const char* name) const {
|
||||
xe::be<uint32_t>* exe_address = nullptr;
|
||||
GetOptHeader(XEX_HEADER_IMAGE_BASE_ADDRESS, &exe_address);
|
||||
assert_not_null(exe_address);
|
||||
|
||||
xex2_opt_data_directory* pe_export_directory = 0;
|
||||
if (!GetOptHeader(XEX_HEADER_EXPORTS_BY_NAME, &pe_export_directory)) {
|
||||
// No exports by name.
|
||||
return 0;
|
||||
}
|
||||
|
||||
assert_not_zero(base_address_);
|
||||
|
||||
auto e = memory()->TranslateVirtual<const X_IMAGE_EXPORT_DIRECTORY*>(
|
||||
*exe_address + pe_export_directory->offset);
|
||||
base_address_ + pe_export_directory->offset);
|
||||
assert_not_null(e);
|
||||
|
||||
// e->AddressOfX RVAs are relative to the IMAGE_EXPORT_DIRECTORY!
|
||||
|
@ -152,7 +243,7 @@ uint32_t XexModule::GetProcAddress(const char* name) const {
|
|||
for (uint32_t i = 0; i < e->NumberOfNames; i++) {
|
||||
auto fn_name = reinterpret_cast<const char*>(uintptr_t(e) + name_table[i]);
|
||||
uint16_t ordinal = ordinal_table[i];
|
||||
uint32_t addr = *exe_address + function_table[ordinal];
|
||||
uint32_t addr = base_address_ + function_table[ordinal];
|
||||
if (!std::strcmp(name, fn_name)) {
|
||||
// We have a match!
|
||||
return addr;
|
||||
|
@ -183,52 +274,507 @@ bool XexModule::ApplyPatch(XexModule* module) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool XexModule::Load(const std::string& name, const std::string& path,
|
||||
const void* xex_addr, size_t xex_length) {
|
||||
// TODO(DrChat): Move loading code here.
|
||||
xex_ = xe_xex2_load(memory(), xex_addr, xex_length, {0});
|
||||
if (!xex_) {
|
||||
return false;
|
||||
void XexModule::DecryptSessionKey(bool useDevkit) {
|
||||
static const uint8_t xe_xex2_retail_key[16] = {
|
||||
0x20, 0xB1, 0x85, 0xA5, 0x9D, 0x28, 0xFD, 0xC3,
|
||||
0x40, 0x58, 0x3F, 0xBB, 0x08, 0x96, 0xBF, 0x91};
|
||||
static const uint8_t xe_xex2_devkit_key[16] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||||
|
||||
const uint8_t* xexkey = useDevkit ? xe_xex2_devkit_key : xe_xex2_retail_key;
|
||||
|
||||
// Decrypt the header key.
|
||||
uint32_t rk[4 * (MAXNR + 1)];
|
||||
int32_t Nr = rijndaelKeySetupDec(rk, xexkey, 128);
|
||||
rijndaelDecrypt(rk, Nr,
|
||||
reinterpret_cast<const u8*>(xex_security_info()->aes_key),
|
||||
session_key_);
|
||||
}
|
||||
|
||||
int XexModule::ReadImage(const void* xex_addr, size_t xex_length) {
|
||||
if (!opt_file_format_info()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Make a copy of the xex header.
|
||||
auto src_header = reinterpret_cast<const xex2_header*>(xex_addr);
|
||||
xex_header_mem_.resize(src_header->header_size);
|
||||
auto* ff = opt_file_format_info();
|
||||
|
||||
std::memcpy(xex_header_mem_.data(), src_header, src_header->header_size);
|
||||
switch (opt_file_format_info()->compression_type) {
|
||||
case XEX_COMPRESSION_NONE:
|
||||
return ReadImageUncompressed(xex_addr, xex_length);
|
||||
case XEX_COMPRESSION_BASIC:
|
||||
return ReadImageBasicCompressed(xex_addr, xex_length);
|
||||
case XEX_COMPRESSION_NORMAL:
|
||||
case XEX_COMPRESSION_DELTA:
|
||||
return ReadImageCompressed(xex_addr, xex_length);
|
||||
default:
|
||||
assert_always();
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return Load(name, path, xex_);
|
||||
int XexModule::ReadImageUncompressed(const void* xex_addr, size_t xex_length) {
|
||||
// Allocate in-place the XEX memory.
|
||||
const uint32_t exe_length =
|
||||
static_cast<uint32_t>(xex_length - xex_header()->header_size);
|
||||
|
||||
uint32_t uncompressed_size = exe_length;
|
||||
bool alloc_result =
|
||||
memory()
|
||||
->LookupHeap(base_address_)
|
||||
->AllocFixed(
|
||||
base_address_, uncompressed_size, 4096,
|
||||
xe::kMemoryAllocationReserve | xe::kMemoryAllocationCommit,
|
||||
xe::kMemoryProtectRead | xe::kMemoryProtectWrite);
|
||||
if (!alloc_result) {
|
||||
XELOGE("Unable to allocate XEX memory at %.8X-%.8X.", base_address_,
|
||||
uncompressed_size);
|
||||
return 2;
|
||||
}
|
||||
uint8_t* buffer = memory()->TranslateVirtual(base_address_);
|
||||
std::memset(buffer, 0, uncompressed_size);
|
||||
|
||||
const uint8_t* p = (const uint8_t*)xex_addr + xex_header()->header_size;
|
||||
|
||||
switch (opt_file_format_info()->encryption_type) {
|
||||
case XEX_ENCRYPTION_NONE:
|
||||
if (exe_length > uncompressed_size) {
|
||||
return 1;
|
||||
}
|
||||
memcpy(buffer, p, exe_length);
|
||||
return 0;
|
||||
case XEX_ENCRYPTION_NORMAL:
|
||||
DecryptBuffer(session_key_, p, exe_length, buffer, uncompressed_size);
|
||||
return 0;
|
||||
default:
|
||||
assert_always();
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int XexModule::ReadImageBasicCompressed(const void* xex_addr,
|
||||
size_t xex_length) {
|
||||
const uint32_t exe_length =
|
||||
static_cast<uint32_t>(xex_length - xex_header()->header_size);
|
||||
const uint8_t* source_buffer =
|
||||
(const uint8_t*)xex_addr + xex_header()->header_size;
|
||||
const uint8_t* p = source_buffer;
|
||||
|
||||
auto heap = memory()->LookupHeap(base_address_);
|
||||
|
||||
// Calculate uncompressed length.
|
||||
uint32_t uncompressed_size = 0;
|
||||
|
||||
auto* file_info = opt_file_format_info();
|
||||
auto& comp_info = file_info->compression_info.basic;
|
||||
|
||||
uint32_t block_count = (file_info->info_size - 8) / 8;
|
||||
for (uint32_t n = 0; n < block_count; n++) {
|
||||
const uint32_t data_size = comp_info.blocks[n].data_size;
|
||||
const uint32_t zero_size = comp_info.blocks[n].zero_size;
|
||||
uncompressed_size += data_size + zero_size;
|
||||
}
|
||||
|
||||
// Calculate the total size of the XEX image from its headers.
|
||||
uint32_t total_size = 0;
|
||||
for (uint32_t i = 0; i < xex_security_info()->page_descriptor_count; i++) {
|
||||
// Byteswap the bitfield manually.
|
||||
xex2_page_descriptor desc;
|
||||
desc.value = xe::byte_swap(xex_security_info()->page_descriptors[i].value);
|
||||
|
||||
total_size += desc.size * heap->page_size();
|
||||
}
|
||||
|
||||
// Allocate in-place the XEX memory.
|
||||
bool alloc_result = heap->AllocFixed(
|
||||
base_address_, total_size, 4096,
|
||||
xe::kMemoryAllocationReserve | xe::kMemoryAllocationCommit,
|
||||
xe::kMemoryProtectRead | xe::kMemoryProtectWrite);
|
||||
if (!alloc_result) {
|
||||
XELOGE("Unable to allocate XEX memory at %.8X-%.8X.", base_address_,
|
||||
uncompressed_size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
uint8_t* buffer = memory()->TranslateVirtual(base_address_);
|
||||
std::memset(buffer, 0, total_size); // Quickly zero the contents.
|
||||
uint8_t* d = buffer;
|
||||
|
||||
uint32_t rk[4 * (MAXNR + 1)];
|
||||
uint8_t ivec[16] = {0};
|
||||
int32_t Nr = rijndaelKeySetupDec(rk, session_key_, 128);
|
||||
|
||||
for (size_t n = 0; n < block_count; n++) {
|
||||
const uint32_t data_size = comp_info.blocks[n].data_size;
|
||||
const uint32_t zero_size = comp_info.blocks[n].zero_size;
|
||||
|
||||
switch (opt_file_format_info()->encryption_type) {
|
||||
case XEX_ENCRYPTION_NONE:
|
||||
if (data_size > uncompressed_size - (d - buffer)) {
|
||||
// Overflow.
|
||||
return 1;
|
||||
}
|
||||
memcpy(d, p, data_size);
|
||||
break;
|
||||
case XEX_ENCRYPTION_NORMAL: {
|
||||
const uint8_t* ct = p;
|
||||
uint8_t* pt = d;
|
||||
for (size_t m = 0; m < data_size; m += 16, ct += 16, pt += 16) {
|
||||
// Decrypt 16 uint8_ts from input -> output.
|
||||
rijndaelDecrypt(rk, Nr, ct, pt);
|
||||
for (size_t i = 0; i < 16; i++) {
|
||||
// XOR with previous.
|
||||
pt[i] ^= ivec[i];
|
||||
// Set previous.
|
||||
ivec[i] = ct[i];
|
||||
}
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
assert_always();
|
||||
return 1;
|
||||
}
|
||||
|
||||
p += data_size;
|
||||
d += data_size + zero_size;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int XexModule::ReadImageCompressed(const void* xex_addr, size_t xex_length) {
|
||||
const uint32_t exe_length =
|
||||
static_cast<uint32_t>(xex_length - xex_header()->header_size);
|
||||
const uint8_t* exe_buffer =
|
||||
(const uint8_t*)xex_addr + xex_header()->header_size;
|
||||
|
||||
// src -> dest:
|
||||
// - decrypt (if encrypted)
|
||||
// - de-block:
|
||||
// 4b total size of next block in uint8_ts
|
||||
// 20b hash of entire next block (including size/hash)
|
||||
// Nb block uint8_ts
|
||||
// - decompress block contents
|
||||
|
||||
uint8_t* compress_buffer = NULL;
|
||||
const uint8_t* p = NULL;
|
||||
uint8_t* d = NULL;
|
||||
uint8_t* deblock_buffer = NULL;
|
||||
// size_t block_size = 0;
|
||||
uint32_t uncompressed_size = 0;
|
||||
struct mspack_system* sys = NULL;
|
||||
mspack_memory_file* lzxsrc = NULL;
|
||||
mspack_memory_file* lzxdst = NULL;
|
||||
struct lzxd_stream* lzxd = NULL;
|
||||
sha1::SHA1 s;
|
||||
|
||||
// Decrypt (if needed).
|
||||
bool free_input = false;
|
||||
const uint8_t* input_buffer = exe_buffer;
|
||||
size_t input_size = exe_length;
|
||||
|
||||
switch (opt_file_format_info()->encryption_type) {
|
||||
case XEX_ENCRYPTION_NONE:
|
||||
// No-op.
|
||||
break;
|
||||
case XEX_ENCRYPTION_NORMAL:
|
||||
// TODO: a way to do without a copy/alloc?
|
||||
free_input = true;
|
||||
input_buffer = (const uint8_t*)calloc(1, exe_length);
|
||||
DecryptBuffer(session_key_, exe_buffer, exe_length,
|
||||
(uint8_t*)input_buffer, exe_length);
|
||||
break;
|
||||
default:
|
||||
assert_always();
|
||||
return 1;
|
||||
}
|
||||
|
||||
const auto* compression_info = &opt_file_format_info()->compression_info;
|
||||
const xex2_compressed_block_info* cur_block =
|
||||
&compression_info->normal.first_block;
|
||||
|
||||
compress_buffer = (uint8_t*)calloc(1, exe_length);
|
||||
|
||||
p = input_buffer;
|
||||
d = compress_buffer;
|
||||
|
||||
// De-block.
|
||||
deblock_buffer = (uint8_t*)calloc(1, input_size);
|
||||
|
||||
int result_code = 0;
|
||||
|
||||
uint8_t block_calced_digest[0x14];
|
||||
while (cur_block->block_size) {
|
||||
const uint8_t* pnext = p + cur_block->block_size;
|
||||
const auto* next_block = (const xex2_compressed_block_info*)p;
|
||||
|
||||
// Compare block hash, if no match we probably used wrong decrypt key
|
||||
s.reset();
|
||||
s.processBytes(p, cur_block->block_size);
|
||||
s.finalize(block_calced_digest);
|
||||
if (memcmp(block_calced_digest, cur_block->block_hash, 0x14) != 0) {
|
||||
result_code = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
p += 4;
|
||||
p += 20; // skip 20b hash
|
||||
|
||||
while (true) {
|
||||
const size_t chunk_size = (p[0] << 8) | p[1];
|
||||
p += 2;
|
||||
if (!chunk_size) {
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(d, p, chunk_size);
|
||||
p += chunk_size;
|
||||
d += chunk_size;
|
||||
|
||||
uncompressed_size += 0x8000;
|
||||
}
|
||||
|
||||
p = pnext;
|
||||
cur_block = next_block;
|
||||
}
|
||||
|
||||
if (!result_code) {
|
||||
// Allocate in-place the XEX memory.
|
||||
bool alloc_result =
|
||||
memory()
|
||||
->LookupHeap(base_address_)
|
||||
->AllocFixed(
|
||||
base_address_, uncompressed_size, 4096,
|
||||
xe::kMemoryAllocationReserve | xe::kMemoryAllocationCommit,
|
||||
xe::kMemoryProtectRead | xe::kMemoryProtectWrite);
|
||||
|
||||
if (alloc_result) {
|
||||
uint8_t* buffer = memory()->TranslateVirtual(base_address_);
|
||||
|
||||
// Reset buffer if this isn't a patch
|
||||
std::memset(buffer, 0, uncompressed_size);
|
||||
|
||||
// Setup decompressor and decompress.
|
||||
uint32_t window_size = compression_info->normal.window_size;
|
||||
uint32_t window_bits = 0;
|
||||
for (size_t m = 0; m < 32; m++, window_bits++) {
|
||||
window_size >>= 1;
|
||||
if (window_size == 0x00000000) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sys = mspack_memory_sys_create();
|
||||
lzxsrc =
|
||||
mspack_memory_open(sys, (void*)compress_buffer, d - compress_buffer);
|
||||
lzxdst = mspack_memory_open(sys, buffer, uncompressed_size);
|
||||
lzxd = lzxd_init(sys, (struct mspack_file*)lzxsrc,
|
||||
(struct mspack_file*)lzxdst, window_bits, 0, 32768,
|
||||
(off_t)xex_security_info()->image_size);
|
||||
result_code =
|
||||
lzxd_decompress(lzxd, (off_t)xex_security_info()->image_size);
|
||||
} else {
|
||||
XELOGE("Unable to allocate XEX memory at %.8X-%.8X.", base_address_,
|
||||
uncompressed_size);
|
||||
result_code = 3;
|
||||
}
|
||||
}
|
||||
|
||||
if (lzxd) {
|
||||
lzxd_free(lzxd);
|
||||
lzxd = NULL;
|
||||
}
|
||||
if (lzxsrc) {
|
||||
mspack_memory_close(lzxsrc);
|
||||
lzxsrc = NULL;
|
||||
}
|
||||
if (lzxdst) {
|
||||
mspack_memory_close(lzxdst);
|
||||
lzxdst = NULL;
|
||||
}
|
||||
if (sys) {
|
||||
mspack_memory_sys_destroy(sys);
|
||||
sys = NULL;
|
||||
}
|
||||
if (compress_buffer) {
|
||||
free((void*)compress_buffer);
|
||||
}
|
||||
if (deblock_buffer) {
|
||||
free((void*)deblock_buffer);
|
||||
}
|
||||
if (free_input) {
|
||||
free((void*)input_buffer);
|
||||
}
|
||||
return result_code;
|
||||
}
|
||||
|
||||
int XexModule::ReadPEHeaders() {
|
||||
const uint8_t* p = memory()->TranslateVirtual(base_address_);
|
||||
|
||||
// Verify DOS signature (MZ).
|
||||
const IMAGE_DOS_HEADER* doshdr = (const IMAGE_DOS_HEADER*)p;
|
||||
if (doshdr->e_magic != IMAGE_DOS_SIGNATURE) {
|
||||
XELOGE("PE signature mismatch; likely bad decryption/decompression");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Move to the NT header offset from the DOS header.
|
||||
p += doshdr->e_lfanew;
|
||||
|
||||
// Verify NT signature (PE\0\0).
|
||||
const IMAGE_NT_HEADERS32* nthdr = (const IMAGE_NT_HEADERS32*)(p);
|
||||
if (nthdr->Signature != IMAGE_NT_SIGNATURE) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Verify matches an Xbox PE.
|
||||
const IMAGE_FILE_HEADER* filehdr = &nthdr->FileHeader;
|
||||
if ((filehdr->Machine != IMAGE_FILE_MACHINE_POWERPCBE) ||
|
||||
!(filehdr->Characteristics & IMAGE_FILE_32BIT_MACHINE)) {
|
||||
return 1;
|
||||
}
|
||||
// Verify the expected size.
|
||||
if (filehdr->SizeOfOptionalHeader != IMAGE_SIZEOF_NT_OPTIONAL_HEADER) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Verify optional header is 32bit.
|
||||
const IMAGE_OPTIONAL_HEADER32* opthdr = &nthdr->OptionalHeader;
|
||||
if (opthdr->Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
|
||||
return 1;
|
||||
}
|
||||
// Verify subsystem.
|
||||
if (opthdr->Subsystem != IMAGE_SUBSYSTEM_XBOX) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Linker version - likely 8+
|
||||
// Could be useful for recognizing certain patterns
|
||||
// opthdr->MajorLinkerVersion; opthdr->MinorLinkerVersion;
|
||||
|
||||
// Data directories of interest:
|
||||
// EXPORT IMAGE_EXPORT_DIRECTORY
|
||||
// IMPORT IMAGE_IMPORT_DESCRIPTOR[]
|
||||
// EXCEPTION IMAGE_CE_RUNTIME_FUNCTION_ENTRY[]
|
||||
// BASERELOC
|
||||
// DEBUG IMAGE_DEBUG_DIRECTORY[]
|
||||
// ARCHITECTURE /IMAGE_ARCHITECTURE_HEADER/ ----- import thunks!
|
||||
// TLS IMAGE_TLS_DIRECTORY
|
||||
// IAT Import Address Table ptr
|
||||
// opthdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_X].VirtualAddress / .Size
|
||||
|
||||
// The macros in pe_image.h don't work with clang, for some reason.
|
||||
// offsetof seems to be unable to find OptionalHeader.
|
||||
#define offsetof1(type, member) ((std::size_t) & (((type*)0)->member))
|
||||
#define IMAGE_FIRST_SECTION1(ntheader) \
|
||||
((PIMAGE_SECTION_HEADER)( \
|
||||
(uint8_t*)ntheader + offsetof1(IMAGE_NT_HEADERS, OptionalHeader) + \
|
||||
((PIMAGE_NT_HEADERS)(ntheader))->FileHeader.SizeOfOptionalHeader))
|
||||
|
||||
// Quick scan to determine bounds of sections.
|
||||
size_t upper_address = 0;
|
||||
const IMAGE_SECTION_HEADER* sechdr = IMAGE_FIRST_SECTION1(nthdr);
|
||||
for (size_t n = 0; n < filehdr->NumberOfSections; n++, sechdr++) {
|
||||
const size_t physical_address = opthdr->ImageBase + sechdr->VirtualAddress;
|
||||
upper_address =
|
||||
std::max(upper_address, physical_address + sechdr->Misc.VirtualSize);
|
||||
}
|
||||
|
||||
// Setup/load sections.
|
||||
sechdr = IMAGE_FIRST_SECTION1(nthdr);
|
||||
for (size_t n = 0; n < filehdr->NumberOfSections; n++, sechdr++) {
|
||||
PESection section;
|
||||
memcpy(section.name, sechdr->Name, sizeof(sechdr->Name));
|
||||
section.name[8] = 0;
|
||||
section.raw_address = sechdr->PointerToRawData;
|
||||
section.raw_size = sechdr->SizeOfRawData;
|
||||
section.address = base_address_ + sechdr->VirtualAddress;
|
||||
section.size = sechdr->Misc.VirtualSize;
|
||||
section.flags = sechdr->Characteristics;
|
||||
pe_sections_.push_back(section);
|
||||
// pe_sections_.push_back(section);
|
||||
}
|
||||
|
||||
// DumpTLSDirectory(pImageBase, pNTHeader, (PIMAGE_TLS_DIRECTORY32)0);
|
||||
// DumpExportsSection(pImageBase, pNTHeader);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool XexModule::Load(const std::string& name, const std::string& path,
|
||||
xe_xex2_ref xex) {
|
||||
const void* xex_addr, size_t xex_length) {
|
||||
auto src_header = reinterpret_cast<const xex2_header*>(xex_addr);
|
||||
|
||||
if (src_header->magic != 'XEX2') {
|
||||
return false;
|
||||
}
|
||||
|
||||
assert_false(loaded_);
|
||||
loaded_ = true;
|
||||
xex_ = xex;
|
||||
|
||||
auto old_header = xe_xex2_get_header(xex_);
|
||||
// Read in XEX headers
|
||||
xex_header_mem_.resize(src_header->header_size);
|
||||
std::memcpy(xex_header_mem_.data(), src_header, src_header->header_size);
|
||||
|
||||
auto sec_header = xex_security_info();
|
||||
|
||||
// Try setting our base_address based on XEX_HEADER_IMAGE_BASE_ADDRESS, fall
|
||||
// back to xex_security_info otherwise
|
||||
base_address_ = sec_header->load_address;
|
||||
xe::be<uint32_t>* base_addr_opt = nullptr;
|
||||
if (GetOptHeader(XEX_HEADER_IMAGE_BASE_ADDRESS, &base_addr_opt))
|
||||
base_address_ = *base_addr_opt;
|
||||
|
||||
// Setup debug info.
|
||||
name_ = std::string(name);
|
||||
path_ = std::string(path);
|
||||
// TODO(benvanik): debug info
|
||||
|
||||
uint8_t* data = memory()->TranslateVirtual(base_address_);
|
||||
|
||||
// Load in the XEX basefile
|
||||
// We'll try using both XEX2 keys to see if any give a valid PE
|
||||
while (true) {
|
||||
memory()->LookupHeap(base_address_)->Reset();
|
||||
|
||||
DecryptSessionKey(is_dev_kit_);
|
||||
|
||||
if (!ReadImage(xex_addr, xex_length) && !ReadPEHeaders()) {
|
||||
break;
|
||||
}
|
||||
|
||||
is_dev_kit_ = !is_dev_kit_;
|
||||
|
||||
// is_dev_kit starts as false, then flips to true if load failed, if it's
|
||||
// back to false again this must be invalid
|
||||
if (!is_dev_kit_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
XELOGW("XEX load failed, trying with devkit encryption key...");
|
||||
}
|
||||
|
||||
// Scan and find the low/high addresses.
|
||||
// All code sections are continuous, so this should be easy.
|
||||
// TODO(DrChat): Use the new xex header to do this.
|
||||
auto heap = memory()->LookupHeap(base_address_);
|
||||
auto page_size = heap->page_size();
|
||||
|
||||
low_address_ = UINT_MAX;
|
||||
high_address_ = 0;
|
||||
for (uint32_t n = 0, i = 0; n < old_header->section_count; n++) {
|
||||
const xe_xex2_section_t* section = &old_header->sections[n];
|
||||
const uint32_t start_address =
|
||||
old_header->exe_address + (i * section->page_size);
|
||||
const uint32_t end_address =
|
||||
start_address + (section->info.page_count * section->page_size);
|
||||
if (section->info.type == XEX_SECTION_CODE) {
|
||||
|
||||
for (uint32_t i = 0, page = 0; i < sec_header->page_descriptor_count; i++) {
|
||||
// Byteswap the bitfield manually.
|
||||
xex2_page_descriptor desc;
|
||||
desc.value = xe::byte_swap(sec_header->page_descriptors[i].value);
|
||||
|
||||
const auto start_address = base_address_ + (page * page_size);
|
||||
const auto end_address = start_address + (desc.size * page_size);
|
||||
if (desc.info == XEX_SECTION_CODE) {
|
||||
low_address_ = std::min(low_address_, start_address);
|
||||
high_address_ = std::max(high_address_, end_address);
|
||||
}
|
||||
i += section->info.page_count;
|
||||
|
||||
page += desc.size;
|
||||
}
|
||||
|
||||
// Notify backend that we have an executable range.
|
||||
|
@ -288,15 +834,12 @@ bool XexModule::Load(const std::string& name, const std::string& path,
|
|||
}
|
||||
|
||||
// Setup memory protection.
|
||||
auto sec_header = xex_security_info();
|
||||
auto heap = memory()->LookupHeap(sec_header->load_address);
|
||||
auto page_size = heap->page_size();
|
||||
for (uint32_t i = 0, page = 0; i < sec_header->page_descriptor_count; i++) {
|
||||
// Byteswap the bitfield manually.
|
||||
xex2_page_descriptor desc;
|
||||
desc.value = xe::byte_swap(sec_header->page_descriptors[i].value);
|
||||
|
||||
auto address = sec_header->load_address + (page * page_size);
|
||||
auto address = base_address_ + (page * page_size);
|
||||
auto size = desc.size * page_size;
|
||||
switch (desc.info) {
|
||||
case XEX_SECTION_CODE:
|
||||
|
@ -321,11 +864,9 @@ bool XexModule::Unload() {
|
|||
loaded_ = false;
|
||||
|
||||
// Just deallocate the memory occupied by the exe
|
||||
xe::be<uint32_t>* exe_address = 0;
|
||||
GetOptHeader(XEX_HEADER_IMAGE_BASE_ADDRESS, &exe_address);
|
||||
assert_not_zero(exe_address);
|
||||
assert_not_zero(base_address_);
|
||||
|
||||
memory()->LookupHeap(*exe_address)->Release(*exe_address);
|
||||
memory()->LookupHeap(base_address_)->Release(base_address_);
|
||||
xex_header_mem_.resize(0);
|
||||
|
||||
return true;
|
||||
|
@ -346,6 +887,12 @@ bool XexModule::SetupLibraryImports(const char* name,
|
|||
libbasename = libbasename.substr(0, dot);
|
||||
}
|
||||
|
||||
ImportLibrary library_info;
|
||||
library_info.Name = libbasename;
|
||||
library_info.ID = library->id;
|
||||
library_info.Version.value = library->version.value;
|
||||
library_info.MinVersion.value = library->version_min.value;
|
||||
|
||||
// Imports are stored as {import descriptor, thunk addr, import desc, ...}
|
||||
// Even thunks have an import descriptor (albeit unused/useless)
|
||||
for (uint32_t i = 0; i < library->count; i++) {
|
||||
|
@ -379,6 +926,12 @@ bool XexModule::SetupLibraryImports(const char* name,
|
|||
StringBuffer import_name;
|
||||
if (record_type == 0) {
|
||||
// Variable.
|
||||
|
||||
ImportLibraryFn import_info;
|
||||
import_info.Ordinal = ordinal;
|
||||
import_info.ValueAddress = record_addr;
|
||||
library_info.Imports.push_back(import_info);
|
||||
|
||||
import_name.AppendFormat("__imp__");
|
||||
if (kernel_export) {
|
||||
import_name.AppendFormat("%s", kernel_export->name);
|
||||
|
@ -418,6 +971,11 @@ bool XexModule::SetupLibraryImports(const char* name,
|
|||
var_info->set_status(Symbol::Status::kDefined);
|
||||
} else if (record_type == 1) {
|
||||
// Thunk.
|
||||
assert_true(library_info.Imports.size() > 0);
|
||||
auto& prev_import = library_info.Imports[library_info.Imports.size() - 1];
|
||||
assert_true(prev_import.Ordinal == ordinal);
|
||||
prev_import.ThunkAddress = record_addr;
|
||||
|
||||
if (kernel_export) {
|
||||
import_name.AppendFormat("%s", kernel_export->name);
|
||||
} else {
|
||||
|
@ -488,6 +1046,8 @@ bool XexModule::SetupLibraryImports(const char* name,
|
|||
}
|
||||
}
|
||||
|
||||
import_libs_.push_back(library_info);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -669,14 +1229,15 @@ bool XexModule::FindSaveRest() {
|
|||
uint32_t gplr_start = 0;
|
||||
uint32_t fpr_start = 0;
|
||||
uint32_t vmx_start = 0;
|
||||
const xe_xex2_header_t* header = xe_xex2_get_header(xex_);
|
||||
for (uint32_t n = 0, i = 0; n < header->section_count; n++) {
|
||||
const xe_xex2_section_t* section = &header->sections[n];
|
||||
const uint32_t start_address =
|
||||
header->exe_address + (i * section->page_size);
|
||||
const uint32_t end_address =
|
||||
start_address + (section->info.page_count * section->page_size);
|
||||
if (section->info.type == XEX_SECTION_CODE) {
|
||||
|
||||
auto page_size = base_address_ <= 0x90000000 ? 64 * 1024 : 4 * 1024;
|
||||
auto sec_header = xex_security_info();
|
||||
for (uint32_t i = 0, page = 0; i < sec_header->page_descriptor_count; i++) {
|
||||
const xex2_page_descriptor* section = &sec_header->page_descriptors[i];
|
||||
const auto start_address = base_address_ + (page * page_size);
|
||||
const auto end_address = start_address + (section->size * page_size);
|
||||
|
||||
if (section->info == XEX_SECTION_CODE) {
|
||||
if (!gplr_start) {
|
||||
gplr_start = memory_->SearchAligned(start_address, end_address,
|
||||
gprlr_code_values,
|
||||
|
@ -696,7 +1257,8 @@ bool XexModule::FindSaveRest() {
|
|||
break;
|
||||
}
|
||||
}
|
||||
i += section->info.page_count;
|
||||
|
||||
page += section->size;
|
||||
}
|
||||
|
||||
// Add function stubs.
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
#include <vector>
|
||||
|
||||
#include "xenia/cpu/module.h"
|
||||
#include "xenia/kernel/util/xex2.h"
|
||||
#include "xenia/kernel/util/xex2_info.h"
|
||||
|
||||
namespace xe {
|
||||
|
@ -30,40 +29,73 @@ class Runtime;
|
|||
|
||||
class XexModule : public xe::cpu::Module {
|
||||
public:
|
||||
struct ImportLibraryFn {
|
||||
public:
|
||||
uint32_t Ordinal;
|
||||
uint32_t ValueAddress;
|
||||
uint32_t ThunkAddress;
|
||||
};
|
||||
struct ImportLibrary {
|
||||
public:
|
||||
std::string Name;
|
||||
uint32_t ID;
|
||||
xe_xex2_version_t Version;
|
||||
xe_xex2_version_t MinVersion;
|
||||
std::vector<ImportLibraryFn> Imports;
|
||||
};
|
||||
|
||||
XexModule(Processor* processor, kernel::KernelState* kernel_state);
|
||||
virtual ~XexModule();
|
||||
|
||||
xe_xex2_ref xex() const { return xex_; }
|
||||
bool loaded() const { return loaded_; }
|
||||
const xex2_header* xex_header() const {
|
||||
return reinterpret_cast<const xex2_header*>(xex_header_mem_.data());
|
||||
}
|
||||
const xex2_security_info* xex_security_info() const {
|
||||
return GetSecurityInfo(xex_header());
|
||||
return reinterpret_cast<const xex2_security_info*>(
|
||||
uintptr_t(xex_header()) + xex_header()->security_offset);
|
||||
}
|
||||
|
||||
const std::vector<ImportLibrary>* import_libraries() const {
|
||||
return &import_libs_;
|
||||
}
|
||||
|
||||
const xex2_opt_execution_info* opt_execution_info() const {
|
||||
xex2_opt_execution_info* retval = nullptr;
|
||||
GetOptHeader(XEX_HEADER_EXECUTION_INFO, &retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
const xex2_opt_file_format_info* opt_file_format_info() const {
|
||||
xex2_opt_file_format_info* retval = nullptr;
|
||||
GetOptHeader(XEX_HEADER_FILE_FORMAT_INFO, &retval);
|
||||
return retval;
|
||||
}
|
||||
|
||||
const uint32_t base_address() const { return base_address_; }
|
||||
|
||||
// Gets an optional header. Returns NULL if not found.
|
||||
// Special case: if key & 0xFF == 0x00, this function will return the value,
|
||||
// not a pointer! This assumes out_ptr points to uint32_t.
|
||||
static bool GetOptHeader(const xex2_header* header, xe_xex2_header_keys key,
|
||||
static bool GetOptHeader(const xex2_header* header, xex2_header_keys key,
|
||||
void** out_ptr);
|
||||
bool GetOptHeader(xe_xex2_header_keys key, void** out_ptr) const;
|
||||
bool GetOptHeader(xex2_header_keys key, void** out_ptr) const;
|
||||
|
||||
// Ultra-cool templated version
|
||||
// Special case: if key & 0xFF == 0x00, this function will return the value,
|
||||
// not a pointer! This assumes out_ptr points to uint32_t.
|
||||
template <typename T>
|
||||
static bool GetOptHeader(const xex2_header* header, xe_xex2_header_keys key,
|
||||
static bool GetOptHeader(const xex2_header* header, xex2_header_keys key,
|
||||
T* out_ptr) {
|
||||
return GetOptHeader(header, key, reinterpret_cast<void**>(out_ptr));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool GetOptHeader(xe_xex2_header_keys key, T* out_ptr) const {
|
||||
bool GetOptHeader(xex2_header_keys key, T* out_ptr) const {
|
||||
return GetOptHeader(key, reinterpret_cast<void**>(out_ptr));
|
||||
}
|
||||
|
||||
static const xex2_security_info* GetSecurityInfo(const xex2_header* header);
|
||||
const PESection* GetPESection(const char* name);
|
||||
|
||||
uint32_t GetProcAddress(uint16_t ordinal) const;
|
||||
uint32_t GetProcAddress(const char* name) const;
|
||||
|
@ -71,7 +103,6 @@ class XexModule : public xe::cpu::Module {
|
|||
bool ApplyPatch(XexModule* module);
|
||||
bool Load(const std::string& name, const std::string& path,
|
||||
const void* xex_addr, size_t xex_length);
|
||||
bool Load(const std::string& name, const std::string& path, xe_xex2_ref xex);
|
||||
bool Unload();
|
||||
|
||||
const std::string& name() const override { return name_; }
|
||||
|
@ -81,10 +112,31 @@ class XexModule : public xe::cpu::Module {
|
|||
|
||||
bool ContainsAddress(uint32_t address) override;
|
||||
|
||||
static void DecryptBuffer(const uint8_t* session_key,
|
||||
const uint8_t* input_buffer,
|
||||
const size_t input_size, uint8_t* output_buffer,
|
||||
const size_t output_size);
|
||||
|
||||
uint8_t* HostData() {
|
||||
if (base_address_)
|
||||
return memory()->TranslateVirtual(base_address_);
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
std::unique_ptr<Function> CreateFunction(uint32_t address) override;
|
||||
|
||||
private:
|
||||
void DecryptSessionKey(bool useDevkit = false);
|
||||
|
||||
int ReadImage(const void* xex_addr, size_t xex_length);
|
||||
int ReadImageUncompressed(const void* xex_addr, size_t xex_length);
|
||||
int ReadImageBasicCompressed(const void* xex_addr, size_t xex_length);
|
||||
int ReadImageCompressed(const void* xex_addr, size_t xex_length);
|
||||
|
||||
int ReadPEHeaders();
|
||||
|
||||
bool SetupLibraryImports(const char* name,
|
||||
const xex2_import_library* library);
|
||||
bool FindSaveRest();
|
||||
|
@ -93,13 +145,23 @@ class XexModule : public xe::cpu::Module {
|
|||
kernel::KernelState* kernel_state_ = nullptr;
|
||||
std::string name_;
|
||||
std::string path_;
|
||||
xe_xex2_ref xex_ = nullptr;
|
||||
std::vector<uint8_t> xex_header_mem_; // Holds the xex header
|
||||
bool loaded_ = false; // Loaded into memory?
|
||||
|
||||
// various optional headers
|
||||
std::vector<ImportLibrary>
|
||||
import_libs_; // pre-loaded import libraries for ease of use
|
||||
|
||||
std::vector<PESection> pe_sections_;
|
||||
|
||||
uint8_t session_key_[0x10];
|
||||
|
||||
bool loaded_ = false; // Loaded into memory?
|
||||
|
||||
uint32_t base_address_ = 0;
|
||||
uint32_t low_address_ = 0;
|
||||
uint32_t high_address_ = 0;
|
||||
|
||||
bool is_dev_kit_ = false;
|
||||
};
|
||||
|
||||
} // namespace cpu
|
||||
|
|
|
@ -138,6 +138,12 @@ X_STATUS UserModule::LoadFromMemory(const void* addr, const size_t length) {
|
|||
uint8_t* xex_header_ptr = memory()->TranslateVirtual(guest_xex_header_);
|
||||
std::memcpy(xex_header_ptr, header, header->header_size);
|
||||
|
||||
// Cache some commonly used headers...
|
||||
this->xex_module()->GetOptHeader(XEX_HEADER_ENTRY_POINT, &entry_point_);
|
||||
this->xex_module()->GetOptHeader(XEX_HEADER_DEFAULT_STACK_SIZE,
|
||||
&stack_size_);
|
||||
is_dll_module_ = !!(header->module_flags & XEX_MODULE_DLL_MODULE);
|
||||
|
||||
// Setup the loader data entry
|
||||
auto ldr_data =
|
||||
memory()->TranslateVirtual<X_LDR_DATA_TABLE_ENTRY*>(hmodule_ptr_);
|
||||
|
@ -145,20 +151,8 @@ X_STATUS UserModule::LoadFromMemory(const void* addr, const size_t length) {
|
|||
ldr_data->dll_base = 0; // GetProcAddress will read this.
|
||||
ldr_data->xex_header_base = guest_xex_header_;
|
||||
ldr_data->full_image_size = security_header->image_size;
|
||||
this->xex_module()->GetOptHeader(XEX_HEADER_ENTRY_POINT,
|
||||
&ldr_data->entry_point);
|
||||
|
||||
xe::be<uint32_t>* image_base_ptr = nullptr;
|
||||
if (this->xex_module()->GetOptHeader(XEX_HEADER_IMAGE_BASE_ADDRESS,
|
||||
&image_base_ptr)) {
|
||||
ldr_data->image_base = *image_base_ptr;
|
||||
}
|
||||
|
||||
// Cache some commonly used headers...
|
||||
this->xex_module()->GetOptHeader(XEX_HEADER_ENTRY_POINT, &entry_point_);
|
||||
this->xex_module()->GetOptHeader(XEX_HEADER_DEFAULT_STACK_SIZE,
|
||||
&stack_size_);
|
||||
is_dll_module_ = !!(header->module_flags & XEX_MODULE_DLL_MODULE);
|
||||
ldr_data->image_base = this->xex_module()->base_address();
|
||||
ldr_data->entry_point = entry_point_;
|
||||
} else if (module_format_ == kModuleFormatElf) {
|
||||
auto elf_module =
|
||||
std::make_unique<cpu::ElfModule>(processor, kernel_state());
|
||||
|
@ -229,7 +223,7 @@ X_STATUS UserModule::GetSection(const char* name, uint32_t* out_section_data,
|
|||
return X_STATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
X_STATUS UserModule::GetOptHeader(xe_xex2_header_keys key, void** out_ptr) {
|
||||
X_STATUS UserModule::GetOptHeader(xex2_header_keys key, void** out_ptr) {
|
||||
assert_not_null(out_ptr);
|
||||
|
||||
if (module_format_ == kModuleFormatElf) {
|
||||
|
@ -245,7 +239,7 @@ X_STATUS UserModule::GetOptHeader(xe_xex2_header_keys key, void** out_ptr) {
|
|||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
X_STATUS UserModule::GetOptHeader(xe_xex2_header_keys key,
|
||||
X_STATUS UserModule::GetOptHeader(xex2_header_keys key,
|
||||
uint32_t* out_header_guest_ptr) {
|
||||
if (module_format_ == kModuleFormatElf) {
|
||||
// Quick die.
|
||||
|
@ -262,7 +256,7 @@ X_STATUS UserModule::GetOptHeader(xe_xex2_header_keys key,
|
|||
}
|
||||
|
||||
X_STATUS UserModule::GetOptHeader(uint8_t* membase, const xex2_header* header,
|
||||
xe_xex2_header_keys key,
|
||||
xex2_header_keys key,
|
||||
uint32_t* out_header_guest_ptr) {
|
||||
assert_not_null(out_header_guest_ptr);
|
||||
uint32_t field_value = 0;
|
||||
|
@ -575,7 +569,7 @@ void UserModule::Dump() {
|
|||
auto dir =
|
||||
reinterpret_cast<const xex2_opt_data_directory*>(opt_header_ptr);
|
||||
|
||||
auto exe_address = xex_module()->xex_security_info()->load_address;
|
||||
auto exe_address = xex_module()->base_address();
|
||||
auto e = memory()->TranslateVirtual<const X_IMAGE_EXPORT_DIRECTORY*>(
|
||||
exe_address + dir->offset);
|
||||
auto e_base = reinterpret_cast<uintptr_t>(e);
|
||||
|
@ -624,8 +618,8 @@ void UserModule::Dump() {
|
|||
}
|
||||
|
||||
const uint32_t page_size =
|
||||
security_info->load_address < 0x90000000 ? 64 * 1024 : 4 * 1024;
|
||||
uint32_t start_address = security_info->load_address + (page * page_size);
|
||||
xex_module()->base_address() < 0x90000000 ? 64 * 1024 : 4 * 1024;
|
||||
uint32_t start_address = xex_module()->base_address() + (page * page_size);
|
||||
uint32_t end_address = start_address + (page_descriptor.size * page_size);
|
||||
|
||||
sb.AppendFormat(" %3u %s %3u pages %.8X - %.8X (%d bytes)\n", page,
|
||||
|
@ -635,24 +629,22 @@ void UserModule::Dump() {
|
|||
}
|
||||
|
||||
// Print out imports.
|
||||
// TODO(benvanik): figure out a way to remove dependency on old xex header.
|
||||
auto old_header = xe_xex2_get_header(xex_module()->xex());
|
||||
|
||||
auto import_libs = xex_module()->import_libraries();
|
||||
|
||||
sb.AppendFormat("Imports:\n");
|
||||
for (size_t n = 0; n < old_header->import_library_count; n++) {
|
||||
const xe_xex2_import_library_t* library = &old_header->import_libraries[n];
|
||||
|
||||
xe_xex2_import_info_t* import_infos;
|
||||
size_t import_info_count;
|
||||
if (!xe_xex2_get_import_infos(xex_module()->xex(), library, &import_infos,
|
||||
&import_info_count)) {
|
||||
sb.AppendFormat(" %s - %lld imports\n", library->name, import_info_count);
|
||||
sb.AppendFormat(" Version: %d.%d.%d.%d\n", library->version.major,
|
||||
library->version.minor, library->version.build,
|
||||
library->version.qfe);
|
||||
for (std::vector<cpu::XexModule::ImportLibrary>::const_iterator library =
|
||||
import_libs->begin();
|
||||
library != import_libs->end(); ++library) {
|
||||
if (library->Imports.size() > 0) {
|
||||
sb.AppendFormat(" %s - %lld imports\n", library->Name.c_str(),
|
||||
library->Imports.size());
|
||||
sb.AppendFormat(" Version: %d.%d.%d.%d\n", library->Version.major,
|
||||
library->Version.minor, library->Version.build,
|
||||
library->Version.qfe);
|
||||
sb.AppendFormat(" Min Version: %d.%d.%d.%d\n",
|
||||
library->min_version.major, library->min_version.minor,
|
||||
library->min_version.build, library->min_version.qfe);
|
||||
library->MinVersion.major, library->MinVersion.minor,
|
||||
library->MinVersion.build, library->MinVersion.qfe);
|
||||
sb.AppendFormat("\n");
|
||||
|
||||
// Counts.
|
||||
|
@ -660,12 +652,13 @@ void UserModule::Dump() {
|
|||
int unknown_count = 0;
|
||||
int impl_count = 0;
|
||||
int unimpl_count = 0;
|
||||
for (size_t m = 0; m < import_info_count; m++) {
|
||||
const xe_xex2_import_info_t* info = &import_infos[m];
|
||||
|
||||
if (kernel_state_->IsKernelModule(library->name)) {
|
||||
auto kernel_export =
|
||||
export_resolver->GetExportByOrdinal(library->name, info->ordinal);
|
||||
for (std::vector<cpu::XexModule::ImportLibraryFn>::const_iterator info =
|
||||
library->Imports.begin();
|
||||
info != library->Imports.end(); ++info) {
|
||||
if (kernel_state_->IsKernelModule(library->Name.c_str())) {
|
||||
auto kernel_export = export_resolver->GetExportByOrdinal(
|
||||
library->Name.c_str(), info->Ordinal);
|
||||
if (kernel_export) {
|
||||
known_count++;
|
||||
if (kernel_export->is_implemented()) {
|
||||
|
@ -678,10 +671,10 @@ void UserModule::Dump() {
|
|||
unimpl_count++;
|
||||
}
|
||||
} else {
|
||||
auto module = kernel_state_->GetModule(library->name);
|
||||
auto module = kernel_state_->GetModule(library->Name.c_str());
|
||||
if (module) {
|
||||
uint32_t export_addr =
|
||||
module->GetProcAddressByOrdinal(info->ordinal);
|
||||
module->GetProcAddressByOrdinal(info->Ordinal);
|
||||
if (export_addr) {
|
||||
impl_count++;
|
||||
known_count++;
|
||||
|
@ -695,8 +688,8 @@ void UserModule::Dump() {
|
|||
}
|
||||
}
|
||||
}
|
||||
float total_count = static_cast<float>(import_info_count) / 100.0f;
|
||||
sb.AppendFormat(" Total: %4llu\n", import_info_count);
|
||||
float total_count = static_cast<float>(library->Imports.size()) / 100.0f;
|
||||
sb.AppendFormat(" Total: %4llu\n", library->Imports.size());
|
||||
sb.AppendFormat(" Known: %3d%% (%d known, %d unknown)\n",
|
||||
static_cast<int>(known_count / total_count), known_count,
|
||||
unknown_count);
|
||||
|
@ -706,22 +699,23 @@ void UserModule::Dump() {
|
|||
sb.AppendFormat("\n");
|
||||
|
||||
// Listing.
|
||||
for (size_t m = 0; m < import_info_count; m++) {
|
||||
const xe_xex2_import_info_t* info = &import_infos[m];
|
||||
for (std::vector<cpu::XexModule::ImportLibraryFn>::const_iterator info =
|
||||
library->Imports.begin();
|
||||
info != library->Imports.end(); ++info) {
|
||||
const char* name = "UNKNOWN";
|
||||
bool implemented = false;
|
||||
|
||||
cpu::Export* kernel_export = nullptr;
|
||||
if (kernel_state_->IsKernelModule(library->name)) {
|
||||
kernel_export =
|
||||
export_resolver->GetExportByOrdinal(library->name, info->ordinal);
|
||||
if (kernel_state_->IsKernelModule(library->Name.c_str())) {
|
||||
kernel_export = export_resolver->GetExportByOrdinal(
|
||||
library->Name.c_str(), info->Ordinal);
|
||||
if (kernel_export) {
|
||||
name = kernel_export->name;
|
||||
implemented = kernel_export->is_implemented();
|
||||
}
|
||||
} else {
|
||||
auto module = kernel_state_->GetModule(library->name);
|
||||
if (module && module->GetProcAddressByOrdinal(info->ordinal)) {
|
||||
auto module = kernel_state_->GetModule(library->Name.c_str());
|
||||
if (module && module->GetProcAddressByOrdinal(info->Ordinal)) {
|
||||
// TODO(benvanik): name lookup.
|
||||
implemented = true;
|
||||
}
|
||||
|
@ -729,13 +723,12 @@ void UserModule::Dump() {
|
|||
if (kernel_export &&
|
||||
kernel_export->type == cpu::Export::Type::kVariable) {
|
||||
sb.AppendFormat(" V %.8X %.3X (%3d) %s %s\n",
|
||||
info->value_address, info->ordinal, info->ordinal,
|
||||
info->ValueAddress, info->Ordinal, info->Ordinal,
|
||||
implemented ? " " : "!!", name);
|
||||
} else if (info->thunk_address) {
|
||||
} else if (info->ThunkAddress) {
|
||||
sb.AppendFormat(" F %.8X %.8X %.3X (%3d) %s %s\n",
|
||||
info->value_address, info->thunk_address,
|
||||
info->ordinal, info->ordinal,
|
||||
implemented ? " " : "!!", name);
|
||||
info->ValueAddress, info->ThunkAddress, info->Ordinal,
|
||||
info->Ordinal, implemented ? " " : "!!", name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,19 +71,18 @@ class UserModule : public XModule {
|
|||
uint32_t* out_section_size) override;
|
||||
|
||||
// Get optional header - FOR HOST USE ONLY!
|
||||
X_STATUS GetOptHeader(xe_xex2_header_keys key, void** out_ptr);
|
||||
X_STATUS GetOptHeader(xex2_header_keys key, void** out_ptr);
|
||||
|
||||
// Get optional header - FOR HOST USE ONLY!
|
||||
template <typename T>
|
||||
X_STATUS GetOptHeader(xe_xex2_header_keys key, T* out_ptr) {
|
||||
X_STATUS GetOptHeader(xex2_header_keys key, T* out_ptr) {
|
||||
return GetOptHeader(key, reinterpret_cast<void**>(out_ptr));
|
||||
}
|
||||
|
||||
// Get optional header that can safely be returned to guest code.
|
||||
X_STATUS GetOptHeader(xe_xex2_header_keys key,
|
||||
uint32_t* out_header_guest_ptr);
|
||||
X_STATUS GetOptHeader(xex2_header_keys key, uint32_t* out_header_guest_ptr);
|
||||
static X_STATUS GetOptHeader(uint8_t* membase, const xex2_header* header,
|
||||
xe_xex2_header_keys key,
|
||||
xex2_header_keys key,
|
||||
uint32_t* out_header_guest_ptr);
|
||||
|
||||
void Dump();
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,72 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_KERNEL_UTIL_XEX2_H_
|
||||
#define XENIA_KERNEL_UTIL_XEX2_H_
|
||||
|
||||
#include "xenia/kernel/util/xex2_info.h"
|
||||
#include "xenia/memory.h"
|
||||
|
||||
namespace xe {} // namespace xe
|
||||
|
||||
typedef struct {
|
||||
int reserved;
|
||||
} xe_xex2_options_t;
|
||||
|
||||
struct xe_xex2;
|
||||
typedef struct xe_xex2* xe_xex2_ref;
|
||||
|
||||
typedef struct {
|
||||
uint32_t ordinal;
|
||||
uint32_t value_address; // address to place value
|
||||
uint32_t thunk_address; // NULL or address of thunk
|
||||
} xe_xex2_import_info_t;
|
||||
|
||||
enum xe_pe_section_flags_e : uint32_t {
|
||||
kXEPESectionContainsCode = 0x00000020,
|
||||
kXEPESectionContainsDataInit = 0x00000040,
|
||||
kXEPESectionContainsDataUninit = 0x00000080,
|
||||
kXEPESectionMemoryExecute = 0x20000000,
|
||||
kXEPESectionMemoryRead = 0x40000000,
|
||||
kXEPESectionMemoryWrite = 0x80000000,
|
||||
};
|
||||
|
||||
class PESection {
|
||||
public:
|
||||
char name[9]; // 8 + 1 for \0
|
||||
uint32_t raw_address;
|
||||
uint32_t raw_size;
|
||||
uint32_t address;
|
||||
uint32_t size;
|
||||
uint32_t flags; // kXEPESection*
|
||||
};
|
||||
|
||||
struct PEExport {
|
||||
const char* name;
|
||||
uint32_t ordinal;
|
||||
|
||||
uint64_t addr; // Function address
|
||||
};
|
||||
|
||||
xe_xex2_ref xe_xex2_load(xe::Memory* memory, const void* addr,
|
||||
const size_t length, xe_xex2_options_t options);
|
||||
void xe_xex2_dealloc(xe_xex2_ref xex);
|
||||
|
||||
const xe_xex2_header_t* xe_xex2_get_header(xe_xex2_ref xex);
|
||||
const PESection* xe_xex2_get_pe_section(xe_xex2_ref xex, const char* name);
|
||||
|
||||
int xe_xex2_get_import_infos(xe_xex2_ref xex,
|
||||
const xe_xex2_import_library_t* library,
|
||||
xe_xex2_import_info_t** out_import_infos,
|
||||
size_t* out_import_info_count);
|
||||
|
||||
uint32_t xe_xex2_lookup_export(xe_xex2_ref xex, const char* name);
|
||||
uint32_t xe_xex2_lookup_export(xe_xex2_ref xex, uint16_t ordinal);
|
||||
|
||||
#endif // XENIA_KERNEL_UTIL_XEX2_H_
|
|
@ -14,40 +14,103 @@
|
|||
|
||||
#include "xenia/base/byte_order.h"
|
||||
|
||||
enum xe_xex2_header_keys : uint32_t {
|
||||
XEX_HEADER_RESOURCE_INFO = 0x000002FF,
|
||||
XEX_HEADER_FILE_FORMAT_INFO = 0x000003FF,
|
||||
XEX_HEADER_DELTA_PATCH_DESCRIPTOR = 0x000005FF,
|
||||
XEX_HEADER_BASE_REFERENCE = 0x00000405,
|
||||
XEX_HEADER_BOUNDING_PATH = 0x000080FF,
|
||||
XEX_HEADER_DEVICE_ID = 0x00008105,
|
||||
XEX_HEADER_ORIGINAL_BASE_ADDRESS = 0x00010001,
|
||||
XEX_HEADER_ENTRY_POINT = 0x00010100,
|
||||
XEX_HEADER_IMAGE_BASE_ADDRESS = 0x00010201,
|
||||
XEX_HEADER_IMPORT_LIBRARIES = 0x000103FF,
|
||||
XEX_HEADER_CHECKSUM_TIMESTAMP = 0x00018002,
|
||||
XEX_HEADER_ENABLED_FOR_CALLCAP = 0x00018102,
|
||||
XEX_HEADER_ENABLED_FOR_FASTCAP = 0x00018200,
|
||||
XEX_HEADER_ORIGINAL_PE_NAME = 0x000183FF,
|
||||
XEX_HEADER_STATIC_LIBRARIES = 0x000200FF,
|
||||
XEX_HEADER_TLS_INFO = 0x00020104,
|
||||
XEX_HEADER_DEFAULT_STACK_SIZE = 0x00020200,
|
||||
XEX_HEADER_DEFAULT_FILESYSTEM_CACHE_SIZE = 0x00020301,
|
||||
XEX_HEADER_DEFAULT_HEAP_SIZE = 0x00020401,
|
||||
XEX_HEADER_PAGE_HEAP_SIZE_AND_FLAGS = 0x00028002,
|
||||
XEX_HEADER_SYSTEM_FLAGS = 0x00030000,
|
||||
XEX_HEADER_EXECUTION_INFO = 0x00040006,
|
||||
XEX_HEADER_TITLE_WORKSPACE_SIZE = 0x00040201,
|
||||
XEX_HEADER_GAME_RATINGS = 0x00040310,
|
||||
XEX_HEADER_LAN_KEY = 0x00040404,
|
||||
XEX_HEADER_XBOX360_LOGO = 0x000405FF,
|
||||
XEX_HEADER_MULTIDISC_MEDIA_IDS = 0x000406FF,
|
||||
XEX_HEADER_ALTERNATE_TITLE_IDS = 0x000407FF,
|
||||
XEX_HEADER_ADDITIONAL_TITLE_MEMORY = 0x00040801,
|
||||
XEX_HEADER_EXPORTS_BY_NAME = 0x00E10402,
|
||||
union xe_xex2_version_t {
|
||||
uint32_t value;
|
||||
struct {
|
||||
uint32_t major : 4;
|
||||
uint32_t minor : 4;
|
||||
uint32_t build : 16;
|
||||
uint32_t qfe : 8;
|
||||
};
|
||||
};
|
||||
|
||||
enum xe_xex2_module_flags : uint32_t {
|
||||
enum xe_pe_section_flags_e : uint32_t {
|
||||
kXEPESectionContainsCode = 0x00000020,
|
||||
kXEPESectionContainsDataInit = 0x00000040,
|
||||
kXEPESectionContainsDataUninit = 0x00000080,
|
||||
kXEPESectionMemoryExecute = 0x20000000,
|
||||
kXEPESectionMemoryRead = 0x40000000,
|
||||
kXEPESectionMemoryWrite = 0x80000000,
|
||||
};
|
||||
|
||||
class PESection {
|
||||
public:
|
||||
char name[9]; // 8 + 1 for \0
|
||||
uint32_t raw_address;
|
||||
uint32_t raw_size;
|
||||
uint32_t address;
|
||||
uint32_t size;
|
||||
uint32_t flags; // kXEPESection*
|
||||
};
|
||||
|
||||
struct PEExport {
|
||||
const char* name;
|
||||
uint32_t ordinal;
|
||||
|
||||
uint64_t addr; // Function address
|
||||
};
|
||||
|
||||
namespace xe {
|
||||
|
||||
enum xex2_section_type {
|
||||
XEX_SECTION_CODE = 1,
|
||||
XEX_SECTION_DATA = 2,
|
||||
XEX_SECTION_READONLY_DATA = 3,
|
||||
};
|
||||
|
||||
enum xex2_image_flags : uint32_t {
|
||||
XEX_IMAGE_MANUFACTURING_UTILITY = 0x00000002,
|
||||
XEX_IMAGE_MANUFACTURING_SUPPORT_TOOLS = 0x00000004,
|
||||
XEX_IMAGE_XGD2_MEDIA_ONLY = 0x00000008,
|
||||
XEX_IMAGE_CARDEA_KEY = 0x00000100,
|
||||
XEX_IMAGE_XEIKA_KEY = 0x00000200,
|
||||
XEX_IMAGE_USERMODE_TITLE = 0x00000400,
|
||||
XEX_IMAGE_USERMODE_SYSTEM = 0x00000800,
|
||||
XEX_IMAGE_ORANGE0 = 0x00001000,
|
||||
XEX_IMAGE_ORANGE1 = 0x00002000,
|
||||
XEX_IMAGE_ORANGE2 = 0x00004000,
|
||||
XEX_IMAGE_IPTV_SIGNUP_APPLICATION = 0x00010000,
|
||||
XEX_IMAGE_IPTV_TITLE_APPLICATION = 0x00020000,
|
||||
XEX_IMAGE_KEYVAULT_PRIVILEGES_REQUIRED = 0x04000000,
|
||||
XEX_IMAGE_ONLINE_ACTIVATION_REQUIRED = 0x08000000,
|
||||
XEX_IMAGE_PAGE_SIZE_4KB = 0x10000000, // else 64KB
|
||||
XEX_IMAGE_REGION_FREE = 0x20000000,
|
||||
XEX_IMAGE_REVOCATION_CHECK_OPTIONAL = 0x40000000,
|
||||
XEX_IMAGE_REVOCATION_CHECK_REQUIRED = 0x80000000,
|
||||
};
|
||||
|
||||
enum xex2_media_flags : uint32_t {
|
||||
XEX_MEDIA_HARDDISK = 0x00000001,
|
||||
XEX_MEDIA_DVD_X2 = 0x00000002,
|
||||
XEX_MEDIA_DVD_CD = 0x00000004,
|
||||
XEX_MEDIA_DVD_5 = 0x00000008,
|
||||
XEX_MEDIA_DVD_9 = 0x00000010,
|
||||
XEX_MEDIA_SYSTEM_FLASH = 0x00000020,
|
||||
XEX_MEDIA_MEMORY_UNIT = 0x00000080,
|
||||
XEX_MEDIA_USB_MASS_STORAGE_DEVICE = 0x00000100,
|
||||
XEX_MEDIA_NETWORK = 0x00000200,
|
||||
XEX_MEDIA_DIRECT_FROM_MEMORY = 0x00000400,
|
||||
XEX_MEDIA_RAM_DRIVE = 0x00000800,
|
||||
XEX_MEDIA_SVOD = 0x00001000,
|
||||
XEX_MEDIA_INSECURE_PACKAGE = 0x01000000,
|
||||
XEX_MEDIA_SAVEGAME_PACKAGE = 0x02000000,
|
||||
XEX_MEDIA_LOCALLY_SIGNED_PACKAGE = 0x04000000,
|
||||
XEX_MEDIA_LIVE_SIGNED_PACKAGE = 0x08000000,
|
||||
XEX_MEDIA_XBOX_PACKAGE = 0x10000000,
|
||||
};
|
||||
|
||||
enum xex2_region_flags : uint32_t {
|
||||
XEX_REGION_NTSCU = 0x000000FF,
|
||||
XEX_REGION_NTSCJ = 0x0000FF00,
|
||||
XEX_REGION_NTSCJ_JAPAN = 0x00000100,
|
||||
XEX_REGION_NTSCJ_CHINA = 0x00000200,
|
||||
XEX_REGION_PAL = 0x00FF0000,
|
||||
XEX_REGION_PAL_AU_NZ = 0x00010000,
|
||||
XEX_REGION_OTHER = 0xFF000000,
|
||||
XEX_REGION_ALL = 0xFFFFFFFF,
|
||||
};
|
||||
|
||||
enum xex2_module_flags : uint32_t {
|
||||
XEX_MODULE_TITLE = 0x00000001,
|
||||
XEX_MODULE_EXPORTS_TO_TITLE = 0x00000002,
|
||||
XEX_MODULE_SYSTEM_DEBUGGER = 0x00000004,
|
||||
|
@ -58,7 +121,7 @@ enum xe_xex2_module_flags : uint32_t {
|
|||
XEX_MODULE_USER_MODE = 0x00000080,
|
||||
};
|
||||
|
||||
enum xe_xex2_system_flags : uint32_t {
|
||||
enum xex2_system_flags : uint32_t {
|
||||
XEX_SYSTEM_NO_FORCED_REBOOT = 0x00000001,
|
||||
XEX_SYSTEM_FOREGROUND_TASKS = 0x00000002,
|
||||
XEX_SYSTEM_NO_ODD_MAPPING = 0x00000004,
|
||||
|
@ -102,7 +165,7 @@ enum xe_xex2_system_flags : uint32_t {
|
|||
};
|
||||
|
||||
// ESRB (Entertainment Software Rating Board)
|
||||
enum xe_xex2_rating_esrb_value : uint32_t {
|
||||
enum xex2_rating_esrb_value : uint32_t {
|
||||
XEX_RATING_ESRB_eC = 0x00,
|
||||
XEX_RATING_ESRB_E = 0x02,
|
||||
XEX_RATING_ESRB_E10 = 0x04,
|
||||
|
@ -112,7 +175,7 @@ enum xe_xex2_rating_esrb_value : uint32_t {
|
|||
XEX_RATING_ESRB_UNRATED = 0xFF,
|
||||
};
|
||||
// PEGI (Pan European Game Information)
|
||||
enum xe_xex2_rating_pegi_value : uint32_t {
|
||||
enum xex2_rating_pegi_value : uint32_t {
|
||||
XEX_RATING_PEGI_3_PLUS = 0,
|
||||
XEX_RATING_PEGI_7_PLUS = 4,
|
||||
XEX_RATING_PEGI_12_PLUS = 9,
|
||||
|
@ -121,7 +184,7 @@ enum xe_xex2_rating_pegi_value : uint32_t {
|
|||
XEX_RATING_PEGI_UNRATED = 0xFF,
|
||||
};
|
||||
// PEGI (Pan European Game Information) - Finland
|
||||
enum xe_xex2_rating_pegi_fi_value : uint32_t {
|
||||
enum xex2_rating_pegi_fi_value : uint32_t {
|
||||
XEX_RATING_PEGI_FI_3_PLUS = 0,
|
||||
XEX_RATING_PEGI_FI_7_PLUS = 4,
|
||||
XEX_RATING_PEGI_FI_11_PLUS = 8,
|
||||
|
@ -130,7 +193,7 @@ enum xe_xex2_rating_pegi_fi_value : uint32_t {
|
|||
XEX_RATING_PEGI_FI_UNRATED = 0xFF,
|
||||
};
|
||||
// PEGI (Pan European Game Information) - Portugal
|
||||
enum xe_xex2_rating_pegi_pt_value : uint32_t {
|
||||
enum xex2_rating_pegi_pt_value : uint32_t {
|
||||
XEX_RATING_PEGI_PT_4_PLUS = 1,
|
||||
XEX_RATING_PEGI_PT_6_PLUS = 3,
|
||||
XEX_RATING_PEGI_PT_12_PLUS = 9,
|
||||
|
@ -139,7 +202,7 @@ enum xe_xex2_rating_pegi_pt_value : uint32_t {
|
|||
XEX_RATING_PEGI_PT_UNRATED = 0xFF,
|
||||
};
|
||||
// BBFC (British Board of Film Classification) - UK/Ireland
|
||||
enum xe_xex2_rating_bbfc_value : uint32_t {
|
||||
enum xex2_rating_bbfc_value : uint32_t {
|
||||
XEX_RATING_BBFC_UNIVERSAL = 1,
|
||||
XEX_RATING_BBFC_PG = 5,
|
||||
XEX_RATING_BBFC_3_PLUS = 0,
|
||||
|
@ -151,7 +214,7 @@ enum xe_xex2_rating_bbfc_value : uint32_t {
|
|||
XEX_RATING_BBFC_UNRATED = 0xFF,
|
||||
};
|
||||
// CERO (Computer Entertainment Rating Organization)
|
||||
enum xe_xex2_rating_cero_value : uint32_t {
|
||||
enum xex2_rating_cero_value : uint32_t {
|
||||
XEX_RATING_CERO_A = 0,
|
||||
XEX_RATING_CERO_B = 2,
|
||||
XEX_RATING_CERO_C = 4,
|
||||
|
@ -160,7 +223,7 @@ enum xe_xex2_rating_cero_value : uint32_t {
|
|||
XEX_RATING_CERO_UNRATED = 0xFF,
|
||||
};
|
||||
// USK (Unterhaltungssoftware SelbstKontrolle)
|
||||
enum xe_xex2_rating_usk_value : uint32_t {
|
||||
enum xex2_rating_usk_value : uint32_t {
|
||||
XEX_RATING_USK_ALL = 0,
|
||||
XEX_RATING_USK_6_PLUS = 2,
|
||||
XEX_RATING_USK_12_PLUS = 4,
|
||||
|
@ -169,7 +232,7 @@ enum xe_xex2_rating_usk_value : uint32_t {
|
|||
XEX_RATING_USK_UNRATED = 0xFF,
|
||||
};
|
||||
// OFLC (Office of Film and Literature Classification) - Australia
|
||||
enum xe_xex2_rating_oflc_au_value : uint32_t {
|
||||
enum xex2_rating_oflc_au_value : uint32_t {
|
||||
XEX_RATING_OFLC_AU_G = 0,
|
||||
XEX_RATING_OFLC_AU_PG = 2,
|
||||
XEX_RATING_OFLC_AU_M = 4,
|
||||
|
@ -177,7 +240,7 @@ enum xe_xex2_rating_oflc_au_value : uint32_t {
|
|||
XEX_RATING_OFLC_AU_UNRATED = 0xFF,
|
||||
};
|
||||
// OFLC (Office of Film and Literature Classification) - New Zealand
|
||||
enum xe_xex2_rating_oflc_nz_value : uint32_t {
|
||||
enum xex2_rating_oflc_nz_value : uint32_t {
|
||||
XEX_RATING_OFLC_NZ_G = 0,
|
||||
XEX_RATING_OFLC_NZ_PG = 2,
|
||||
XEX_RATING_OFLC_NZ_M = 4,
|
||||
|
@ -185,7 +248,7 @@ enum xe_xex2_rating_oflc_nz_value : uint32_t {
|
|||
XEX_RATING_OFLC_NZ_UNRATED = 0xFF,
|
||||
};
|
||||
// KMRB (Korea Media Rating Board)
|
||||
enum xe_xex2_rating_kmrb_value : uint32_t {
|
||||
enum xex2_rating_kmrb_value : uint32_t {
|
||||
XEX_RATING_KMRB_ALL = 0,
|
||||
XEX_RATING_KMRB_12_PLUS = 2,
|
||||
XEX_RATING_KMRB_15_PLUS = 4,
|
||||
|
@ -193,7 +256,7 @@ enum xe_xex2_rating_kmrb_value : uint32_t {
|
|||
XEX_RATING_KMRB_UNRATED = 0xFF,
|
||||
};
|
||||
// Brazil
|
||||
enum xe_xex2_rating_brazil_value : uint32_t {
|
||||
enum xex2_rating_brazil_value : uint32_t {
|
||||
XEX_RATING_BRAZIL_ALL = 0,
|
||||
XEX_RATING_BRAZIL_12_PLUS = 2,
|
||||
XEX_RATING_BRAZIL_14_PLUS = 4,
|
||||
|
@ -202,7 +265,7 @@ enum xe_xex2_rating_brazil_value : uint32_t {
|
|||
XEX_RATING_BRAZIL_UNRATED = 0xFF,
|
||||
};
|
||||
// FPB (Film and Publication Board)
|
||||
enum xe_xex2_rating_fpb_value : uint32_t {
|
||||
enum xex2_rating_fpb_value : uint32_t {
|
||||
XEX_RATING_FPB_ALL = 0,
|
||||
XEX_RATING_FPB_PG = 6,
|
||||
XEX_RATING_FPB_10_PLUS = 7,
|
||||
|
@ -212,252 +275,102 @@ enum xe_xex2_rating_fpb_value : uint32_t {
|
|||
XEX_RATING_FPB_UNRATED = 0xFF,
|
||||
};
|
||||
|
||||
struct xe_xex2_game_ratings_t {
|
||||
xe_xex2_rating_esrb_value esrb;
|
||||
xe_xex2_rating_pegi_value pegi;
|
||||
xe_xex2_rating_pegi_fi_value pegifi;
|
||||
xe_xex2_rating_pegi_pt_value pegipt;
|
||||
xe_xex2_rating_bbfc_value bbfc;
|
||||
xe_xex2_rating_cero_value cero;
|
||||
xe_xex2_rating_usk_value usk;
|
||||
xe_xex2_rating_oflc_au_value oflcau;
|
||||
xe_xex2_rating_oflc_nz_value oflcnz;
|
||||
xe_xex2_rating_kmrb_value kmrb;
|
||||
xe_xex2_rating_brazil_value brazil;
|
||||
xe_xex2_rating_fpb_value fpb;
|
||||
enum xex2_header_keys : uint32_t {
|
||||
XEX_HEADER_RESOURCE_INFO = 0x000002FF,
|
||||
XEX_HEADER_FILE_FORMAT_INFO = 0x000003FF,
|
||||
XEX_HEADER_DELTA_PATCH_DESCRIPTOR = 0x000005FF,
|
||||
XEX_HEADER_BASE_REFERENCE = 0x00000405,
|
||||
XEX_HEADER_BOUNDING_PATH = 0x000080FF,
|
||||
XEX_HEADER_DEVICE_ID = 0x00008105,
|
||||
XEX_HEADER_ORIGINAL_BASE_ADDRESS = 0x00010001,
|
||||
XEX_HEADER_ENTRY_POINT = 0x00010100,
|
||||
XEX_HEADER_IMAGE_BASE_ADDRESS = 0x00010201,
|
||||
XEX_HEADER_IMPORT_LIBRARIES = 0x000103FF,
|
||||
XEX_HEADER_CHECKSUM_TIMESTAMP = 0x00018002,
|
||||
XEX_HEADER_ENABLED_FOR_CALLCAP = 0x00018102,
|
||||
XEX_HEADER_ENABLED_FOR_FASTCAP = 0x00018200,
|
||||
XEX_HEADER_ORIGINAL_PE_NAME = 0x000183FF,
|
||||
XEX_HEADER_STATIC_LIBRARIES = 0x000200FF,
|
||||
XEX_HEADER_TLS_INFO = 0x00020104,
|
||||
XEX_HEADER_DEFAULT_STACK_SIZE = 0x00020200,
|
||||
XEX_HEADER_DEFAULT_FILESYSTEM_CACHE_SIZE = 0x00020301,
|
||||
XEX_HEADER_DEFAULT_HEAP_SIZE = 0x00020401,
|
||||
XEX_HEADER_PAGE_HEAP_SIZE_AND_FLAGS = 0x00028002,
|
||||
XEX_HEADER_SYSTEM_FLAGS = 0x00030000,
|
||||
XEX_HEADER_EXECUTION_INFO = 0x00040006,
|
||||
XEX_HEADER_TITLE_WORKSPACE_SIZE = 0x00040201,
|
||||
XEX_HEADER_GAME_RATINGS = 0x00040310,
|
||||
XEX_HEADER_LAN_KEY = 0x00040404,
|
||||
XEX_HEADER_XBOX360_LOGO = 0x000405FF,
|
||||
XEX_HEADER_MULTIDISC_MEDIA_IDS = 0x000406FF,
|
||||
XEX_HEADER_ALTERNATE_TITLE_IDS = 0x000407FF,
|
||||
XEX_HEADER_ADDITIONAL_TITLE_MEMORY = 0x00040801,
|
||||
XEX_HEADER_EXPORTS_BY_NAME = 0x00E10402,
|
||||
};
|
||||
|
||||
union xe_xex2_version_t {
|
||||
uint32_t value;
|
||||
struct {
|
||||
uint32_t major : 4;
|
||||
uint32_t minor : 4;
|
||||
uint32_t build : 16;
|
||||
uint32_t qfe : 8;
|
||||
};
|
||||
};
|
||||
|
||||
struct xe_xex2_opt_header_t {
|
||||
uint32_t key;
|
||||
uint32_t length;
|
||||
union {
|
||||
uint32_t value;
|
||||
uint32_t offset;
|
||||
};
|
||||
};
|
||||
|
||||
struct xe_xex2_resource_info_t {
|
||||
char name[9];
|
||||
uint32_t address;
|
||||
uint32_t size;
|
||||
};
|
||||
|
||||
struct xe_xex2_execution_info_t {
|
||||
uint32_t media_id;
|
||||
xe_xex2_version_t version;
|
||||
xe_xex2_version_t base_version;
|
||||
uint32_t title_id;
|
||||
uint8_t platform;
|
||||
uint8_t executable_table;
|
||||
uint8_t disc_number;
|
||||
uint8_t disc_count;
|
||||
uint32_t savegame_id;
|
||||
};
|
||||
|
||||
struct xe_xex2_tls_info_t {
|
||||
uint32_t slot_count;
|
||||
uint32_t raw_data_address;
|
||||
uint32_t data_size;
|
||||
uint32_t raw_data_size;
|
||||
};
|
||||
|
||||
struct xe_xex2_import_library_t {
|
||||
char name[32];
|
||||
uint8_t digest[20];
|
||||
uint32_t import_id;
|
||||
xe_xex2_version_t version;
|
||||
xe_xex2_version_t min_version;
|
||||
size_t record_count;
|
||||
uint32_t* records;
|
||||
};
|
||||
|
||||
enum xe_xex2_approval_type : uint32_t {
|
||||
XEX_APPROVAL_UNAPPROVED = 0,
|
||||
XEX_APPROVAL_POSSIBLE = 1,
|
||||
XEX_APPROVAL_APPROVED = 2,
|
||||
XEX_APPROVAL_EXPIRED = 3,
|
||||
};
|
||||
|
||||
struct xe_xex2_static_library_t {
|
||||
char name[9]; // 8 + 1 for \0
|
||||
uint16_t major;
|
||||
uint16_t minor;
|
||||
uint16_t build;
|
||||
uint16_t qfe;
|
||||
xe_xex2_approval_type approval;
|
||||
};
|
||||
|
||||
enum xe_xex2_encryption_type : uint32_t {
|
||||
enum xex2_encryption_type : uint16_t {
|
||||
XEX_ENCRYPTION_NONE = 0,
|
||||
XEX_ENCRYPTION_NORMAL = 1,
|
||||
};
|
||||
|
||||
enum xe_xex2_compression_type : uint32_t {
|
||||
enum xex2_compression_type : uint16_t {
|
||||
XEX_COMPRESSION_NONE = 0,
|
||||
XEX_COMPRESSION_BASIC = 1,
|
||||
XEX_COMPRESSION_NORMAL = 2,
|
||||
XEX_COMPRESSION_DELTA = 3,
|
||||
};
|
||||
|
||||
struct xe_xex2_file_basic_compression_block_t {
|
||||
uint32_t data_size;
|
||||
uint32_t zero_size;
|
||||
enum xex2_approval_type : uint32_t {
|
||||
XEX_APPROVAL_UNAPPROVED = 0,
|
||||
XEX_APPROVAL_POSSIBLE = 1,
|
||||
XEX_APPROVAL_APPROVED = 2,
|
||||
XEX_APPROVAL_EXPIRED = 3,
|
||||
};
|
||||
|
||||
struct xe_xex2_file_basic_compression_info_t {
|
||||
uint32_t block_count;
|
||||
xe_xex2_file_basic_compression_block_t* blocks;
|
||||
struct xex2_game_ratings_t {
|
||||
xe::be<xex2_rating_esrb_value> esrb;
|
||||
xe::be<xex2_rating_pegi_value> pegi;
|
||||
xe::be<xex2_rating_pegi_fi_value> pegifi;
|
||||
xe::be<xex2_rating_pegi_pt_value> pegipt;
|
||||
xe::be<xex2_rating_bbfc_value> bbfc;
|
||||
xe::be<xex2_rating_cero_value> cero;
|
||||
xe::be<xex2_rating_usk_value> usk;
|
||||
xe::be<xex2_rating_oflc_au_value> oflcau;
|
||||
xe::be<xex2_rating_oflc_nz_value> oflcnz;
|
||||
xe::be<xex2_rating_kmrb_value> kmrb;
|
||||
xe::be<xex2_rating_brazil_value> brazil;
|
||||
xe::be<xex2_rating_fpb_value> fpb;
|
||||
};
|
||||
|
||||
struct xe_xex2_file_normal_compression_info_t {
|
||||
uint32_t window_size;
|
||||
uint32_t window_bits;
|
||||
uint32_t block_size;
|
||||
struct xex2_file_basic_compression_block {
|
||||
xe::be<uint32_t> data_size;
|
||||
xe::be<uint32_t> zero_size;
|
||||
};
|
||||
|
||||
struct xex2_file_basic_compression_info {
|
||||
xex2_file_basic_compression_block blocks[1];
|
||||
};
|
||||
|
||||
struct xex2_compressed_block_info {
|
||||
xe::be<uint32_t> block_size;
|
||||
uint8_t block_hash[20];
|
||||
};
|
||||
|
||||
struct xe_xex2_file_format_info_t {
|
||||
xe_xex2_encryption_type encryption_type;
|
||||
xe_xex2_compression_type compression_type;
|
||||
struct xex2_file_normal_compression_info {
|
||||
xe::be<uint32_t> window_size;
|
||||
xex2_compressed_block_info first_block;
|
||||
};
|
||||
|
||||
struct xex2_opt_file_format_info {
|
||||
xe::be<uint32_t> info_size;
|
||||
xe::be<xex2_encryption_type> encryption_type;
|
||||
xe::be<xex2_compression_type> compression_type;
|
||||
union {
|
||||
xe_xex2_file_basic_compression_info_t basic;
|
||||
xe_xex2_file_normal_compression_info_t normal;
|
||||
xex2_file_basic_compression_info basic;
|
||||
xex2_file_normal_compression_info normal;
|
||||
} compression_info;
|
||||
};
|
||||
|
||||
enum xe_xex2_image_flags : uint32_t {
|
||||
XEX_IMAGE_MANUFACTURING_UTILITY = 0x00000002,
|
||||
XEX_IMAGE_MANUFACTURING_SUPPORT_TOOLS = 0x00000004,
|
||||
XEX_IMAGE_XGD2_MEDIA_ONLY = 0x00000008,
|
||||
XEX_IMAGE_CARDEA_KEY = 0x00000100,
|
||||
XEX_IMAGE_XEIKA_KEY = 0x00000200,
|
||||
XEX_IMAGE_USERMODE_TITLE = 0x00000400,
|
||||
XEX_IMAGE_USERMODE_SYSTEM = 0x00000800,
|
||||
XEX_IMAGE_ORANGE0 = 0x00001000,
|
||||
XEX_IMAGE_ORANGE1 = 0x00002000,
|
||||
XEX_IMAGE_ORANGE2 = 0x00004000,
|
||||
XEX_IMAGE_IPTV_SIGNUP_APPLICATION = 0x00010000,
|
||||
XEX_IMAGE_IPTV_TITLE_APPLICATION = 0x00020000,
|
||||
XEX_IMAGE_KEYVAULT_PRIVILEGES_REQUIRED = 0x04000000,
|
||||
XEX_IMAGE_ONLINE_ACTIVATION_REQUIRED = 0x08000000,
|
||||
XEX_IMAGE_PAGE_SIZE_4KB = 0x10000000, // else 64KB
|
||||
XEX_IMAGE_REGION_FREE = 0x20000000,
|
||||
XEX_IMAGE_REVOCATION_CHECK_OPTIONAL = 0x40000000,
|
||||
XEX_IMAGE_REVOCATION_CHECK_REQUIRED = 0x80000000,
|
||||
};
|
||||
|
||||
enum xe_xex2_media_flags : uint32_t {
|
||||
XEX_MEDIA_HARDDISK = 0x00000001,
|
||||
XEX_MEDIA_DVD_X2 = 0x00000002,
|
||||
XEX_MEDIA_DVD_CD = 0x00000004,
|
||||
XEX_MEDIA_DVD_5 = 0x00000008,
|
||||
XEX_MEDIA_DVD_9 = 0x00000010,
|
||||
XEX_MEDIA_SYSTEM_FLASH = 0x00000020,
|
||||
XEX_MEDIA_MEMORY_UNIT = 0x00000080,
|
||||
XEX_MEDIA_USB_MASS_STORAGE_DEVICE = 0x00000100,
|
||||
XEX_MEDIA_NETWORK = 0x00000200,
|
||||
XEX_MEDIA_DIRECT_FROM_MEMORY = 0x00000400,
|
||||
XEX_MEDIA_RAM_DRIVE = 0x00000800,
|
||||
XEX_MEDIA_SVOD = 0x00001000,
|
||||
XEX_MEDIA_INSECURE_PACKAGE = 0x01000000,
|
||||
XEX_MEDIA_SAVEGAME_PACKAGE = 0x02000000,
|
||||
XEX_MEDIA_LOCALLY_SIGNED_PACKAGE = 0x04000000,
|
||||
XEX_MEDIA_LIVE_SIGNED_PACKAGE = 0x08000000,
|
||||
XEX_MEDIA_XBOX_PACKAGE = 0x10000000,
|
||||
};
|
||||
|
||||
enum xe_xex2_region_flags : uint32_t {
|
||||
XEX_REGION_NTSCU = 0x000000FF,
|
||||
XEX_REGION_NTSCJ = 0x0000FF00,
|
||||
XEX_REGION_NTSCJ_JAPAN = 0x00000100,
|
||||
XEX_REGION_NTSCJ_CHINA = 0x00000200,
|
||||
XEX_REGION_PAL = 0x00FF0000,
|
||||
XEX_REGION_PAL_AU_NZ = 0x00010000,
|
||||
XEX_REGION_OTHER = 0xFF000000,
|
||||
XEX_REGION_ALL = 0xFFFFFFFF,
|
||||
};
|
||||
|
||||
struct xe_xex2_loader_info_t {
|
||||
uint32_t header_size;
|
||||
uint32_t image_size;
|
||||
uint8_t rsa_signature[256];
|
||||
uint32_t unklength;
|
||||
xe_xex2_image_flags image_flags;
|
||||
uint32_t load_address;
|
||||
uint8_t section_digest[20];
|
||||
uint32_t import_table_count;
|
||||
uint8_t import_table_digest[20];
|
||||
uint8_t media_id[16];
|
||||
uint8_t file_key[16];
|
||||
uint32_t export_table; // address of the export table
|
||||
uint8_t header_digest[20];
|
||||
xe_xex2_region_flags game_regions;
|
||||
xe_xex2_media_flags media_flags;
|
||||
};
|
||||
|
||||
enum xe_xex2_section_type {
|
||||
XEX_SECTION_CODE = 1,
|
||||
XEX_SECTION_DATA = 2,
|
||||
XEX_SECTION_READONLY_DATA = 3,
|
||||
};
|
||||
|
||||
struct xe_xex2_section_t {
|
||||
uint32_t page_size;
|
||||
union {
|
||||
struct {
|
||||
xe_xex2_section_type type : 4;
|
||||
uint32_t page_count : 28; // # of pages
|
||||
};
|
||||
uint32_t value; // To make uint8_t swapping easier
|
||||
} info;
|
||||
uint8_t digest[20];
|
||||
};
|
||||
|
||||
struct xe_xex2_header_t {
|
||||
uint32_t xex2;
|
||||
xe_xex2_module_flags module_flags;
|
||||
uint32_t exe_offset;
|
||||
uint32_t unknown0;
|
||||
uint32_t certificate_offset;
|
||||
|
||||
xe_xex2_system_flags system_flags;
|
||||
xe_xex2_execution_info_t execution_info;
|
||||
xe_xex2_game_ratings_t game_ratings;
|
||||
xe_xex2_tls_info_t tls_info;
|
||||
size_t import_library_count;
|
||||
xe_xex2_import_library_t import_libraries[32];
|
||||
uint32_t pe_export_table_offset; // PE Export Directory
|
||||
size_t static_library_count;
|
||||
xe_xex2_static_library_t static_libraries[32];
|
||||
xe_xex2_file_format_info_t file_format_info;
|
||||
xe_xex2_loader_info_t loader_info;
|
||||
uint8_t session_key[16];
|
||||
|
||||
uint32_t exe_address;
|
||||
uint32_t exe_entry_point;
|
||||
uint32_t exe_stack_size;
|
||||
uint32_t exe_heap_size;
|
||||
|
||||
size_t header_count;
|
||||
xe_xex2_opt_header_t headers[64];
|
||||
|
||||
size_t resource_info_count;
|
||||
xe_xex2_resource_info_t* resource_infos;
|
||||
size_t section_count;
|
||||
xe_xex2_section_t* sections;
|
||||
};
|
||||
|
||||
namespace xe {
|
||||
union xex2_version {
|
||||
xe::be<uint32_t> value;
|
||||
struct {
|
||||
|
@ -536,8 +449,9 @@ struct xex2_opt_delta_patch_descriptor {
|
|||
xe::be<uint32_t> delta_image_source_offset; // 0x40
|
||||
xe::be<uint32_t> delta_image_source_size; // 0x44
|
||||
xe::be<uint32_t> delta_image_target_offset; // 0x48
|
||||
char delta_header_patch_data[1];
|
||||
};
|
||||
static_assert_size(xex2_opt_delta_patch_descriptor, 0x4C);
|
||||
// static_assert_size(xex2_opt_delta_patch_descriptor, 0x4D);
|
||||
|
||||
struct xex2_opt_execution_info {
|
||||
xe::be<uint32_t> media_id; // 0x0
|
||||
|
@ -580,12 +494,12 @@ struct xex2_opt_header {
|
|||
};
|
||||
|
||||
struct xex2_header {
|
||||
xe::be<uint32_t> magic; // 0x0 'XEX2'
|
||||
xe::be<xe_xex2_module_flags> module_flags; // 0x4
|
||||
xe::be<uint32_t> header_size; // 0x8
|
||||
xe::be<uint32_t> reserved; // 0xC
|
||||
xe::be<uint32_t> security_offset; // 0x10
|
||||
xe::be<uint32_t> header_count; // 0x14
|
||||
xe::be<uint32_t> magic; // 0x0 'XEX2'
|
||||
xe::be<xex2_module_flags> module_flags; // 0x4
|
||||
xe::be<uint32_t> header_size; // 0x8
|
||||
xe::be<uint32_t> reserved; // 0xC
|
||||
xe::be<uint32_t> security_offset; // 0x10
|
||||
xe::be<uint32_t> header_count; // 0x14
|
||||
|
||||
xex2_opt_header headers[1]; // 0x18
|
||||
};
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
#include "xenia/kernel/kernel_state.h"
|
||||
#include "xenia/kernel/user_module.h"
|
||||
#include "xenia/kernel/util/shim_utils.h"
|
||||
#include "xenia/kernel/util/xex2.h"
|
||||
#include "xenia/kernel/xam/xam_module.h"
|
||||
#include "xenia/kernel/xam/xam_private.h"
|
||||
#include "xenia/kernel/xenumerator.h"
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
#include "xenia/kernel/kernel_state.h"
|
||||
#include "xenia/kernel/user_module.h"
|
||||
#include "xenia/kernel/util/shim_utils.h"
|
||||
#include "xenia/kernel/util/xex2.h"
|
||||
#include "xenia/kernel/xboxkrnl/xboxkrnl_error.h"
|
||||
#include "xenia/kernel/xboxkrnl/xboxkrnl_private.h"
|
||||
#include "xenia/kernel/xthread.h"
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
#include "xenia/kernel/kernel_state.h"
|
||||
#include "xenia/kernel/user_module.h"
|
||||
#include "xenia/kernel/util/shim_utils.h"
|
||||
#include "xenia/kernel/util/xex2.h"
|
||||
#include "xenia/kernel/xboxkrnl/xboxkrnl_private.h"
|
||||
#include "xenia/xbox.h"
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include "xenia/kernel/kernel_state.h"
|
||||
#include "xenia/kernel/user_module.h"
|
||||
#include "xenia/kernel/util/shim_utils.h"
|
||||
#include "xenia/kernel/util/xex2.h"
|
||||
#include "xenia/kernel/xboxkrnl/xboxkrnl_private.h"
|
||||
#include "xenia/kernel/xboxkrnl/xboxkrnl_threading.h"
|
||||
#include "xenia/kernel/xevent.h"
|
||||
|
@ -285,7 +284,7 @@ pointer_result_t RtlImageXexHeaderField(pointer_t<xex2_header> xex_header,
|
|||
uint32_t field = field_dword; // VS acts weird going from dword_t -> enum
|
||||
|
||||
UserModule::GetOptHeader(kernel_memory()->virtual_membase(), xex_header,
|
||||
xe_xex2_header_keys(field), &field_value);
|
||||
xex2_header_keys(field), &field_value);
|
||||
|
||||
return field_value;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
#include "xenia/kernel/kernel_state.h"
|
||||
#include "xenia/kernel/user_module.h"
|
||||
#include "xenia/kernel/util/shim_utils.h"
|
||||
#include "xenia/kernel/util/xex2.h"
|
||||
#include "xenia/kernel/xboxkrnl/xboxkrnl_private.h"
|
||||
#include "xenia/kernel/xthread.h"
|
||||
#include "xenia/xbox.h"
|
||||
|
|
Loading…
Reference in New Issue