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. *
|
|
|
|
******************************************************************************
|
|
|
|
*/
|
|
|
|
|
2015-02-01 06:49:47 +00:00
|
|
|
#include "xenia/emulator.h"
|
2013-10-24 03:42:24 +00:00
|
|
|
|
2015-05-27 05:20:46 +00:00
|
|
|
#include <gflags/gflags.h>
|
|
|
|
|
2015-02-01 06:49:47 +00:00
|
|
|
#include "xenia/apu/apu.h"
|
2015-05-02 10:42:51 +00:00
|
|
|
#include "xenia/base/assert.h"
|
2015-05-27 05:20:46 +00:00
|
|
|
#include "xenia/base/clock.h"
|
2015-05-02 10:42:51 +00:00
|
|
|
#include "xenia/base/string.h"
|
2015-02-01 06:49:47 +00:00
|
|
|
#include "xenia/cpu/cpu.h"
|
|
|
|
#include "xenia/gpu/gpu.h"
|
|
|
|
#include "xenia/hid/hid.h"
|
|
|
|
#include "xenia/kernel/kernel.h"
|
|
|
|
#include "xenia/kernel/kernel_state.h"
|
|
|
|
#include "xenia/kernel/modules.h"
|
|
|
|
#include "xenia/kernel/fs/filesystem.h"
|
|
|
|
#include "xenia/memory.h"
|
|
|
|
#include "xenia/ui/main_window.h"
|
2013-10-24 03:42:24 +00:00
|
|
|
|
2015-05-27 05:20:46 +00:00
|
|
|
DEFINE_double(time_scalar, 1.0,
|
|
|
|
"Scalar used to speed or slow time (1x, 2x, 1/2x, etc).");
|
|
|
|
|
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;
|
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)
|
2014-12-20 21:54:55 +00:00
|
|
|
: command_line_(command_line) {}
|
2013-10-24 03:42:24 +00:00
|
|
|
|
|
|
|
Emulator::~Emulator() {
|
|
|
|
// Note that we delete things in the reverse order they were initialized.
|
|
|
|
|
2015-05-24 05:27:43 +00:00
|
|
|
// Kill the debugger first, so that we don't have it messing with things.
|
2015-05-25 03:44:27 +00:00
|
|
|
debugger_->StopSession();
|
2015-05-24 05:27:43 +00:00
|
|
|
|
2015-05-20 05:20:49 +00:00
|
|
|
// Give the systems time to shutdown before we delete them.
|
|
|
|
graphics_system_->Shutdown();
|
|
|
|
audio_system_->Shutdown();
|
2015-06-21 07:25:24 +00:00
|
|
|
xma_decoder_->Shutdown();
|
2013-10-24 03:42:24 +00:00
|
|
|
|
2014-08-21 06:26:46 +00:00
|
|
|
input_system_.reset();
|
|
|
|
graphics_system_.reset();
|
|
|
|
audio_system_.reset();
|
2015-06-21 07:25:24 +00:00
|
|
|
xma_decoder_.reset();
|
2014-01-03 02:58:44 +00:00
|
|
|
|
2015-05-25 03:44:27 +00:00
|
|
|
kernel_state_.reset();
|
|
|
|
file_system_.reset();
|
|
|
|
|
2014-08-21 06:26:46 +00:00
|
|
|
processor_.reset();
|
2013-10-24 03:42:24 +00:00
|
|
|
|
2015-05-25 03:44:27 +00:00
|
|
|
debugger_.reset();
|
|
|
|
|
2014-08-21 06:26:46 +00:00
|
|
|
export_resolver_.reset();
|
2014-12-20 21:54:55 +00:00
|
|
|
|
|
|
|
// Kill the window last, as until the graphics system/etc is dead it's needed.
|
|
|
|
main_window_.reset();
|
2013-10-24 03:42:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
X_STATUS Emulator::Setup() {
|
|
|
|
X_STATUS result = X_STATUS_UNSUCCESSFUL;
|
|
|
|
|
2015-05-27 05:20:46 +00:00
|
|
|
// Initialize clock.
|
|
|
|
// 360 uses a 50MHz clock.
|
2015-05-28 02:04:47 +00:00
|
|
|
Clock::set_guest_tick_frequency(50000000);
|
2015-05-27 05:20:46 +00:00
|
|
|
// We could reset this with save state data/constant value to help replays.
|
|
|
|
Clock::set_guest_system_time_base(Clock::QueryHostSystemTime());
|
|
|
|
// This can be adjusted dynamically, as well.
|
|
|
|
Clock::set_guest_time_scalar(FLAGS_time_scalar);
|
|
|
|
|
|
|
|
// Before we can set thread affinity we must enable the process to use all
|
|
|
|
// logical processors.
|
2015-05-16 06:52:48 +00:00
|
|
|
HANDLE process_handle = GetCurrentProcess();
|
|
|
|
DWORD_PTR process_affinity_mask;
|
|
|
|
DWORD_PTR system_affinity_mask;
|
|
|
|
GetProcessAffinityMask(process_handle, &process_affinity_mask,
|
|
|
|
&system_affinity_mask);
|
|
|
|
SetProcessAffinityMask(process_handle, system_affinity_mask);
|
|
|
|
|
2014-12-20 21:54:55 +00:00
|
|
|
// Create the main window. Other parts will hook into this.
|
2014-12-21 06:17:57 +00:00
|
|
|
main_window_ = std::make_unique<ui::MainWindow>(this);
|
|
|
|
main_window_->Start();
|
2014-12-20 21:54:55 +00:00
|
|
|
|
2013-10-24 03:42:24 +00:00
|
|
|
// Create memory system first, as it is required for other systems.
|
2014-08-21 06:26:46 +00:00
|
|
|
memory_ = std::make_unique<Memory>();
|
2013-12-07 06:57:16 +00:00
|
|
|
result = memory_->Initialize();
|
2014-08-21 06:26:46 +00:00
|
|
|
if (result) {
|
|
|
|
return result;
|
|
|
|
}
|
2013-10-24 03:42:24 +00:00
|
|
|
|
|
|
|
// Shared export resolver used to attach and query for HLE exports.
|
2015-05-02 09:11:11 +00:00
|
|
|
export_resolver_ = std::make_unique<xe::cpu::ExportResolver>();
|
2013-10-24 03:42:24 +00:00
|
|
|
|
2015-05-24 05:27:43 +00:00
|
|
|
// Debugger first, as other parts hook into it.
|
|
|
|
debugger_.reset(new debug::Debugger(this));
|
|
|
|
|
|
|
|
// Create debugger first. Other types hook up to it.
|
|
|
|
debugger_->StartSession();
|
|
|
|
|
2013-10-24 03:42:24 +00:00
|
|
|
// Initialize the CPU.
|
2015-05-24 05:27:43 +00:00
|
|
|
processor_ = std::make_unique<Processor>(
|
|
|
|
memory_.get(), export_resolver_.get(), debugger_.get());
|
2013-10-24 03:42:24 +00:00
|
|
|
|
|
|
|
// Initialize the APU.
|
2014-08-21 06:26:46 +00:00
|
|
|
audio_system_ = std::move(xe::apu::Create(this));
|
|
|
|
if (!audio_system_) {
|
|
|
|
return X_STATUS_NOT_IMPLEMENTED;
|
|
|
|
}
|
2013-10-24 03:42:24 +00:00
|
|
|
|
2015-06-21 07:25:24 +00:00
|
|
|
xma_decoder_ = std::move(std::make_unique<XmaDecoder>(this));
|
|
|
|
|
2013-10-24 03:42:24 +00:00
|
|
|
// Initialize the GPU.
|
2015-05-20 05:20:49 +00:00
|
|
|
graphics_system_ = std::move(xe::gpu::Create(this));
|
2014-08-21 06:26:46 +00:00
|
|
|
if (!graphics_system_) {
|
|
|
|
return X_STATUS_NOT_IMPLEMENTED;
|
|
|
|
}
|
2013-10-24 04:47:36 +00:00
|
|
|
|
|
|
|
// Initialize the HID.
|
2014-08-21 06:26:46 +00:00
|
|
|
input_system_ = std::move(xe::hid::Create(this));
|
|
|
|
if (!input_system_) {
|
|
|
|
return X_STATUS_NOT_IMPLEMENTED;
|
|
|
|
}
|
2014-01-05 01:12:46 +00:00
|
|
|
|
2013-10-24 04:47:36 +00:00
|
|
|
result = input_system_->Setup();
|
2014-08-21 06:26:46 +00:00
|
|
|
if (result) {
|
|
|
|
return result;
|
|
|
|
}
|
2013-10-24 03:42:24 +00:00
|
|
|
|
|
|
|
// Bring up the virtual filesystem used by the kernel.
|
2014-08-21 06:26:46 +00:00
|
|
|
file_system_ = std::make_unique<FileSystem>();
|
2013-10-24 03:42:24 +00:00
|
|
|
|
2014-01-05 01:12:46 +00:00
|
|
|
// Shared kernel state.
|
2015-05-20 05:20:49 +00:00
|
|
|
kernel_state_ = std::make_unique<kernel::KernelState>(this);
|
|
|
|
|
|
|
|
// Setup the core components.
|
|
|
|
if (!processor_->Setup()) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
result = graphics_system_->Setup(processor_.get(), main_window_->loop(),
|
|
|
|
main_window_.get());
|
|
|
|
if (result) {
|
|
|
|
return result;
|
|
|
|
}
|
2014-01-05 01:12:46 +00:00
|
|
|
|
2015-05-19 03:25:35 +00:00
|
|
|
result = audio_system_->Setup();
|
|
|
|
if (result) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2015-06-21 07:25:24 +00:00
|
|
|
result = xma_decoder_->Setup();
|
|
|
|
if (result) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2013-10-24 03:42:24 +00:00
|
|
|
// HLE kernel modules.
|
2015-05-20 05:20:49 +00:00
|
|
|
kernel_state_->LoadKernelModule<kernel::XboxkrnlModule>();
|
|
|
|
kernel_state_->LoadKernelModule<kernel::XamModule>();
|
2013-10-24 03:42:24 +00:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
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;
|
2015-05-02 10:42:51 +00:00
|
|
|
auto last_slash = path.find_last_of(xe::path_separator);
|
2014-08-15 17:19:59 +00:00
|
|
|
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.
|
2015-05-02 10:42:51 +00:00
|
|
|
std::string fs_path = "game:\\" + xe::to_string(file_name);
|
2014-08-15 17:19:59 +00:00
|
|
|
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) {
|
2015-05-25 03:44:27 +00:00
|
|
|
auto xboxkrnl_module = kernel_state_->GetModule("xboxkrnl.exe");
|
|
|
|
auto xboxkrnl = kernel::object_ref<kernel::XboxkrnlModule>(
|
|
|
|
reinterpret_cast<kernel::XboxkrnlModule*>(xboxkrnl_module.release()));
|
2015-05-20 05:20:49 +00:00
|
|
|
int result = xboxkrnl->LaunchModule(module_path.c_str());
|
|
|
|
if (result == 0) {
|
|
|
|
return X_STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
return X_STATUS_UNSUCCESSFUL;
|
|
|
|
}
|
2014-01-19 06:23:26 +00:00
|
|
|
}
|
2014-08-17 20:13:03 +00:00
|
|
|
|
|
|
|
} // namespace xe
|