From 1ba5dd5eb1f78b1c018886f9dbb1c07245eb1262 Mon Sep 17 00:00:00 2001 From: gibbed Date: Wed, 21 Nov 2018 18:04:43 -0600 Subject: [PATCH] Use platform-specific user directory to store content. Create a file named portable.txt next to xenia.exe to restore previous behavior. --- src/xenia/app/xenia_main.cc | 31 +++++++++++++++++++++++++++++- src/xenia/base/filesystem.h | 9 +++++++++ src/xenia/base/filesystem_posix.cc | 15 +++++++++++++++ src/xenia/base/filesystem_win.cc | 22 +++++++++++++++++++++ src/xenia/emulator.cc | 5 +++-- src/xenia/emulator.h | 8 +++++++- src/xenia/gpu/trace_dump.cc | 2 +- src/xenia/gpu/trace_viewer.cc | 2 +- src/xenia/kernel/kernel_state.cc | 4 +--- 9 files changed, 89 insertions(+), 9 deletions(-) diff --git a/src/xenia/app/xenia_main.cc b/src/xenia/app/xenia_main.cc index def29b7c5..ee474485b 100644 --- a/src/xenia/app/xenia_main.cc +++ b/src/xenia/app/xenia_main.cc @@ -44,6 +44,8 @@ DEFINE_string(hid, "any", "Input system. Use: [any, nop, winkey, xinput]"); DEFINE_string(target, "", "Specifies the target .xex or .iso to execute."); DEFINE_bool(fullscreen, false, "Toggles fullscreen"); +DEFINE_string(content_root, "", "Root path for content (save/etc) storage."); + DEFINE_bool(mount_scratch, false, "Enable scratch mount"); DEFINE_bool(mount_cache, false, "Enable cache mount"); @@ -129,8 +131,35 @@ int xenia_main(const std::vector& args) { Profiler::Initialize(); Profiler::ThreadEnter("main"); + // Figure out where content should go. + std::wstring content_root; + if (!FLAGS_content_root.empty()) { + content_root = xe::to_wstring(FLAGS_content_root); + } else { + auto base_path = xe::filesystem::GetExecutableFolder(); + base_path = xe::to_absolute_path(base_path); + + auto portable_path = xe::join_paths(base_path, L"portable.txt"); + if (xe::filesystem::PathExists(portable_path)) { + content_root = xe::join_paths(base_path, L"content"); + } else { + content_root = xe::filesystem::GetUserFolder(); +#if defined(XE_PLATFORM_WIN32) + content_root = xe::join_paths(content_root, L"Xenia"); +#elif defined(XE_PLATFORM_LINUX) + content_root = xe::join_paths(content_root, L".xenia"); +#else +#warning Unhandled platform for content root. + content_root = xe::join_paths(content_root, L"Xenia"); +#endif + content_root = xe::join_paths(content_root, L"content"); + } + + content_root = xe::to_absolute_path(content_root); + } + // Create the emulator but don't initialize so we can setup the window. - auto emulator = std::make_unique(L""); + auto emulator = std::make_unique(L"", content_root); // Main emulator display window. auto emulator_window = EmulatorWindow::Create(emulator.get()); diff --git a/src/xenia/base/filesystem.h b/src/xenia/base/filesystem.h index f2f2684cf..8cce19719 100644 --- a/src/xenia/base/filesystem.h +++ b/src/xenia/base/filesystem.h @@ -20,6 +20,15 @@ namespace xe { namespace filesystem { +// Get executable path. +std::wstring GetExecutablePath(); + +// Get executable folder. +std::wstring GetExecutableFolder(); + +// Get user folder. +std::wstring GetUserFolder(); + // Canonicalizes a path, removing ..'s. std::string CanonicalizePath(const std::string& original_path); diff --git a/src/xenia/base/filesystem_posix.cc b/src/xenia/base/filesystem_posix.cc index c3c2eaf80..66a90d56f 100644 --- a/src/xenia/base/filesystem_posix.cc +++ b/src/xenia/base/filesystem_posix.cc @@ -21,6 +21,21 @@ namespace xe { namespace filesystem { +std::wstring GetExecutablePath() { + assert_always(); // IMPLEMENT ME. + return std::wstring(); +} + +std::wstring GetExecutableFolder() { + assert_always(); // IMPLEMENT ME. + return std::wstring(); +} + +std::wstring GetUserFolder() { + assert_always(); // IMPLEMENT ME. + return std::wstring(); +} + bool PathExists(const std::wstring& path) { struct stat st; return stat(xe::to_string(path).c_str(), &st) == 0; diff --git a/src/xenia/base/filesystem_win.cc b/src/xenia/base/filesystem_win.cc index c72ebea07..25b0c319d 100644 --- a/src/xenia/base/filesystem_win.cc +++ b/src/xenia/base/filesystem_win.cc @@ -12,11 +12,33 @@ #include +#include + #include "xenia/base/platform_win.h" namespace xe { namespace filesystem { +std::wstring GetExecutablePath() { + wchar_t* path; + auto error = _get_wpgmptr(&path); + return !error ? std::wstring(path) : std::wstring(); +} + +std::wstring GetExecutableFolder() { + auto path = GetExecutablePath(); + return xe::find_base_path(path); +} + +std::wstring GetUserFolder() { + wchar_t path[MAX_PATH]; + if (!SUCCEEDED(SHGetFolderPathW(nullptr, CSIDL_MYDOCUMENTS, nullptr, + SHGFP_TYPE_CURRENT, path))) { + return std::wstring(); + } + return std::wstring(path); +} + bool PathExists(const std::wstring& path) { DWORD attrib = GetFileAttributes(path.c_str()); return attrib != INVALID_FILE_ATTRIBUTES; diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc index 19a82a85e..8030a8078 100644 --- a/src/xenia/emulator.cc +++ b/src/xenia/emulator.cc @@ -48,8 +48,9 @@ DEFINE_double(time_scalar, 1.0, namespace xe { -Emulator::Emulator(const std::wstring& command_line) - : command_line_(command_line) {} +Emulator::Emulator(const std::wstring& command_line, + const std::wstring& content_root) + : command_line_(command_line), content_root_(content_root) {} Emulator::~Emulator() { // Note that we delete things in the reverse order they were initialized. diff --git a/src/xenia/emulator.h b/src/xenia/emulator.h index 4492419e7..a60b4e2e7 100644 --- a/src/xenia/emulator.h +++ b/src/xenia/emulator.h @@ -47,12 +47,16 @@ namespace xe { // This is responsible for initializing and managing all the various subsystems. class Emulator { public: - explicit Emulator(const std::wstring& command_line); + explicit Emulator(const std::wstring& command_line, + const std::wstring& content_root); ~Emulator(); // Full command line used when launching the process. const std::wstring& command_line() const { return command_line_; } + // Folder content is stored in. + const std::wstring& content_root() const { return content_root_; } + // Title of the game in the default language. const std::wstring& game_title() const { return game_title_; } @@ -154,6 +158,8 @@ class Emulator { const std::string& module_path); std::wstring command_line_; + std::wstring content_root_; + std::wstring game_title_; ui::Window* display_window_; diff --git a/src/xenia/gpu/trace_dump.cc b/src/xenia/gpu/trace_dump.cc index 8c3de28be..66df289b7 100644 --- a/src/xenia/gpu/trace_dump.cc +++ b/src/xenia/gpu/trace_dump.cc @@ -103,7 +103,7 @@ int TraceDump::Main(const std::vector& args) { bool TraceDump::Setup() { // Create the emulator but don't initialize so we can setup the window. - emulator_ = std::make_unique(L""); + emulator_ = std::make_unique(L"", L""); X_STATUS result = emulator_->Setup( nullptr, nullptr, [this]() { return CreateGraphicsSystem(); }, nullptr); if (XFAILED(result)) { diff --git a/src/xenia/gpu/trace_viewer.cc b/src/xenia/gpu/trace_viewer.cc index 4e08e7081..5549e358c 100644 --- a/src/xenia/gpu/trace_viewer.cc +++ b/src/xenia/gpu/trace_viewer.cc @@ -122,7 +122,7 @@ bool TraceViewer::Setup() { window_->Resize(1920, 1200); // Create the emulator but don't initialize so we can setup the window. - emulator_ = std::make_unique(L""); + emulator_ = std::make_unique(L"", L""); X_STATUS result = emulator_->Setup(window_.get(), nullptr, [this]() { return CreateGraphicsSystem(); }, nullptr); diff --git a/src/xenia/kernel/kernel_state.cc b/src/xenia/kernel/kernel_state.cc index 5cb8be150..aef2b9bc8 100644 --- a/src/xenia/kernel/kernel_state.cc +++ b/src/xenia/kernel/kernel_state.cc @@ -31,8 +31,6 @@ DEFINE_bool(headless, false, "Don't display any UI, using defaults for prompts as needed."); -DEFINE_string(content_root, "content", - "Root path for content (save/etc) storage."); namespace xe { namespace kernel { @@ -57,7 +55,7 @@ KernelState::KernelState(Emulator* emulator) app_manager_ = std::make_unique(); user_profile_ = std::make_unique(); - auto content_root = xe::to_wstring(FLAGS_content_root); + auto content_root = emulator_->content_root(); content_root = xe::to_absolute_path(content_root); content_manager_ = std::make_unique(this, content_root);