add XEX dump tooling and silent audio fallback
This commit is contained in:
parent
97ab3e107d
commit
34accdd241
3 changed files with 133 additions and 4 deletions
|
|
@ -37,6 +37,21 @@ REXCVAR_DEFINE_INT32(audio_worker_thread_priority, 0, "Audio",
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
class SilentAudioDriver final : public rex::audio::AudioDriver {
|
||||||
|
public:
|
||||||
|
SilentAudioDriver(rex::memory::Memory* memory, rex::thread::Semaphore* semaphore)
|
||||||
|
: AudioDriver(memory), semaphore_(semaphore) {}
|
||||||
|
|
||||||
|
void SubmitFrame(uint32_t /*samples_ptr*/) override {
|
||||||
|
if (semaphore_) {
|
||||||
|
semaphore_->Release(1, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
rex::thread::Semaphore* semaphore_;
|
||||||
|
};
|
||||||
|
|
||||||
int32_t AudioWorkerNativePriority() {
|
int32_t AudioWorkerNativePriority() {
|
||||||
switch (std::clamp(REXCVAR_GET(audio_worker_thread_priority), -2, 2)) {
|
switch (std::clamp(REXCVAR_GET(audio_worker_thread_priority), -2, 2)) {
|
||||||
case -2:
|
case -2:
|
||||||
|
|
@ -262,7 +277,11 @@ X_STATUS AudioSystem::RegisterClient(uint32_t callback, uint32_t callback_arg, s
|
||||||
AudioDriver* driver;
|
AudioDriver* driver;
|
||||||
auto result = CreateDriver(index, client_semaphore, &driver);
|
auto result = CreateDriver(index, client_semaphore, &driver);
|
||||||
if (XFAILED(result)) {
|
if (XFAILED(result)) {
|
||||||
return result;
|
REXAPU_WARN(
|
||||||
|
"AudioSystem::RegisterClient: audio driver creation failed with status {:08X}; "
|
||||||
|
"using silent audio fallback",
|
||||||
|
result);
|
||||||
|
driver = new SilentAudioDriver(memory_, client_semaphore);
|
||||||
}
|
}
|
||||||
assert_not_null(driver);
|
assert_not_null(driver);
|
||||||
|
|
||||||
|
|
@ -371,11 +390,11 @@ bool AudioSystem::Restore(stream::ByteStream* stream) {
|
||||||
AudioDriver* driver = nullptr;
|
AudioDriver* driver = nullptr;
|
||||||
auto status = CreateDriver(id, client_semaphore, &driver);
|
auto status = CreateDriver(id, client_semaphore, &driver);
|
||||||
if (XFAILED(status)) {
|
if (XFAILED(status)) {
|
||||||
REXAPU_ERROR(
|
REXAPU_WARN(
|
||||||
"AudioSystem::Restore - Call to CreateDriver failed with status "
|
"AudioSystem::Restore - Call to CreateDriver failed with status "
|
||||||
"{:08X}",
|
"{:08X}; using silent audio fallback",
|
||||||
status);
|
status);
|
||||||
return false;
|
driver = new SilentAudioDriver(memory_, client_semaphore);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert_not_null(driver);
|
assert_not_null(driver);
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <filesystem>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
|
@ -21,8 +22,10 @@
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
|
||||||
#include <rex/cvar.h>
|
#include <rex/cvar.h>
|
||||||
|
#include <rex/kernel/init.h>
|
||||||
#include <rex/logging.h>
|
#include <rex/logging.h>
|
||||||
#include <rex/result.h>
|
#include <rex/result.h>
|
||||||
|
#include <rex/runtime.h>
|
||||||
#include <rex/version.h>
|
#include <rex/version.h>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
@ -87,6 +90,53 @@ int main(int argc, char** argv) {
|
||||||
rexglue::cli::RegisterInit(app, ctx, pending);
|
rexglue::cli::RegisterInit(app, ctx, pending);
|
||||||
rexglue::cli::RegisterRecompileTests(app, ctx, pending);
|
rexglue::cli::RegisterRecompileTests(app, ctx, pending);
|
||||||
|
|
||||||
|
std::string dump_xex_path;
|
||||||
|
std::string dump_output_dir;
|
||||||
|
auto* dump_xex =
|
||||||
|
app.add_subcommand("dump-xex", "Load, decrypt/decompress and dump a XEX image");
|
||||||
|
dump_xex->add_option("xex", dump_xex_path, "Path to the XEX file")->required();
|
||||||
|
dump_xex->add_option("output", dump_output_dir, "Directory for decoded image dumps")->required();
|
||||||
|
dump_xex->callback([&] {
|
||||||
|
pending = [&]() -> rex::Result<void> {
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
std::error_code ec;
|
||||||
|
fs::path xex_path = fs::weakly_canonical(fs::absolute(dump_xex_path), ec);
|
||||||
|
if (ec || !fs::is_regular_file(xex_path)) {
|
||||||
|
return rex::Err<void>(rex::ErrorCategory::IO,
|
||||||
|
fmt::format("XEX file not found: {}", dump_xex_path));
|
||||||
|
}
|
||||||
|
fs::path output_dir = fs::weakly_canonical(fs::absolute(dump_output_dir), ec);
|
||||||
|
if (ec) {
|
||||||
|
output_dir = fs::absolute(dump_output_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
_putenv_s("REXGLUE_DUMP_XEX_IMAGE_DIR", output_dir.string().c_str());
|
||||||
|
#else
|
||||||
|
setenv("REXGLUE_DUMP_XEX_IMAGE_DIR", output_dir.string().c_str(), 1);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
rex::Runtime runtime(xex_path.parent_path(), {}, {}, output_dir);
|
||||||
|
rex::X_STATUS status = runtime.Setup(rex::RuntimeConfig{
|
||||||
|
.kernel_init = rex::kernel::InitializeKernel,
|
||||||
|
.tool_mode = true,
|
||||||
|
});
|
||||||
|
if (status != 0) {
|
||||||
|
return rex::Err<void>(rex::ErrorCategory::IO,
|
||||||
|
fmt::format("Failed to initialize runtime: {:#x}", status));
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string guest_path = "game:\\" + xex_path.filename().string();
|
||||||
|
status = runtime.LoadXexImage(guest_path);
|
||||||
|
runtime.Shutdown();
|
||||||
|
if (status != 0) {
|
||||||
|
return rex::Err<void>(rex::ErrorCategory::IO,
|
||||||
|
fmt::format("Failed to load XEX image: {:#x}", status));
|
||||||
|
}
|
||||||
|
return rex::Ok();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
CLI11_PARSE(app, argc, argv);
|
CLI11_PARSE(app, argc, argv);
|
||||||
|
|
||||||
ConfigureLogging(log_level, log_file, verbose);
|
ConfigureLogging(log_level, log_file, verbose);
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,9 @@
|
||||||
#include "pe/pe_image.h"
|
#include "pe/pe_image.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
|
|
@ -58,6 +61,62 @@ void aes_decrypt_buffer(const uint8_t* session_key, const uint8_t* input_buffer,
|
||||||
|
|
||||||
namespace rex::runtime {
|
namespace rex::runtime {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
std::string DumpSafeModuleName(std::string_view name) {
|
||||||
|
std::string safe;
|
||||||
|
safe.reserve(name.size());
|
||||||
|
for (char ch : name) {
|
||||||
|
const bool allowed = (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') ||
|
||||||
|
(ch >= '0' && ch <= '9') || ch == '-' || ch == '_' || ch == '.';
|
||||||
|
safe.push_back(allowed ? ch : '_');
|
||||||
|
}
|
||||||
|
return safe.empty() ? "module" : safe;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaybeDumpLoadedXexImage(const XexModule& module) {
|
||||||
|
const char* dump_dir_value = std::getenv("REXGLUE_DUMP_XEX_IMAGE_DIR");
|
||||||
|
if (!dump_dir_value || !*dump_dir_value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::error_code ec;
|
||||||
|
const std::filesystem::path dump_dir = std::filesystem::path(dump_dir_value);
|
||||||
|
std::filesystem::create_directories(dump_dir, ec);
|
||||||
|
if (ec) {
|
||||||
|
REXLOG_WARN("Failed to create XEX dump directory {}: {}", dump_dir.string(), ec.message());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string safe_name = DumpSafeModuleName(module.name());
|
||||||
|
const std::filesystem::path image_path =
|
||||||
|
dump_dir / fmt::format("{}_{:08X}_{:08X}.bin", safe_name, module.base_address(),
|
||||||
|
module.image_size());
|
||||||
|
std::ofstream image_file(image_path, std::ios::binary);
|
||||||
|
if (!image_file) {
|
||||||
|
REXLOG_WARN("Failed to open XEX image dump {}", image_path.string());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t* image_data = module.memory()->TranslateVirtual(module.base_address());
|
||||||
|
image_file.write(reinterpret_cast<const char*>(image_data), module.image_size());
|
||||||
|
image_file.close();
|
||||||
|
|
||||||
|
const std::filesystem::path meta_path =
|
||||||
|
dump_dir / fmt::format("{}_{:08X}_{:08X}.txt", safe_name, module.base_address(),
|
||||||
|
module.image_size());
|
||||||
|
std::ofstream meta_file(meta_path);
|
||||||
|
if (meta_file) {
|
||||||
|
meta_file << "name=" << module.name() << "\n";
|
||||||
|
meta_file << fmt::format("base=0x{:08X}\n", module.base_address());
|
||||||
|
meta_file << fmt::format("size=0x{:08X}\n", module.image_size());
|
||||||
|
meta_file << fmt::format("entry=0x{:08X}\n", module.entry_point());
|
||||||
|
}
|
||||||
|
REXLOG_INFO("Dumped loaded XEX image {} to {}", module.name(), image_path.string());
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
using rex::system::KernelState;
|
using rex::system::KernelState;
|
||||||
|
|
||||||
XexModule::XexModule(FunctionDispatcher* function_dispatcher, KernelState* kernel_state)
|
XexModule::XexModule(FunctionDispatcher* function_dispatcher, KernelState* kernel_state)
|
||||||
|
|
@ -932,6 +991,7 @@ bool XexModule::LoadContinue() {
|
||||||
}
|
}
|
||||||
REXLOG_INFO("XEX image loaded at {:08X}-{:08X} (size {:08X})", base_address_,
|
REXLOG_INFO("XEX image loaded at {:08X}-{:08X} (size {:08X})", base_address_,
|
||||||
base_address_ + image_size(), image_size());
|
base_address_ + image_size(), image_size());
|
||||||
|
MaybeDumpLoadedXexImage(*this);
|
||||||
|
|
||||||
// Parse any "unsafe" headers into safer variants
|
// Parse any "unsafe" headers into safer variants
|
||||||
xex2_opt_generic_u32* alternate_titleids;
|
xex2_opt_generic_u32* alternate_titleids;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue