2013-10-24 03:42:24 +00:00
|
|
|
/**
|
|
|
|
******************************************************************************
|
|
|
|
* 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. *
|
|
|
|
******************************************************************************
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <xenia/emulator.h>
|
|
|
|
|
2014-08-15 17:19:59 +00:00
|
|
|
#include <poly/poly.h>
|
2014-08-15 03:28:44 +00:00
|
|
|
#include <xdb/protocol.h>
|
2013-10-24 03:42:24 +00:00
|
|
|
#include <xenia/apu/apu.h>
|
|
|
|
#include <xenia/cpu/cpu.h>
|
2013-12-07 06:57:16 +00:00
|
|
|
#include <xenia/cpu/xenon_memory.h>
|
2013-10-24 03:42:24 +00:00
|
|
|
#include <xenia/gpu/gpu.h>
|
2013-10-24 04:47:36 +00:00
|
|
|
#include <xenia/hid/hid.h>
|
2013-10-24 03:42:24 +00:00
|
|
|
#include <xenia/kernel/kernel.h>
|
2014-01-05 01:12:46 +00:00
|
|
|
#include <xenia/kernel/kernel_state.h>
|
2013-10-24 03:42:24 +00:00
|
|
|
#include <xenia/kernel/modules.h>
|
2014-01-05 01:12:46 +00:00
|
|
|
#include <xenia/kernel/fs/filesystem.h>
|
2014-01-15 06:40:02 +00:00
|
|
|
#include <xenia/ui/window.h>
|
2013-10-24 03:42:24 +00:00
|
|
|
|
2014-08-17 20:13:03 +00:00
|
|
|
namespace xe {
|
2013-10-24 03:42:24 +00:00
|
|
|
|
|
|
|
using namespace xe::apu;
|
|
|
|
using namespace xe::cpu;
|
|
|
|
using namespace xe::gpu;
|
2013-10-24 04:47:36 +00:00
|
|
|
using namespace xe::hid;
|
2013-10-24 03:42:24 +00:00
|
|
|
using namespace xe::kernel;
|
2014-01-05 01:12:46 +00:00
|
|
|
using namespace xe::kernel::fs;
|
2014-01-15 06:40:02 +00:00
|
|
|
using namespace xe::ui;
|
2013-10-24 03:42:24 +00:00
|
|
|
|
2014-08-16 23:46:20 +00:00
|
|
|
Emulator::Emulator(const std::wstring& command_line)
|
|
|
|
: command_line_(command_line),
|
|
|
|
main_window_(0),
|
|
|
|
memory_(0),
|
|
|
|
processor_(0),
|
|
|
|
audio_system_(0),
|
|
|
|
graphics_system_(0),
|
|
|
|
input_system_(0),
|
|
|
|
export_resolver_(0),
|
|
|
|
file_system_(0),
|
|
|
|
kernel_state_(0),
|
|
|
|
xam_(0),
|
|
|
|
xboxkrnl_(0) {}
|
2013-10-24 03:42:24 +00:00
|
|
|
|
|
|
|
Emulator::~Emulator() {
|
|
|
|
// Note that we delete things in the reverse order they were initialized.
|
|
|
|
|
2014-08-15 03:28:44 +00:00
|
|
|
auto ev = xdb::protocol::ProcessExitEvent::Append(memory()->trace_base());
|
|
|
|
if (ev) {
|
|
|
|
ev->type = xdb::protocol::EventType::PROCESS_EXIT;
|
|
|
|
}
|
|
|
|
|
2014-01-15 06:40:02 +00:00
|
|
|
if (main_window_) {
|
|
|
|
main_window_->Close();
|
|
|
|
}
|
|
|
|
|
2014-08-15 03:28:44 +00:00
|
|
|
debug_agent_.reset();
|
|
|
|
|
2013-10-24 03:42:24 +00:00
|
|
|
delete xam_;
|
|
|
|
delete xboxkrnl_;
|
2014-01-05 01:12:46 +00:00
|
|
|
delete kernel_state_;
|
2013-10-24 03:42:24 +00:00
|
|
|
|
|
|
|
delete file_system_;
|
|
|
|
|
2013-10-24 04:47:36 +00:00
|
|
|
delete input_system_;
|
2014-01-03 02:58:44 +00:00
|
|
|
|
|
|
|
// Give the systems time to shutdown before we delete them.
|
|
|
|
graphics_system_->Shutdown();
|
|
|
|
audio_system_->Shutdown();
|
2013-10-24 03:42:24 +00:00
|
|
|
delete graphics_system_;
|
|
|
|
delete audio_system_;
|
2014-01-03 02:58:44 +00:00
|
|
|
|
2013-10-24 03:42:24 +00:00
|
|
|
delete processor_;
|
|
|
|
|
|
|
|
delete export_resolver_;
|
|
|
|
}
|
|
|
|
|
|
|
|
X_STATUS Emulator::Setup() {
|
|
|
|
X_STATUS result = X_STATUS_UNSUCCESSFUL;
|
|
|
|
|
2014-08-15 03:28:44 +00:00
|
|
|
debug_agent_.reset(new DebugAgent(this));
|
|
|
|
result = debug_agent_->Initialize();
|
|
|
|
XEEXPECTZERO(result);
|
|
|
|
|
2013-10-24 03:42:24 +00:00
|
|
|
// Create memory system first, as it is required for other systems.
|
2013-12-07 06:57:16 +00:00
|
|
|
memory_ = new XenonMemory();
|
2013-10-24 03:42:24 +00:00
|
|
|
XEEXPECTNOTNULL(memory_);
|
2013-12-07 06:57:16 +00:00
|
|
|
result = memory_->Initialize();
|
|
|
|
XEEXPECTZERO(result);
|
2014-08-15 03:28:44 +00:00
|
|
|
memory_->set_trace_base(debug_agent_->trace_base());
|
2013-10-24 03:42:24 +00:00
|
|
|
|
|
|
|
// Shared export resolver used to attach and query for HLE exports.
|
|
|
|
export_resolver_ = new ExportResolver();
|
|
|
|
XEEXPECTNOTNULL(export_resolver_);
|
|
|
|
|
|
|
|
// Initialize the CPU.
|
2013-12-07 06:57:16 +00:00
|
|
|
processor_ = new Processor(this);
|
2013-10-24 03:42:24 +00:00
|
|
|
XEEXPECTNOTNULL(processor_);
|
|
|
|
|
|
|
|
// Initialize the APU.
|
|
|
|
audio_system_ = xe::apu::Create(this);
|
|
|
|
XEEXPECTNOTNULL(audio_system_);
|
|
|
|
|
|
|
|
// Initialize the GPU.
|
|
|
|
graphics_system_ = xe::gpu::Create(this);
|
|
|
|
XEEXPECTNOTNULL(graphics_system_);
|
2013-10-24 04:47:36 +00:00
|
|
|
|
|
|
|
// Initialize the HID.
|
|
|
|
input_system_ = xe::hid::Create(this);
|
|
|
|
XEEXPECTNOTNULL(input_system_);
|
2014-01-05 01:12:46 +00:00
|
|
|
|
2013-10-24 03:42:24 +00:00
|
|
|
// Setup the core components.
|
|
|
|
result = processor_->Setup();
|
|
|
|
XEEXPECTZERO(result);
|
|
|
|
result = audio_system_->Setup();
|
|
|
|
XEEXPECTZERO(result);
|
|
|
|
result = graphics_system_->Setup();
|
|
|
|
XEEXPECTZERO(result);
|
2013-10-24 04:47:36 +00:00
|
|
|
result = input_system_->Setup();
|
|
|
|
XEEXPECTZERO(result);
|
2013-10-24 03:42:24 +00:00
|
|
|
|
|
|
|
// Bring up the virtual filesystem used by the kernel.
|
|
|
|
file_system_ = new FileSystem();
|
|
|
|
XEEXPECTNOTNULL(file_system_);
|
|
|
|
|
2014-01-05 01:12:46 +00:00
|
|
|
// Shared kernel state.
|
|
|
|
kernel_state_ = new KernelState(this);
|
|
|
|
XEEXPECTNOTNULL(kernel_state_);
|
|
|
|
|
2013-10-24 03:42:24 +00:00
|
|
|
// HLE kernel modules.
|
2014-01-05 01:12:46 +00:00
|
|
|
xboxkrnl_ = new XboxkrnlModule(this, kernel_state_);
|
2013-10-24 03:42:24 +00:00
|
|
|
XEEXPECTNOTNULL(xboxkrnl_);
|
2014-01-05 01:12:46 +00:00
|
|
|
xam_ = new XamModule(this, kernel_state_);
|
2013-10-24 03:42:24 +00:00
|
|
|
XEEXPECTNOTNULL(xam_);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
|
|
|
XECLEANUP:
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2014-01-15 15:31:21 +00:00
|
|
|
void Emulator::set_main_window(Window* window) {
|
2014-07-12 23:51:52 +00:00
|
|
|
assert_null(main_window_);
|
2014-01-15 15:31:21 +00:00
|
|
|
main_window_ = window;
|
|
|
|
|
|
|
|
window->closed.AddListener([](UIEvent& e) {
|
|
|
|
// TODO(benvanik): call module API to kill? this is a bad shutdown.
|
|
|
|
exit(1);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2014-08-15 17:19:59 +00:00
|
|
|
X_STATUS Emulator::LaunchXexFile(const std::wstring& path) {
|
2013-10-24 03:42:24 +00:00
|
|
|
// We create a virtual filesystem pointing to its directory and symlink
|
|
|
|
// that to the game filesystem.
|
|
|
|
// e.g., /my/files/foo.xex will get a local fs at:
|
|
|
|
// \\Device\\Harddisk0\\Partition1
|
|
|
|
// and then get that symlinked to game:\, so
|
|
|
|
// -> game:\foo.xex
|
|
|
|
|
2014-08-15 17:19:59 +00:00
|
|
|
int result_code =
|
|
|
|
file_system_->InitializeFromPath(FileSystemType::XEX_FILE, path);
|
|
|
|
if (result_code) {
|
|
|
|
return X_STATUS_INVALID_PARAMETER;
|
2014-08-15 03:28:44 +00:00
|
|
|
}
|
|
|
|
|
2013-10-24 03:42:24 +00:00
|
|
|
// Get just the filename (foo.xex).
|
2014-08-15 17:19:59 +00:00
|
|
|
std::wstring file_name;
|
|
|
|
auto last_slash = path.find_last_of(poly::path_separator);
|
|
|
|
if (last_slash == std::string::npos) {
|
2013-10-24 03:42:24 +00:00
|
|
|
// No slash found, whole thing is a file.
|
|
|
|
file_name = path;
|
2014-08-15 17:19:59 +00:00
|
|
|
} else {
|
|
|
|
// Skip slash.
|
|
|
|
file_name = path.substr(last_slash + 1);
|
2013-10-24 03:42:24 +00:00
|
|
|
}
|
|
|
|
|
2014-08-15 17:19:59 +00:00
|
|
|
// Launch the game.
|
|
|
|
std::string fs_path = "game:\\" + poly::to_string(file_name);
|
|
|
|
return CompleteLaunch(path, fs_path);
|
|
|
|
}
|
2013-10-24 03:42:24 +00:00
|
|
|
|
2014-08-15 17:19:59 +00:00
|
|
|
X_STATUS Emulator::LaunchDiscImage(const std::wstring& path) {
|
|
|
|
int result_code =
|
|
|
|
file_system_->InitializeFromPath(FileSystemType::DISC_IMAGE, path);
|
2013-10-24 03:42:24 +00:00
|
|
|
if (result_code) {
|
2014-08-15 17:19:59 +00:00
|
|
|
return X_STATUS_INVALID_PARAMETER;
|
2013-10-24 03:42:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Launch the game.
|
2014-08-15 17:19:59 +00:00
|
|
|
return CompleteLaunch(path, "game:\\default.xex");
|
2013-10-24 03:42:24 +00:00
|
|
|
}
|
|
|
|
|
2014-08-15 17:19:59 +00:00
|
|
|
X_STATUS Emulator::LaunchSTFSTitle(const std::wstring& path) {
|
|
|
|
int result_code =
|
|
|
|
file_system_->InitializeFromPath(FileSystemType::STFS_TITLE, path);
|
2013-10-24 03:42:24 +00:00
|
|
|
if (result_code) {
|
2014-08-15 17:19:59 +00:00
|
|
|
return X_STATUS_INVALID_PARAMETER;
|
2013-10-24 03:42:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Launch the game.
|
2014-08-15 17:19:59 +00:00
|
|
|
return CompleteLaunch(path, "game:\\default.xex");
|
2013-10-24 03:42:24 +00:00
|
|
|
}
|
2014-01-19 06:23:26 +00:00
|
|
|
|
2014-08-15 17:19:59 +00:00
|
|
|
X_STATUS Emulator::CompleteLaunch(const std::wstring& path,
|
|
|
|
const std::string& module_path) {
|
2014-08-15 03:28:44 +00:00
|
|
|
auto ev = xdb::protocol::ProcessStartEvent::Append(memory()->trace_base());
|
|
|
|
if (ev) {
|
|
|
|
ev->type = xdb::protocol::EventType::PROCESS_START;
|
2014-08-15 17:19:59 +00:00
|
|
|
ev->membase = reinterpret_cast<uint64_t>(memory()->membase());
|
|
|
|
auto path_length = poly::to_string(path)
|
|
|
|
.copy(ev->launch_path, sizeof(ev->launch_path) - 1);
|
|
|
|
ev->launch_path[path_length] = 0;
|
2014-08-15 03:28:44 +00:00
|
|
|
}
|
|
|
|
|
2014-08-15 17:19:59 +00:00
|
|
|
return xboxkrnl_->LaunchModule(module_path.c_str());
|
2014-01-19 06:23:26 +00:00
|
|
|
}
|
2014-08-17 20:13:03 +00:00
|
|
|
|
|
|
|
} // namespace xe
|