[Kernel] Cleanup in TU applying code.
Externally it is still applied via: ApplyTitleUpdate But internally it is splitted into: - FindTitleUpdate - LoadTitleUpdate - IsPatchSignatureProper - ApplyTitleUpdate Added host window with warning about TU incompatibility
This commit is contained in:
parent
0ee6c77d91
commit
71de56416d
|
@ -230,18 +230,6 @@ 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 base XEX signature
|
|
||||||
uint8_t digest[0x14];
|
|
||||||
sha1::SHA1 s;
|
|
||||||
s.processBytes(module->xex_security_info()->rsa_signature, 0x100);
|
|
||||||
s.finalize(digest);
|
|
||||||
|
|
||||||
if (memcmp(digest, patch_header->digest_source, 0x14) != 0) {
|
|
||||||
XELOGW(
|
|
||||||
"XEX patch signature hash doesn't match base XEX signature hash, patch "
|
|
||||||
"will likely fail!");
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t size = module->xex_header()->header_size;
|
uint32_t size = module->xex_header()->header_size;
|
||||||
if (patch_header->delta_headers_source_offset > size) {
|
if (patch_header->delta_headers_source_offset > size) {
|
||||||
XELOGE("XEX header patch source is outside base XEX header area");
|
XELOGE("XEX header patch source is outside base XEX header area");
|
||||||
|
@ -419,6 +407,8 @@ int XexModule::ApplyPatch(XexModule* module) {
|
||||||
original_image_size - image_target_size);
|
original_image_size - image_target_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t digest[0x14];
|
||||||
|
sha1::SHA1 s;
|
||||||
// Now loop through each block and apply the delta patches inside
|
// 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;
|
||||||
|
|
|
@ -31,6 +31,9 @@
|
||||||
#include "xenia/kernel/xnotifylistener.h"
|
#include "xenia/kernel/xnotifylistener.h"
|
||||||
#include "xenia/kernel/xobject.h"
|
#include "xenia/kernel/xobject.h"
|
||||||
#include "xenia/kernel/xthread.h"
|
#include "xenia/kernel/xthread.h"
|
||||||
|
#include "xenia/ui/imgui_host_notification.h"
|
||||||
|
|
||||||
|
#include "third_party/crypto/TinySHA1.hpp"
|
||||||
|
|
||||||
DEFINE_bool(apply_title_update, true, "Apply title updates.", "Kernel");
|
DEFINE_bool(apply_title_update, true, "Apply title updates.", "Kernel");
|
||||||
|
|
||||||
|
@ -550,28 +553,59 @@ X_RESULT KernelState::FinishLoadingUserModule(
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
X_RESULT KernelState::ApplyTitleUpdate(const object_ref<UserModule> module) {
|
X_RESULT KernelState::ApplyTitleUpdate(
|
||||||
X_RESULT result = X_STATUS_SUCCESS;
|
const object_ref<UserModule> title_module) {
|
||||||
|
const auto title_updates = FindTitleUpdate(title_module->title_id());
|
||||||
|
if (title_updates.empty()) {
|
||||||
|
return X_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto patch_module = LoadTitleUpdate(&title_updates.front(), title_module);
|
||||||
|
if (!patch_module) {
|
||||||
|
return X_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!patch_module->xex_module()->is_patch()) {
|
||||||
|
return X_STATUS_UNSUCCESSFUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsPatchSignatureProper(title_module, patch_module)) {
|
||||||
|
// First module that is loaded is always main executable. That way we can
|
||||||
|
// prevent random message spam in case of loading/unloading.
|
||||||
|
if (!GetExecutableModule()) {
|
||||||
|
emulator_->display_window()->app_context().CallInUIThread([&]() {
|
||||||
|
new xe::ui::HostNotificationWindow(
|
||||||
|
emulator_->imgui_drawer(), "Warning!",
|
||||||
|
"Title Update signature doesn't match. This can cause unexpected "
|
||||||
|
"issues or crashes!",
|
||||||
|
0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ApplyTitleUpdate(title_module, patch_module);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<xam::XCONTENT_AGGREGATE_DATA> KernelState::FindTitleUpdate(
|
||||||
|
const uint32_t title_id) const {
|
||||||
if (!cvars::apply_title_update) {
|
if (!cvars::apply_title_update) {
|
||||||
return result;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<xam::XCONTENT_AGGREGATE_DATA> tu_list =
|
return content_manager()->ListContent(1, xe::XContentType::kInstaller,
|
||||||
content_manager()->ListContent(1, xe::XContentType::kInstaller,
|
title_id);
|
||||||
module->title_id());
|
}
|
||||||
|
|
||||||
if (tu_list.empty()) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const object_ref<UserModule> KernelState::LoadTitleUpdate(
|
||||||
|
const xam::XCONTENT_AGGREGATE_DATA* title_update,
|
||||||
|
const object_ref<UserModule> module) {
|
||||||
uint32_t disc_number = -1;
|
uint32_t disc_number = -1;
|
||||||
if (module->is_multi_disc_title()) {
|
if (module->is_multi_disc_title()) {
|
||||||
disc_number = module->disc_number();
|
disc_number = module->disc_number();
|
||||||
}
|
}
|
||||||
// TODO(Gliniak): Support for selecting from multiple TUs
|
|
||||||
const xam::XCONTENT_AGGREGATE_DATA& title_update = tu_list.front();
|
|
||||||
X_RESULT open_status =
|
X_RESULT open_status =
|
||||||
content_manager()->OpenContent("UPDATE", title_update, disc_number);
|
content_manager()->OpenContent("UPDATE", *title_update, disc_number);
|
||||||
|
|
||||||
// Use the corresponding patch for the launch module
|
// Use the corresponding patch for the launch module
|
||||||
std::filesystem::path patch_xexp;
|
std::filesystem::path patch_xexp;
|
||||||
|
@ -582,7 +616,7 @@ X_RESULT KernelState::ApplyTitleUpdate(const object_ref<UserModule> module) {
|
||||||
auto is_relative = std::filesystem::relative(module->path(), mount_path);
|
auto is_relative = std::filesystem::relative(module->path(), mount_path);
|
||||||
|
|
||||||
if (is_relative.empty()) {
|
if (is_relative.empty()) {
|
||||||
return X_STATUS_UNSUCCESSFUL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
patch_xexp =
|
patch_xexp =
|
||||||
|
@ -593,22 +627,66 @@ X_RESULT KernelState::ApplyTitleUpdate(const object_ref<UserModule> module) {
|
||||||
xe::vfs::Entry* patch_entry = kernel_state()->file_system()->ResolvePath(
|
xe::vfs::Entry* patch_entry = kernel_state()->file_system()->ResolvePath(
|
||||||
resolved_path + patch_xexp.generic_string());
|
resolved_path + patch_xexp.generic_string());
|
||||||
|
|
||||||
if (patch_entry) {
|
if (!patch_entry) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
const std::string patch_path = patch_entry->absolute_path();
|
const std::string patch_path = patch_entry->absolute_path();
|
||||||
XELOGI("Loading XEX patch from {}", patch_path);
|
XELOGI("Loading XEX patch from {}", patch_path);
|
||||||
auto patch_module = object_ref<UserModule>(new UserModule(this));
|
auto patch_module = object_ref<UserModule>(new UserModule(this));
|
||||||
|
|
||||||
result = patch_module->LoadFromFile(patch_path);
|
X_RESULT result = patch_module->LoadFromFile(patch_path);
|
||||||
if (result != X_STATUS_SUCCESS) {
|
if (result != X_STATUS_SUCCESS) {
|
||||||
XELOGE("Failed to load XEX patch, code: {}", result);
|
XELOGE("Failed to load XEX patch, code: {}", result);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return patch_module;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool KernelState::IsPatchSignatureProper(
|
||||||
|
const object_ref<UserModule> title_module,
|
||||||
|
const object_ref<UserModule> patch_module) const {
|
||||||
|
xex2_opt_delta_patch_descriptor* patch_header = nullptr;
|
||||||
|
patch_module->GetOptHeader(XEX_HEADER_DELTA_PATCH_DESCRIPTOR,
|
||||||
|
reinterpret_cast<void**>(&patch_header));
|
||||||
|
|
||||||
|
assert_not_null(patch_header);
|
||||||
|
|
||||||
|
// Compare hash inside delta descriptor to base XEX signature
|
||||||
|
uint8_t digest[0x14];
|
||||||
|
sha1::SHA1 s;
|
||||||
|
s.processBytes(title_module->xex_module()->xex_security_info()->rsa_signature,
|
||||||
|
0x100);
|
||||||
|
s.finalize(digest);
|
||||||
|
|
||||||
|
if (memcmp(digest, patch_header->digest_source, 0x14) != 0) {
|
||||||
|
XELOGW(
|
||||||
|
"XEX patch signature hash doesn't match base XEX signature hash, patch "
|
||||||
|
"will likely fail!");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
X_RESULT KernelState::ApplyTitleUpdate(
|
||||||
|
const object_ref<UserModule> title_module,
|
||||||
|
const object_ref<UserModule> patch_module) {
|
||||||
|
if (!title_module) {
|
||||||
|
XELOGE("{}: No title_module provided!", __FUNCTION__);
|
||||||
return X_STATUS_UNSUCCESSFUL;
|
return X_STATUS_UNSUCCESSFUL;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = patch_module->xex_module()->ApplyPatch(module->xex_module());
|
if (!patch_module) {
|
||||||
if (result != X_STATUS_SUCCESS) {
|
XELOGE("{}: No patch_module provided!", __FUNCTION__);
|
||||||
XELOGE("Failed to apply XEX patch, code: {}", result);
|
|
||||||
return X_STATUS_UNSUCCESSFUL;
|
return X_STATUS_UNSUCCESSFUL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
X_STATUS result =
|
||||||
|
patch_module->xex_module()->ApplyPatch(title_module->xex_module());
|
||||||
|
if (result != X_STATUS_SUCCESS) {
|
||||||
|
XELOGE("Failed to apply XEX patch, code: {}", result);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -279,7 +279,7 @@ class KernelState {
|
||||||
return object_ref<T>(reinterpret_cast<T*>(module.release()));
|
return object_ref<T>(reinterpret_cast<T*>(module.release()));
|
||||||
}
|
}
|
||||||
|
|
||||||
X_RESULT ApplyTitleUpdate(const object_ref<UserModule> module);
|
X_RESULT ApplyTitleUpdate(const object_ref<UserModule> title_module);
|
||||||
// Terminates a title: Unloads all modules, and kills all guest threads.
|
// Terminates a title: Unloads all modules, and kills all guest threads.
|
||||||
// This DOES NOT RETURN if called from a guest thread!
|
// This DOES NOT RETURN if called from a guest thread!
|
||||||
void TerminateTitle();
|
void TerminateTitle();
|
||||||
|
@ -352,6 +352,18 @@ class KernelState {
|
||||||
void SetProcessTLSVars(X_KPROCESS* process, int num_slots, int tls_data_size,
|
void SetProcessTLSVars(X_KPROCESS* process, int num_slots, int tls_data_size,
|
||||||
int tls_static_data_address);
|
int tls_static_data_address);
|
||||||
void InitializeKernelGuestGlobals();
|
void InitializeKernelGuestGlobals();
|
||||||
|
|
||||||
|
std::vector<xam::XCONTENT_AGGREGATE_DATA> FindTitleUpdate(
|
||||||
|
const uint32_t title_id) const;
|
||||||
|
const object_ref<UserModule> LoadTitleUpdate(
|
||||||
|
const xam::XCONTENT_AGGREGATE_DATA* title_update,
|
||||||
|
const object_ref<UserModule> module);
|
||||||
|
bool IsPatchSignatureProper(const object_ref<UserModule> title_module,
|
||||||
|
const object_ref<UserModule> patch_module) const;
|
||||||
|
|
||||||
|
X_RESULT ApplyTitleUpdate(const object_ref<UserModule> title_module,
|
||||||
|
const object_ref<UserModule> patch_module);
|
||||||
|
|
||||||
Emulator* emulator_;
|
Emulator* emulator_;
|
||||||
Memory* memory_;
|
Memory* memory_;
|
||||||
cpu::Processor* processor_;
|
cpu::Processor* processor_;
|
||||||
|
|
Loading…
Reference in New Issue