[CPU] Add support for delta patches using *_offset fields
This commit is contained in:
parent
d2fd109af3
commit
6e74ba93d6
|
@ -101,7 +101,7 @@ struct mspack_system* mspack_memory_sys_create() {
|
||||||
void mspack_memory_sys_destroy(struct mspack_system* sys) { free(sys); }
|
void mspack_memory_sys_destroy(struct mspack_system* sys) { free(sys); }
|
||||||
|
|
||||||
int lzx_decompress(const void* lzx_data, size_t lzx_len, void* dest,
|
int lzx_decompress(const void* lzx_data, size_t lzx_len, void* dest,
|
||||||
size_t dest_len, int window_size, void* window_data,
|
size_t dest_len, uint32_t window_size, void* window_data,
|
||||||
size_t window_data_len) {
|
size_t window_data_len) {
|
||||||
uint32_t window_bits = 0;
|
uint32_t window_bits = 0;
|
||||||
uint32_t temp_sz = window_size;
|
uint32_t temp_sz = window_size;
|
||||||
|
@ -152,7 +152,7 @@ int lzx_decompress(const void* lzx_data, size_t lzx_len, void* dest,
|
||||||
}
|
}
|
||||||
|
|
||||||
int lzxdelta_apply_patch(xe::xex2_delta_patch* patch, size_t patch_len,
|
int lzxdelta_apply_patch(xe::xex2_delta_patch* patch, size_t patch_len,
|
||||||
void* dest) {
|
uint32_t window_size, void* dest) {
|
||||||
void* patch_end = (char*)patch + patch_len;
|
void* patch_end = (char*)patch + patch_len;
|
||||||
auto* cur_patch = patch;
|
auto* cur_patch = patch;
|
||||||
|
|
||||||
|
@ -178,7 +178,7 @@ int lzxdelta_apply_patch(xe::xex2_delta_patch* patch, size_t patch_len,
|
||||||
int result = lzx_decompress(
|
int result = lzx_decompress(
|
||||||
cur_patch->patch_data, cur_patch->compressed_len,
|
cur_patch->patch_data, cur_patch->compressed_len,
|
||||||
(char*)dest + cur_patch->new_addr, cur_patch->uncompressed_len,
|
(char*)dest + cur_patch->new_addr, cur_patch->uncompressed_len,
|
||||||
0x8000, (char*)dest + cur_patch->old_addr,
|
window_size, (char*)dest + cur_patch->old_addr,
|
||||||
cur_patch->uncompressed_len);
|
cur_patch->uncompressed_len);
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
|
@ -369,7 +369,7 @@ int XexModule::ApplyPatch(XexModule* module) {
|
||||||
reinterpret_cast<void**>(&patch_header));
|
reinterpret_cast<void**>(&patch_header));
|
||||||
assert_not_null(patch_header);
|
assert_not_null(patch_header);
|
||||||
|
|
||||||
// Compare hash inside delta descriptor to our loaded base XEX
|
// Compare hash inside delta descriptor to base XEX signature
|
||||||
uint8_t digest[0x14];
|
uint8_t digest[0x14];
|
||||||
sha1::SHA1 s;
|
sha1::SHA1 s;
|
||||||
s.processBytes(module->xex_security_info()->rsa_signature, 0x100);
|
s.processBytes(module->xex_security_info()->rsa_signature, 0x100);
|
||||||
|
@ -407,37 +407,61 @@ int XexModule::ApplyPatch(XexModule* module) {
|
||||||
// though
|
// though
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: actually use delta_*_offset / delta_*_size etc
|
// Patch base XEX header
|
||||||
assert_zero(patch_header->delta_headers_source_offset);
|
uint32_t original_image_size = module->image_size();
|
||||||
assert_zero(patch_header->delta_headers_target_offset);
|
uint32_t header_target_size = patch_header->delta_headers_target_offset +
|
||||||
assert_zero(patch_header->delta_image_source_offset);
|
patch_header->delta_headers_source_size;
|
||||||
assert_zero(patch_header->delta_image_target_offset);
|
|
||||||
|
|
||||||
if (patch_header->delta_headers_source_offset ||
|
if (!header_target_size) {
|
||||||
patch_header->delta_headers_target_offset ||
|
header_target_size =
|
||||||
patch_header->delta_image_source_offset ||
|
patch_header->size_of_target_headers; // unsure which is more correct..
|
||||||
patch_header->delta_image_target_offset) {
|
|
||||||
XELOGW(
|
|
||||||
"XEX patch descriptor has a non-zero delta_*_offset field, patch might "
|
|
||||||
"not get applied properly!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Patch base XEX's header
|
size_t mem_size = module->xex_header_mem_.size();
|
||||||
module->xex_header_mem_.resize(patch_header->size_of_target_headers);
|
|
||||||
|
|
||||||
uint32_t original_image_size = module->image_size();
|
// Increase xex header buffer length if needed
|
||||||
uint32_t headerpatch_size = patch_header->info.compressed_len;
|
if (header_target_size > module->xex_header_mem_.size()) {
|
||||||
|
module->xex_header_mem_.resize(header_target_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto header_ptr = (uint8_t*)module->xex_header();
|
||||||
|
|
||||||
|
// If headers_source_offset is set, copy [source_offset:source_size] to
|
||||||
|
// target_offset
|
||||||
|
if (patch_header->delta_headers_source_offset) {
|
||||||
|
memcpy(header_ptr + patch_header->delta_headers_target_offset,
|
||||||
|
header_ptr + patch_header->delta_headers_source_offset,
|
||||||
|
patch_header->delta_headers_source_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If new size is smaller than original, null out the difference
|
||||||
|
if (header_target_size < module->xex_header_mem_.size()) {
|
||||||
|
memset(header_ptr + header_target_size, 0,
|
||||||
|
module->xex_header_mem_.size() - header_target_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto file_format_header = opt_file_format_info();
|
||||||
|
assert_not_null(file_format_header);
|
||||||
|
|
||||||
|
// Apply header patch...
|
||||||
|
uint32_t headerpatch_size = patch_header->info.compressed_len + 0xC;
|
||||||
|
|
||||||
auto dest_header = module->xex_header();
|
|
||||||
int result_code = lzxdelta_apply_patch(
|
int result_code = lzxdelta_apply_patch(
|
||||||
&patch_header->info, headerpatch_size + 0xC, (void*)dest_header);
|
&patch_header->info, headerpatch_size,
|
||||||
|
file_format_header->compression_info.normal.window_size, header_ptr);
|
||||||
if (result_code) {
|
if (result_code) {
|
||||||
XELOGE("XEX header patch application failed, error code %d", result_code);
|
XELOGE("XEX header patch application failed, error code %d", result_code);
|
||||||
return result_code;
|
return result_code;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if we need to alloc new memory for the patched xex
|
// Decrease xex header buffer length if needed (but only after patching)
|
||||||
|
if (module->xex_header_mem_.size() > header_target_size) {
|
||||||
|
module->xex_header_mem_.resize(header_target_size);
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t new_image_size = module->image_size();
|
uint32_t new_image_size = module->image_size();
|
||||||
|
|
||||||
|
// Check if we need to alloc new memory for the patched xex
|
||||||
if (new_image_size > original_image_size) {
|
if (new_image_size > original_image_size) {
|
||||||
uint32_t size_delta = new_image_size - original_image_size;
|
uint32_t size_delta = new_image_size - original_image_size;
|
||||||
uint32_t addr_new_mem = module->base_address_ + original_image_size;
|
uint32_t addr_new_mem = module->base_address_ + original_image_size;
|
||||||
|
@ -487,35 +511,52 @@ int XexModule::ApplyPatch(XexModule* module) {
|
||||||
|
|
||||||
// Decrypt (if needed).
|
// Decrypt (if needed).
|
||||||
bool free_input = false;
|
bool free_input = false;
|
||||||
const uint8_t* exe_buffer = xexp_data_mem_.data();
|
const uint8_t* patch_buffer = xexp_data_mem_.data();
|
||||||
const size_t exe_length = xexp_data_mem_.size();
|
const size_t patch_length = xexp_data_mem_.size();
|
||||||
|
|
||||||
const uint8_t* input_buffer = exe_buffer;
|
const uint8_t* input_buffer = patch_buffer;
|
||||||
|
|
||||||
switch (opt_file_format_info()->encryption_type) {
|
switch (file_format_header->encryption_type) {
|
||||||
case XEX_ENCRYPTION_NONE:
|
case XEX_ENCRYPTION_NONE:
|
||||||
// No-op.
|
// No-op.
|
||||||
break;
|
break;
|
||||||
case XEX_ENCRYPTION_NORMAL:
|
case XEX_ENCRYPTION_NORMAL:
|
||||||
// TODO: a way to do without a copy/alloc?
|
// TODO: a way to do without a copy/alloc?
|
||||||
free_input = true;
|
free_input = true;
|
||||||
input_buffer = (const uint8_t*)calloc(1, exe_length);
|
input_buffer = (const uint8_t*)calloc(1, patch_length);
|
||||||
aes_decrypt_buffer(session_key_, exe_buffer, exe_length,
|
aes_decrypt_buffer(session_key_, patch_buffer, patch_length,
|
||||||
(uint8_t*)input_buffer, exe_length);
|
(uint8_t*)input_buffer, patch_length);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
assert_always();
|
assert_always();
|
||||||
return 8;
|
return 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now loop through each block and apply the delta patches inside
|
|
||||||
|
|
||||||
const xex2_compressed_block_info* cur_block =
|
const xex2_compressed_block_info* cur_block =
|
||||||
&opt_file_format_info()->compression_info.normal.first_block;
|
&file_format_header->compression_info.normal.first_block;
|
||||||
|
|
||||||
const uint8_t* p = input_buffer;
|
const uint8_t* p = input_buffer;
|
||||||
uint8_t* base_exe = memory()->TranslateVirtual(module->base_address_);
|
uint8_t* base_exe = memory()->TranslateVirtual(module->base_address_);
|
||||||
|
|
||||||
|
// If image_source_offset is set, copy [source_offset:source_size] to
|
||||||
|
// target_offset
|
||||||
|
if (patch_header->delta_image_source_offset) {
|
||||||
|
memcpy(base_exe + patch_header->delta_image_target_offset,
|
||||||
|
base_exe + patch_header->delta_image_source_offset,
|
||||||
|
patch_header->delta_image_source_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: should we use new_image_size here instead?
|
||||||
|
uint32_t image_target_size = patch_header->delta_image_target_offset +
|
||||||
|
patch_header->delta_image_source_size;
|
||||||
|
|
||||||
|
// If new size is smaller than original, null out the difference
|
||||||
|
if (image_target_size < original_image_size) {
|
||||||
|
memset(base_exe + image_target_size, 0,
|
||||||
|
original_image_size - image_target_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now loop through each block and apply the delta patches inside
|
||||||
while (cur_block->block_size) {
|
while (cur_block->block_size) {
|
||||||
const auto* next_block = (const xex2_compressed_block_info*)p;
|
const auto* next_block = (const xex2_compressed_block_info*)p;
|
||||||
|
|
||||||
|
@ -536,8 +577,10 @@ int XexModule::ApplyPatch(XexModule* module) {
|
||||||
|
|
||||||
uint32_t block_data_size = cur_block->block_size - 20 - 4;
|
uint32_t block_data_size = cur_block->block_size - 20 - 4;
|
||||||
|
|
||||||
result_code =
|
// Apply delta patch
|
||||||
lzxdelta_apply_patch((xex2_delta_patch*)p, block_data_size, base_exe);
|
result_code = lzxdelta_apply_patch(
|
||||||
|
(xex2_delta_patch*)p, block_data_size,
|
||||||
|
file_format_header->compression_info.normal.window_size, base_exe);
|
||||||
if (result_code) {
|
if (result_code) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -547,6 +590,22 @@ int XexModule::ApplyPatch(XexModule* module) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!result_code) {
|
if (!result_code) {
|
||||||
|
// Decommit unused pages if new image size is smaller than original
|
||||||
|
if (original_image_size > new_image_size) {
|
||||||
|
uint32_t size_delta = original_image_size - new_image_size;
|
||||||
|
uint32_t addr_free_mem = module->base_address_ + new_image_size;
|
||||||
|
|
||||||
|
bool free_result = memory()
|
||||||
|
->LookupHeap(addr_free_mem)
|
||||||
|
->Decommit(addr_free_mem, size_delta);
|
||||||
|
|
||||||
|
if (!free_result) {
|
||||||
|
XELOGE("Unable to decommit XEX memory at %.8X-%.8X.", addr_free_mem,
|
||||||
|
size_delta);
|
||||||
|
assert_always();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// byteswap versions because of bitfields...
|
// byteswap versions because of bitfields...
|
||||||
xex2_version source_ver, target_ver;
|
xex2_version source_ver, target_ver;
|
||||||
source_ver.value =
|
source_ver.value =
|
||||||
|
|
Loading…
Reference in New Issue