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 {
|
||||
|
||||
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() {
|
||||
switch (std::clamp(REXCVAR_GET(audio_worker_thread_priority), -2, 2)) {
|
||||
case -2:
|
||||
|
|
@ -262,7 +277,11 @@ X_STATUS AudioSystem::RegisterClient(uint32_t callback, uint32_t callback_arg, s
|
|||
AudioDriver* driver;
|
||||
auto result = CreateDriver(index, client_semaphore, &driver);
|
||||
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);
|
||||
|
||||
|
|
@ -371,11 +390,11 @@ bool AudioSystem::Restore(stream::ByteStream* stream) {
|
|||
AudioDriver* driver = nullptr;
|
||||
auto status = CreateDriver(id, client_semaphore, &driver);
|
||||
if (XFAILED(status)) {
|
||||
REXAPU_ERROR(
|
||||
REXAPU_WARN(
|
||||
"AudioSystem::Restore - Call to CreateDriver failed with status "
|
||||
"{:08X}",
|
||||
"{:08X}; using silent audio fallback",
|
||||
status);
|
||||
return false;
|
||||
driver = new SilentAudioDriver(memory_, client_semaphore);
|
||||
}
|
||||
|
||||
assert_not_null(driver);
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
|
|
@ -21,8 +22,10 @@
|
|||
#include <fmt/format.h>
|
||||
|
||||
#include <rex/cvar.h>
|
||||
#include <rex/kernel/init.h>
|
||||
#include <rex/logging.h>
|
||||
#include <rex/result.h>
|
||||
#include <rex/runtime.h>
|
||||
#include <rex/version.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
|
|
@ -87,6 +90,53 @@ int main(int argc, char** argv) {
|
|||
rexglue::cli::RegisterInit(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);
|
||||
|
||||
ConfigureLogging(log_level, log_file, verbose);
|
||||
|
|
|
|||
|
|
@ -15,6 +15,9 @@
|
|||
#include "pe/pe_image.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <unordered_map>
|
||||
|
||||
#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 {
|
||||
|
||||
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;
|
||||
|
||||
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_,
|
||||
base_address_ + image_size(), image_size());
|
||||
MaybeDumpLoadedXexImage(*this);
|
||||
|
||||
// Parse any "unsafe" headers into safer variants
|
||||
xex2_opt_generic_u32* alternate_titleids;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue