From 498c662c61ccc530b51910bb085c60c34082878a Mon Sep 17 00:00:00 2001 From: Triang3l Date: Sat, 19 Jun 2021 19:38:04 +0300 Subject: [PATCH 01/15] [Docs] Add libx11-xcb-dev to the dependencies --- docs/building.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/building.md b/docs/building.md index 0a70fb206..a02cd989d 100644 --- a/docs/building.md +++ b/docs/building.md @@ -99,7 +99,7 @@ Clang-9 or newer should be available from system repositories on all up to date You will also need some development libraries. To get them on an Ubuntu system: ```bash -sudo apt-get install libgtk-3-dev libpthread-stubs0-dev liblz4-dev libx11-dev libvulkan-dev libsdl2-dev libiberty-dev libunwind-dev libc++-dev libc++abi-dev +sudo apt-get install libgtk-3-dev libpthread-stubs0-dev liblz4-dev libx11-dev libx11-xcb-dev libvulkan-dev libsdl2-dev libiberty-dev libunwind-dev libc++-dev libc++abi-dev ``` In addition, you will need up to date Vulkan libraries and drivers for your hardware, which most distributions have in their standard repositories nowadays. From dcffc1a4d5207bba49fba68b60c6218efc23a600 Mon Sep 17 00:00:00 2001 From: Gliniak Date: Wed, 23 Jun 2021 23:21:01 +0200 Subject: [PATCH 02/15] [Base] Remove multiple trailing separators in path --- src/xenia/base/utf8.cc | 59 ++++++++++++++++++++++-------------------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/src/xenia/base/utf8.cc b/src/xenia/base/utf8.cc index 308117477..3f4775b7a 100644 --- a/src/xenia/base/utf8.cc +++ b/src/xenia/base/utf8.cc @@ -582,43 +582,40 @@ std::string find_name_from_path(const std::string_view path, auto it = end; --it; - // path is padded with separator - size_t padding = 0; - if (*it == uint32_t(separator)) { + // skip trailing separators at the end of the path + while (*it == uint32_t(separator)) { if (it == begin) { + // path is all separators, name is empty return std::string(); } --it; - padding = 1; } - // path is just separator - if (it == begin) { - return std::string(); - } + // update end so it is before any trailing separators + end = std::next(it); - // search for separator - while (it != begin) { - if (*it == uint32_t(separator)) { + // skip non-separators + while (*it != uint32_t(separator)) { + if (it == begin) { break; } --it; } - // no separator -- copy entire string (except trailing separator) - if (it == begin) { - return std::string(path.substr(0, path.size() - padding)); + // if the iterator is on a separator, advance + if (*it == uint32_t(separator)) { + ++it; } - auto length = byte_length(std::next(it), end); - auto offset = path.length() - length; - return std::string(path.substr(offset, length - padding)); + auto offset = byte_length(begin, it); + auto length = byte_length(it, end); + return std::string(path.substr(offset, length)); } std::string find_base_name_from_path(const std::string_view path, char32_t separator) { auto name = find_name_from_path(path, separator); - if (!name.size()) { + if (!name.length()) { return std::string(); } @@ -653,28 +650,34 @@ std::string find_base_path(const std::string_view path, char32_t separator) { auto it = end; --it; - // skip trailing separator - if (*it == uint32_t(separator)) { + // skip trailing separators at the end of the path + while (*it == uint32_t(separator)) { if (it == begin) { return std::string(); } --it; } - while (it != begin) { - if (*it == uint32_t(separator)) { - break; + // skip non-separators + while (*it != uint32_t(separator)) { + if (it == begin) { + // there are no separators, base path is empty + return std::string(); } --it; } - if (it == begin) { - return std::string(); + // skip trailing separators at the end of the base path + while (*it == uint32_t(separator)) { + if (it == begin) { + // base path is all separators, base path is empty + return std::string(); + } + --it; } - auto length = byte_length(it, end); - auto offset = path.length() - length; - return std::string(path.substr(0, offset)); + auto length = byte_length(begin, std::next(it)); + return std::string(path.substr(0, length)); } std::string canonicalize_path(const std::string_view path, char32_t separator) { From 0d3ef65dcdefd06d760257d99d53ff922e9705bc Mon Sep 17 00:00:00 2001 From: Rick Gibbed Date: Fri, 25 Jun 2021 23:06:08 -0500 Subject: [PATCH 03/15] Add more UTF-8 path tests. Add more UTF-8 path tests to catch newly discovered issues. --- src/xenia/base/testing/utf8_test.cc | 80 +++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/src/xenia/base/testing/utf8_test.cc b/src/xenia/base/testing/utf8_test.cc index 9a33ff4fb..37255549d 100644 --- a/src/xenia/base/testing/utf8_test.cc +++ b/src/xenia/base/testing/utf8_test.cc @@ -388,20 +388,39 @@ TEST_CASE("UTF-8 Fix Path Separators", "[utf8]") { TEST_CASE("UTF-8 Find Name From Path", "[utf8]") { TEST_PATH(utf8::find_name_from_path, "/", ""); + TEST_PATH(utf8::find_name_from_path, "//", ""); + TEST_PATH(utf8::find_name_from_path, "///", ""); TEST_PATH(utf8::find_name_from_path, "foo/bar/baz/qux/", "qux"); + TEST_PATH(utf8::find_name_from_path, "foo/bar/baz/qux//", "qux"); + TEST_PATH(utf8::find_name_from_path, "foo/bar/baz/qux///", "qux"); TEST_PATH(utf8::find_name_from_path, "foo/bar/baz/qux.txt", "qux.txt"); TEST_PATH(utf8::find_name_from_path, "ほげ/ぴよ/ふが/ほげら/ほげほげ/", "ほげほげ"); + TEST_PATH(utf8::find_name_from_path, "ほげ/ぴよ/ふが/ほげら/ほげほげ//", + "ほげほげ"); + TEST_PATH(utf8::find_name_from_path, "ほげ/ぴよ/ふが/ほげら/ほげほげ///", + "ほげほげ"); TEST_PATH(utf8::find_name_from_path, "ほげ/ぴよ/ふが/ほげら/ほげほげ.txt", "ほげほげ.txt"); + TEST_PATH(utf8::find_name_from_path, "/foo", "foo"); + TEST_PATH(utf8::find_name_from_path, "//foo", "foo"); + TEST_PATH(utf8::find_name_from_path, "///foo", "foo"); TEST_PATH(utf8::find_name_from_path, "/foo/bar/baz/qux.txt", "qux.txt"); TEST_PATH(utf8::find_name_from_path, "/ほげ/ぴよ/ふが/ほげら/ほげほげ/", "ほげほげ"); + TEST_PATH(utf8::find_name_from_path, "/ほげ/ぴよ/ふが/ほげら/ほげほげ//", + "ほげほげ"); + TEST_PATH(utf8::find_name_from_path, "/ほげ/ぴよ/ふが/ほげら/ほげほげ///", + "ほげほげ"); TEST_PATH(utf8::find_name_from_path, "/ほげ/ぴよ/ふが/ほげら/ほげほげ.txt", "ほげほげ.txt"); TEST_PATH(utf8::find_name_from_path, "X:/foo/bar/baz/qux.txt", "qux.txt"); TEST_PATH(utf8::find_name_from_path, "X:/ほげ/ぴよ/ふが/ほげら/ほげほげ/", "ほげほげ"); + TEST_PATH(utf8::find_name_from_path, "X:/ほげ/ぴよ/ふが/ほげら/ほげほげ//", + "ほげほげ"); + TEST_PATH(utf8::find_name_from_path, "X:/ほげ/ぴよ/ふが/ほげら/ほげほげ///", + "ほげほげ"); TEST_PATH(utf8::find_name_from_path, "X:/ほげ/ぴよ/ふが/ほげら/ほげほげ.txt", "ほげほげ.txt"); TEST_PATH(utf8::find_name_from_path, "X:/ほげ/ぴよ/ふが/ほげら.ほげほげ", @@ -413,26 +432,44 @@ TEST_CASE("UTF-8 Find Name From Path", "[utf8]") { TEST_CASE("UTF-8 Find Base Name From Path", "[utf8]") { TEST_PATH(utf8::find_base_name_from_path, "foo/bar/baz/qux.txt", "qux"); TEST_PATH(utf8::find_base_name_from_path, "foo/bar/baz/qux/", "qux"); + TEST_PATH(utf8::find_base_name_from_path, "foo/bar/baz/qux//", "qux"); + TEST_PATH(utf8::find_base_name_from_path, "foo/bar/baz/qux///", "qux"); TEST_PATH(utf8::find_base_name_from_path, "ほげ/ぴよ/ふが/ほげら/ほげほげ.txt", "ほげほげ"); TEST_PATH(utf8::find_base_name_from_path, "ほげ/ぴよ/ふが/ほげら/ほげほげ/", "ほげほげ"); + TEST_PATH(utf8::find_base_name_from_path, "ほげ/ぴよ/ふが/ほげら/ほげほげ//", + "ほげほげ"); + TEST_PATH(utf8::find_base_name_from_path, "ほげ/ぴよ/ふが/ほげら/ほげほげ///", + "ほげほげ"); TEST_PATH(utf8::find_base_name_from_path, "ほげ/ぴよ/ふが/ほげら.ほげほげ", "ほげら"); TEST_PATH(utf8::find_base_name_from_path, "/foo/bar/baz/qux.txt", "qux"); TEST_PATH(utf8::find_base_name_from_path, "/foo/bar/baz/qux/", "qux"); + TEST_PATH(utf8::find_base_name_from_path, "/foo/bar/baz/qux//", "qux"); + TEST_PATH(utf8::find_base_name_from_path, "/foo/bar/baz/qux///", "qux"); TEST_PATH(utf8::find_base_name_from_path, "/ほげ/ぴよ/ふが/ほげら/ほげほげ.txt", "ほげほげ"); TEST_PATH(utf8::find_base_name_from_path, "/ほげ/ぴよ/ふが/ほげら/ほげほげ/", "ほげほげ"); + TEST_PATH(utf8::find_base_name_from_path, "/ほげ/ぴよ/ふが/ほげら/ほげほげ//", + "ほげほげ"); + TEST_PATH(utf8::find_base_name_from_path, + "/ほげ/ぴよ/ふが/ほげら/ほげほげ///", "ほげほげ"); TEST_PATH(utf8::find_base_name_from_path, "/ほげ/ぴよ/ふが/ほげら.ほげほげ", "ほげら"); TEST_PATH(utf8::find_base_name_from_path, "X:/foo/bar/baz/qux.txt", "qux"); TEST_PATH(utf8::find_base_name_from_path, "X:/foo/bar/baz/qux/", "qux"); + TEST_PATH(utf8::find_base_name_from_path, "X:/foo/bar/baz/qux//", "qux"); + TEST_PATH(utf8::find_base_name_from_path, "X:/foo/bar/baz/qux///", "qux"); TEST_PATH(utf8::find_base_name_from_path, "X:/ほげ/ぴよ/ふが/ほげら/ほげほげ.txt", "ほげほげ"); TEST_PATH(utf8::find_base_name_from_path, "X:/ほげ/ぴよ/ふが/ほげら/ほげほげ/", "ほげほげ"); + TEST_PATH(utf8::find_base_name_from_path, + "X:/ほげ/ぴよ/ふが/ほげら/ほげほげ//", "ほげほげ"); + TEST_PATH(utf8::find_base_name_from_path, + "X:/ほげ/ぴよ/ふが/ほげら/ほげほげ///", "ほげほげ"); TEST_PATH(utf8::find_base_name_from_path, "X:/ほげ/ぴよ/ふが/ほげら.ほげほげ", "ほげら"); } @@ -443,38 +480,79 @@ TEST_CASE("UTF-8 Find Base Path", "[utf8]") { TEST_PATH(utf8::find_base_path, "", ""); TEST_PATH(utf8::find_base_path, "/", ""); TEST_PATH(utf8::find_base_path, "//", ""); + TEST_PATH(utf8::find_base_path, "///", ""); TEST_PATH(utf8::find_base_path, "/foo", ""); + TEST_PATH(utf8::find_base_path, "//foo", ""); + TEST_PATH(utf8::find_base_path, "///foo", ""); TEST_PATH(utf8::find_base_path, "/foo/", ""); + TEST_PATH(utf8::find_base_path, "/foo//", ""); + TEST_PATH(utf8::find_base_path, "/foo///", ""); + TEST_PATH(utf8::find_base_path, "//foo/", ""); + TEST_PATH(utf8::find_base_path, "//foo//", ""); + TEST_PATH(utf8::find_base_path, "//foo///", ""); + TEST_PATH(utf8::find_base_path, "///foo/", ""); + TEST_PATH(utf8::find_base_path, "///foo//", ""); + TEST_PATH(utf8::find_base_path, "///foo///", ""); TEST_PATH(utf8::find_base_path, "/foo/bar", "/foo"); TEST_PATH(utf8::find_base_path, "/foo/bar/", "/foo"); + TEST_PATH(utf8::find_base_path, "/foo/bar//", "/foo"); + TEST_PATH(utf8::find_base_path, "/foo/bar///", "/foo"); TEST_PATH(utf8::find_base_path, "/foo/bar/baz/qux", "/foo/bar/baz"); TEST_PATH(utf8::find_base_path, "/foo/bar/baz/qux/", "/foo/bar/baz"); + TEST_PATH(utf8::find_base_path, "/foo/bar/baz/qux//", "/foo/bar/baz"); + TEST_PATH(utf8::find_base_path, "/foo/bar/baz/qux///", "/foo/bar/baz"); TEST_PATH(utf8::find_base_path, "/ほげ/ぴよ/ふが/ほげら/ほげほげ", "/ほげ/ぴよ/ふが/ほげら"); TEST_PATH(utf8::find_base_path, "/ほげ/ぴよ/ふが/ほげら/ほげほげ/", "/ほげ/ぴよ/ふが/ほげら"); + TEST_PATH(utf8::find_base_path, "/ほげ/ぴよ/ふが/ほげら/ほげほげ//", + "/ほげ/ぴよ/ふが/ほげら"); + TEST_PATH(utf8::find_base_path, "/ほげ/ぴよ/ふが/ほげら/ほげほげ///", + "/ほげ/ぴよ/ふが/ほげら"); TEST_PATH(utf8::find_base_path, "foo", ""); TEST_PATH(utf8::find_base_path, "foo/", ""); + TEST_PATH(utf8::find_base_path, "foo//", ""); + TEST_PATH(utf8::find_base_path, "foo///", ""); TEST_PATH(utf8::find_base_path, "foo/bar", "foo"); TEST_PATH(utf8::find_base_path, "foo/bar/", "foo"); + TEST_PATH(utf8::find_base_path, "foo/bar//", "foo"); + TEST_PATH(utf8::find_base_path, "foo/bar///", "foo"); TEST_PATH(utf8::find_base_path, "foo/bar/baz/qux", "foo/bar/baz"); TEST_PATH(utf8::find_base_path, "foo/bar/baz/qux/", "foo/bar/baz"); + TEST_PATH(utf8::find_base_path, "foo/bar/baz/qux//", "foo/bar/baz"); + TEST_PATH(utf8::find_base_path, "foo/bar/baz/qux///", "foo/bar/baz"); TEST_PATH(utf8::find_base_path, "ほげ/ぴよ/ふが/ほげら/ほげほげ", "ほげ/ぴよ/ふが/ほげら"); TEST_PATH(utf8::find_base_path, "ほげ/ぴよ/ふが/ほげら/ほげほげ/", "ほげ/ぴよ/ふが/ほげら"); + TEST_PATH(utf8::find_base_path, "ほげ/ぴよ/ふが/ほげら/ほげほげ//", + "ほげ/ぴよ/ふが/ほげら"); + TEST_PATH(utf8::find_base_path, "ほげ/ぴよ/ふが/ほげら/ほげほげ///", + "ほげ/ぴよ/ふが/ほげら"); TEST_PATH(utf8::find_base_path, "X:", ""); TEST_PATH(utf8::find_base_path, "X:/", ""); + TEST_PATH(utf8::find_base_path, "X://", ""); + TEST_PATH(utf8::find_base_path, "X:///", ""); TEST_PATH(utf8::find_base_path, "X:/foo", "X:"); TEST_PATH(utf8::find_base_path, "X:/foo/", "X:"); + TEST_PATH(utf8::find_base_path, "X:/foo//", "X:"); + TEST_PATH(utf8::find_base_path, "X:/foo///", "X:"); TEST_PATH(utf8::find_base_path, "X:/foo/bar", "X:/foo"); TEST_PATH(utf8::find_base_path, "X:/foo/bar/", "X:/foo"); + TEST_PATH(utf8::find_base_path, "X:/foo/bar//", "X:/foo"); + TEST_PATH(utf8::find_base_path, "X:/foo/bar///", "X:/foo"); TEST_PATH(utf8::find_base_path, "X:/foo/bar/baz/qux", "X:/foo/bar/baz"); TEST_PATH(utf8::find_base_path, "X:/foo/bar/baz/qux/", "X:/foo/bar/baz"); + TEST_PATH(utf8::find_base_path, "X:/foo/bar/baz/qux//", "X:/foo/bar/baz"); + TEST_PATH(utf8::find_base_path, "X:/foo/bar/baz/qux///", "X:/foo/bar/baz"); TEST_PATH(utf8::find_base_path, "X:/ほげ/ぴよ/ふが/ほげら/ほげほげ", "X:/ほげ/ぴよ/ふが/ほげら"); TEST_PATH(utf8::find_base_path, "X:/ほげ/ぴよ/ふが/ほげら/ほげほげ/", "X:/ほげ/ぴよ/ふが/ほげら"); + TEST_PATH(utf8::find_base_path, "X:/ほげ/ぴよ/ふが/ほげら/ほげほげ//", + "X:/ほげ/ぴよ/ふが/ほげら"); + TEST_PATH(utf8::find_base_path, "X:/ほげ/ぴよ/ふが/ほげら/ほげほげ///", + "X:/ほげ/ぴよ/ふが/ほげら"); } // TODO(gibbed): find_base_guest_path @@ -482,6 +560,8 @@ TEST_CASE("UTF-8 Find Base Path", "[utf8]") { TEST_CASE("UTF-8 Canonicalize Path", "[utf8]") { TEST_PATH(utf8::canonicalize_path, "foo/bar/baz/qux", "foo/bar/baz/qux"); TEST_PATH(utf8::canonicalize_path, "foo/bar/baz/qux/", "foo/bar/baz/qux"); + TEST_PATH(utf8::canonicalize_path, "foo/bar/baz/qux//", "foo/bar/baz/qux"); + TEST_PATH(utf8::canonicalize_path, "foo/bar/baz/qux///", "foo/bar/baz/qux"); TEST_PATH(utf8::canonicalize_path, "foo/./baz/qux", "foo/baz/qux"); TEST_PATH(utf8::canonicalize_path, "foo/./baz/qux/", "foo/baz/qux"); TEST_PATH(utf8::canonicalize_path, "foo/../baz/qux", "baz/qux"); From 052ce3d389c765861a49044589540dab0b8f4948 Mon Sep 17 00:00:00 2001 From: emoose Date: Mon, 24 May 2021 04:01:35 +0100 Subject: [PATCH 04/15] [Kernel] Make KeEnter/LeaveCriticalRegion only affect the caller thread Adds a new X_KTHREAD::apc_disable_count field at 0xB0 into X_KTHREAD based on where 360 kernel seems to store it, and made CriticalRegion funcs act on that, instead of locking things between all threads, and changes DeliverAPCs to check that field before running the APCs. XThread::LockApc/UnlockApc were also updated too as those previously called into EnterCrit/LeaveCrit to work, but AFAIK the code that uses LockApc/UnlockApc does have an actual need for locking between threads, so changed them to work on XThread::global_critical_region_ directly instead. --- src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc | 8 ++++---- src/xenia/kernel/xthread.cc | 15 ++++++++++----- src/xenia/kernel/xthread.h | 8 +++++--- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc index bbe78ec87..3a323d579 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc @@ -960,14 +960,14 @@ void KeReleaseSpinLockFromRaisedIrql(lpdword_t lock_ptr) { DECLARE_XBOXKRNL_EXPORT2(KeReleaseSpinLockFromRaisedIrql, kThreading, kImplemented, kHighFrequency); -void KeEnterCriticalRegion() { XThread::EnterCriticalRegion(); } +void KeEnterCriticalRegion() { + XThread::GetCurrentThread()->EnterCriticalRegion(); +} DECLARE_XBOXKRNL_EXPORT2(KeEnterCriticalRegion, kThreading, kImplemented, kHighFrequency); void KeLeaveCriticalRegion() { - XThread::LeaveCriticalRegion(); - - XThread::GetCurrentThread()->CheckApcs(); + XThread::GetCurrentThread()->LeaveCriticalRegion(); } DECLARE_XBOXKRNL_EXPORT2(KeLeaveCriticalRegion, kThreading, kImplemented, kHighFrequency); diff --git a/src/xenia/kernel/xthread.cc b/src/xenia/kernel/xthread.cc index 8776b43dc..871a50820 100644 --- a/src/xenia/kernel/xthread.cc +++ b/src/xenia/kernel/xthread.cc @@ -578,11 +578,15 @@ void XThread::Reenter(uint32_t address) { } void XThread::EnterCriticalRegion() { - xe::global_critical_region::mutex().lock(); + guest_object()->apc_disable_count--; } void XThread::LeaveCriticalRegion() { - xe::global_critical_region::mutex().unlock(); + auto kthread = guest_object(); + auto apc_disable_count = ++kthread->apc_disable_count; + if (apc_disable_count == 0) { + CheckApcs(); + } } uint32_t XThread::RaiseIrql(uint32_t new_irql) { @@ -593,11 +597,11 @@ void XThread::LowerIrql(uint32_t new_irql) { irql_ = new_irql; } void XThread::CheckApcs() { DeliverAPCs(); } -void XThread::LockApc() { EnterCriticalRegion(); } +void XThread::LockApc() { global_critical_region_.mutex().lock(); } void XThread::UnlockApc(bool queue_delivery) { bool needs_apc = apc_list_.HasPending(); - LeaveCriticalRegion(); + global_critical_region_.mutex().unlock(); if (needs_apc && queue_delivery) { thread_->QueueUserCallback([this]() { DeliverAPCs(); }); } @@ -632,7 +636,8 @@ void XThread::DeliverAPCs() { // https://www.drdobbs.com/inside-nts-asynchronous-procedure-call/184416590?pgno=7 auto processor = kernel_state()->processor(); LockApc(); - while (apc_list_.HasPending()) { + auto kthread = guest_object(); + while (apc_list_.HasPending() && kthread->apc_disable_count == 0) { // Get APC entry (offset for LIST_ENTRY offset) and cache what we need. // Calling the routine may delete the memory/overwrite it. uint32_t apc_ptr = apc_list_.Shift() - 8; diff --git a/src/xenia/kernel/xthread.h b/src/xenia/kernel/xthread.h index 045769bda..c6bb7cb6c 100644 --- a/src/xenia/kernel/xthread.h +++ b/src/xenia/kernel/xthread.h @@ -113,7 +113,9 @@ struct X_KTHREAD { uint8_t unk_8B; // 0x8B uint8_t unk_8C[0x10]; // 0x8C xe::be unk_9C; // 0x9C - uint8_t unk_A0[0x1C]; // 0xA0 + uint8_t unk_A0[0x10]; // 0xA0 + int32_t apc_disable_count; // 0xB0 + uint8_t unk_B4[0x8]; // 0xB4 uint8_t suspend_count; // 0xBC uint8_t unk_BD; // 0xBD uint8_t unk_BE; // 0xBE @@ -192,8 +194,8 @@ class XThread : public XObject, public cpu::Thread { virtual void Reenter(uint32_t address); - static void EnterCriticalRegion(); - static void LeaveCriticalRegion(); + void EnterCriticalRegion(); + void LeaveCriticalRegion(); uint32_t RaiseIrql(uint32_t new_irql); void LowerIrql(uint32_t new_irql); From 4cc2dad005945b3597338fbb1b890c451105a06f Mon Sep 17 00:00:00 2001 From: Gliniak Date: Sun, 13 Jun 2021 14:59:51 +0200 Subject: [PATCH 05/15] [Object] Remove object name from name_table on object removal --- src/xenia/kernel/util/object_table.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/xenia/kernel/util/object_table.cc b/src/xenia/kernel/util/object_table.cc index df881e3a1..d1250791c 100644 --- a/src/xenia/kernel/util/object_table.cc +++ b/src/xenia/kernel/util/object_table.cc @@ -205,6 +205,10 @@ X_STATUS ObjectTable::RemoveHandle(X_HANDLE handle) { XELOGI("Removed handle:{:08X} for {}", handle, typeid(*object).name()); + // Remove object name from mapping to prevent naming collision. + if (!object->name().empty()) { + RemoveNameMapping(object->name()); + } // Release now that the object has been removed from the table. object->Release(); } From 1cc0c1c985de83fc7dccb05d1b9733910ff34b38 Mon Sep 17 00:00:00 2001 From: Gliniak Date: Mon, 31 May 2021 19:15:10 +0200 Subject: [PATCH 06/15] Added more UTF-8 path tests (fnfp & fbnfp). Added more UTF-8 path tests (find_name_from_path & find_base_name_from_path). --- src/xenia/base/testing/utf8_test.cc | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/xenia/base/testing/utf8_test.cc b/src/xenia/base/testing/utf8_test.cc index 37255549d..356f8ad0f 100644 --- a/src/xenia/base/testing/utf8_test.cc +++ b/src/xenia/base/testing/utf8_test.cc @@ -390,6 +390,12 @@ TEST_CASE("UTF-8 Find Name From Path", "[utf8]") { TEST_PATH(utf8::find_name_from_path, "/", ""); TEST_PATH(utf8::find_name_from_path, "//", ""); TEST_PATH(utf8::find_name_from_path, "///", ""); + TEST_PATH(utf8::find_name_from_path, "C/", "C"); + TEST_PATH(utf8::find_name_from_path, "/C/", "C"); + TEST_PATH(utf8::find_name_from_path, "C/D/", "D"); + TEST_PATH(utf8::find_name_from_path, "/C/D/E/", "E"); + TEST_PATH(utf8::find_name_from_path, "foo/bar/D/", "D"); + TEST_PATH(utf8::find_name_from_path, "/foo/bar/E/qux/", "qux"); TEST_PATH(utf8::find_name_from_path, "foo/bar/baz/qux/", "qux"); TEST_PATH(utf8::find_name_from_path, "foo/bar/baz/qux//", "qux"); TEST_PATH(utf8::find_name_from_path, "foo/bar/baz/qux///", "qux"); @@ -434,6 +440,11 @@ TEST_CASE("UTF-8 Find Base Name From Path", "[utf8]") { TEST_PATH(utf8::find_base_name_from_path, "foo/bar/baz/qux/", "qux"); TEST_PATH(utf8::find_base_name_from_path, "foo/bar/baz/qux//", "qux"); TEST_PATH(utf8::find_base_name_from_path, "foo/bar/baz/qux///", "qux"); + TEST_PATH(utf8::find_base_name_from_path, "C/", "C"); + TEST_PATH(utf8::find_base_name_from_path, "/C/", "C"); + TEST_PATH(utf8::find_base_name_from_path, "C/D/", "D"); + TEST_PATH(utf8::find_base_name_from_path, "/C/D/E/", "E"); + TEST_PATH(utf8::find_base_name_from_path, "foo/bar/D/", "D"); TEST_PATH(utf8::find_base_name_from_path, "ほげ/ぴよ/ふが/ほげら/ほげほげ.txt", "ほげほげ"); TEST_PATH(utf8::find_base_name_from_path, "ほげ/ぴよ/ふが/ほげら/ほげほげ/", From e8dde519cc1119347557f6e453cd52cd49032f5f Mon Sep 17 00:00:00 2001 From: madmodder123 <33565771+madmodder123@users.noreply.github.com> Date: Mon, 24 May 2021 21:33:12 -0700 Subject: [PATCH 07/15] [XAM] XamShowSigninUI: Fix issue with loading profile(s) --- src/xenia/kernel/xam/xam_user.cc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/xenia/kernel/xam/xam_user.cc b/src/xenia/kernel/xam/xam_user.cc index 2d98aa1e0..11457e822 100644 --- a/src/xenia/kernel/xam/xam_user.cc +++ b/src/xenia/kernel/xam/xam_user.cc @@ -546,7 +546,12 @@ DECLARE_XAM_EXPORT1(XamUserAreUsersFriends, kUserProfiles, kStub); dword_result_t XamShowSigninUI(dword_t unk, dword_t unk_mask) { // Mask values vary. Probably matching user types? Local/remote? - // Games seem to sit and loop until we trigger this notification. + + // To fix game modes that display a 4 profile signin UI (even if playing alone): + // XN_SYS_SIGNINCHANGED + kernel_state()->BroadcastNotification(0x0000000A, 1); + // Games seem to sit and loop until we trigger this notification: + // XN_SYS_UI (off) kernel_state()->BroadcastNotification(0x00000009, 0); return X_ERROR_SUCCESS; } From e9f1a534dff810c066ef6e0b99d888bb1d34b951 Mon Sep 17 00:00:00 2001 From: Gliniak Date: Sat, 1 Aug 2020 08:13:54 +0200 Subject: [PATCH 08/15] [Kernel/Thread] Changed default stack location --- src/xenia/kernel/xthread.cc | 12 ++++++------ src/xenia/kernel/xthread.h | 3 +++ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/xenia/kernel/xthread.cc b/src/xenia/kernel/xthread.cc index 871a50820..e11a94296 100644 --- a/src/xenia/kernel/xthread.cc +++ b/src/xenia/kernel/xthread.cc @@ -226,7 +226,7 @@ void XThread::InitializeGuestObject() { } bool XThread::AllocateStack(uint32_t size) { - auto heap = memory()->LookupHeap(0x40000000); + auto heap = memory()->LookupHeap(kStackAddressRangeBegin); auto alignment = heap->page_size(); auto padding = heap->page_size() * 2; // Guard page size * 2 @@ -234,10 +234,10 @@ bool XThread::AllocateStack(uint32_t size) { auto actual_size = size + padding; uint32_t address = 0; - if (!heap->AllocRange(0x40000000, 0x7F000000, actual_size, alignment, - kMemoryAllocationReserve | kMemoryAllocationCommit, - kMemoryProtectRead | kMemoryProtectWrite, false, - &address)) { + if (!heap->AllocRange( + kStackAddressRangeBegin, kStackAddressRangeEnd, actual_size, + alignment, kMemoryAllocationReserve | kMemoryAllocationCommit, + kMemoryProtectRead | kMemoryProtectWrite, false, &address)) { return false; } @@ -258,7 +258,7 @@ bool XThread::AllocateStack(uint32_t size) { void XThread::FreeStack() { if (stack_alloc_base_) { - auto heap = memory()->LookupHeap(0x40000000); + auto heap = memory()->LookupHeap(kStackAddressRangeBegin); heap->Release(stack_alloc_base_); stack_alloc_base_ = 0; diff --git a/src/xenia/kernel/xthread.h b/src/xenia/kernel/xthread.h index c6bb7cb6c..9eef807b2 100644 --- a/src/xenia/kernel/xthread.h +++ b/src/xenia/kernel/xthread.h @@ -149,6 +149,9 @@ class XThread : public XObject, public cpu::Thread { public: static const XObject::Type kObjectType = XObject::Type::Thread; + static constexpr uint32_t kStackAddressRangeBegin = 0x70000000; + static constexpr uint32_t kStackAddressRangeEnd = 0x7F000000; + struct CreationParams { uint32_t stack_size; uint32_t xapi_thread_startup; From 13f30d34928cf809eb44baaa2d87577785302f8c Mon Sep 17 00:00:00 2001 From: emoose Date: Tue, 15 Jun 2021 22:13:30 +0100 Subject: [PATCH 09/15] [Kernel] Merge Content*Data structs into XCONTENT*_DATA Also moved XContentType to xbox.h so code can make use of it without needing to include STFS stuff --- src/xenia/kernel/xam/content_manager.cc | 68 +++---- src/xenia/kernel/xam/content_manager.h | 166 ++++++++---------- src/xenia/kernel/xam/xam_content.cc | 22 +-- src/xenia/kernel/xam/xam_content_aggregate.cc | 33 ++-- src/xenia/vfs/devices/stfs_xbox.h | 37 ---- src/xenia/xbox.h | 37 ++++ 6 files changed, 157 insertions(+), 206 deletions(-) diff --git a/src/xenia/kernel/xam/content_manager.cc b/src/xenia/kernel/xam/content_manager.cc index bdc09af8b..2b3aee4c9 100644 --- a/src/xenia/kernel/xam/content_manager.cc +++ b/src/xenia/kernel/xam/content_manager.cc @@ -31,7 +31,7 @@ static int content_device_id_ = 0; ContentPackage::ContentPackage(KernelState* kernel_state, const std::string_view root_name, - const ContentData& data, + const XCONTENT_DATA& data, const std::filesystem::path& package_path) : kernel_state_(kernel_state), root_name_(root_name) { device_path_ = fmt::format("\\Device\\Content\\{0}\\", ++content_device_id_); @@ -58,48 +58,26 @@ ContentManager::ContentManager(KernelState* kernel_state, ContentManager::~ContentManager() = default; std::filesystem::path ContentManager::ResolvePackageRoot( - uint32_t content_type) { - auto title_id = fmt::format("{:8X}", kernel_state_->title_id()); - - std::string type_name; - switch (content_type) { - case 1: - // Save games. - type_name = "00000001"; - break; - case 2: - // DLC from the marketplace. - type_name = "00000002"; - break; - case 3: - // Publisher content? - type_name = "00000003"; - break; - case 0x000D0000: - // ??? - type_name = "000D0000"; - break; - default: - assert_unhandled_case(data.content_type); - return std::filesystem::path(); - } + XContentType content_type) { + auto title_id_str = fmt::format("{:8X}", kernel_state_->title_id()); + auto content_type_str = fmt::format("{:08X}", uint32_t(content_type)); // Package root path: - // content_root/title_id/type_name/ - return root_path_ / title_id / type_name; + // content_root/title_id/content_type/ + return root_path_ / title_id_str / content_type_str; } std::filesystem::path ContentManager::ResolvePackagePath( - const ContentData& data) { + const XCONTENT_DATA& data) { // Content path: - // content_root/title_id/type_name/data_file_name/ + // content_root/title_id/content_type/data_file_name/ auto package_root = ResolvePackageRoot(data.content_type); - return package_root / xe::to_path(data.file_name); + return package_root / xe::to_path(data.file_name()); } -std::vector ContentManager::ListContent(uint32_t device_id, - uint32_t content_type) { - std::vector result; +std::vector ContentManager::ListContent( + uint32_t device_id, XContentType content_type) { + std::vector result; // Search path: // content_root/title_id/type_name/* @@ -110,11 +88,11 @@ std::vector ContentManager::ListContent(uint32_t device_id, // Directories only. continue; } - ContentData content_data; + XCONTENT_DATA content_data; content_data.device_id = device_id; content_data.content_type = content_type; - content_data.display_name = xe::path_to_utf16(file_info.name); - content_data.file_name = xe::path_to_utf8(file_info.name); + content_data.set_display_name(xe::path_to_utf16(file_info.name)); + content_data.set_file_name(xe::path_to_utf8(file_info.name)); result.emplace_back(std::move(content_data)); } @@ -122,7 +100,7 @@ std::vector ContentManager::ListContent(uint32_t device_id, } std::unique_ptr ContentManager::ResolvePackage( - const std::string_view root_name, const ContentData& data) { + const std::string_view root_name, const XCONTENT_DATA& data) { auto package_path = ResolvePackagePath(data); if (!std::filesystem::exists(package_path)) { return nullptr; @@ -135,13 +113,13 @@ std::unique_ptr ContentManager::ResolvePackage( return package; } -bool ContentManager::ContentExists(const ContentData& data) { +bool ContentManager::ContentExists(const XCONTENT_DATA& data) { auto path = ResolvePackagePath(data); return std::filesystem::exists(path); } X_RESULT ContentManager::CreateContent(const std::string_view root_name, - const ContentData& data) { + const XCONTENT_DATA& data) { auto global_lock = global_critical_region_.Acquire(); if (open_packages_.count(string_key(root_name))) { @@ -168,7 +146,7 @@ X_RESULT ContentManager::CreateContent(const std::string_view root_name, } X_RESULT ContentManager::OpenContent(const std::string_view root_name, - const ContentData& data) { + const XCONTENT_DATA& data) { auto global_lock = global_critical_region_.Acquire(); if (open_packages_.count(string_key(root_name))) { @@ -207,7 +185,7 @@ X_RESULT ContentManager::CloseContent(const std::string_view root_name) { return X_ERROR_SUCCESS; } -X_RESULT ContentManager::GetContentThumbnail(const ContentData& data, +X_RESULT ContentManager::GetContentThumbnail(const XCONTENT_DATA& data, std::vector* buffer) { auto global_lock = global_critical_region_.Acquire(); auto package_path = ResolvePackagePath(data); @@ -226,7 +204,7 @@ X_RESULT ContentManager::GetContentThumbnail(const ContentData& data, } } -X_RESULT ContentManager::SetContentThumbnail(const ContentData& data, +X_RESULT ContentManager::SetContentThumbnail(const XCONTENT_DATA& data, std::vector buffer) { auto global_lock = global_critical_region_.Acquire(); auto package_path = ResolvePackagePath(data); @@ -242,7 +220,7 @@ X_RESULT ContentManager::SetContentThumbnail(const ContentData& data, } } -X_RESULT ContentManager::DeleteContent(const ContentData& data) { +X_RESULT ContentManager::DeleteContent(const XCONTENT_DATA& data) { auto global_lock = global_critical_region_.Acquire(); if (IsContentOpen(data)) { @@ -267,7 +245,7 @@ std::filesystem::path ContentManager::ResolveGameUserContentPath() { return root_path_ / title_id / kGameUserContentDirName / user_name; } -bool ContentManager::IsContentOpen(const ContentData& data) const { +bool ContentManager::IsContentOpen(const XCONTENT_DATA& data) const { return std::any_of(open_packages_.cbegin(), open_packages_.cend(), [data](std::pair content) { return data == content.second->GetPackageContentData(); diff --git a/src/xenia/kernel/xam/content_manager.h b/src/xenia/kernel/xam/content_manager.h index 5eef60b4f..f0ff9c5b9 100644 --- a/src/xenia/kernel/xam/content_manager.h +++ b/src/xenia/kernel/xam/content_manager.h @@ -33,105 +33,87 @@ namespace xam { struct XCONTENT_DATA { be device_id; - be content_type; + be content_type; union { - be display_name[128]; - char16_t display_name_chars[128]; - }; - char file_name[42]; - uint8_t padding[2]; -}; -static_assert_size(XCONTENT_DATA, 308); + // this should be be, but that stops copy constructor being + // generated... + uint16_t uint[128]; + char16_t chars[128]; + } display_name_raw; -struct XCONTENT_AGGREGATE_DATA { - be device_id; - be content_type; - union { - be display_name[128]; - char16_t display_name_chars[128]; - }; - char file_name[42]; + char file_name_raw[42]; + + // Some games use this padding field as a null-terminator, as eg. + // DLC packages usually fill the entire file_name_raw array + // Not every game sets it to 0 though, so make sure any file_name_raw reads + // only go up to 42 chars! uint8_t padding[2]; + + bool operator==(const XCONTENT_DATA& other) const { + // Package is located via device_id/content_type/file_name, so only need to + // compare those + return device_id == other.device_id && content_type == other.content_type && + file_name() == other.file_name(); + } + + std::u16string display_name() const { + return load_and_swap(display_name_raw.uint); + } + + std::string file_name() const { + std::string value; + value.assign(file_name_raw, + std::min(strlen(file_name_raw), countof(file_name_raw))); + return value; + } + + void set_display_name(const std::u16string_view value) { + // Some games (eg Goldeneye XBLA) require multiple null-terminators for it + // to read the string properly, blanking the array should take care of that + + std::fill_n(display_name_raw.chars, countof(display_name_raw.chars), 0); + string_util::copy_and_swap_truncating(display_name_raw.chars, value, + countof(display_name_raw.chars)); + } + + void set_file_name(const std::string_view value) { + std::fill_n(file_name_raw, countof(file_name_raw), 0); + string_util::copy_maybe_truncating( + file_name_raw, value, xe::countof(file_name_raw)); + + // Some games rely on padding field acting as a null-terminator... + padding[0] = padding[1] = 0; + } +}; +static_assert_size(XCONTENT_DATA, 0x134); + +struct XCONTENT_AGGREGATE_DATA : XCONTENT_DATA { be title_id; -}; -static_assert_size(XCONTENT_AGGREGATE_DATA, 312); -struct ContentData { - uint32_t device_id; - uint32_t content_type; - std::u16string display_name; - std::string file_name; - - ContentData() = default; - - bool operator==(const ContentData& rhs) const { - return device_id == rhs.device_id && content_type == rhs.content_type && - file_name == rhs.file_name; - } - - explicit ContentData(const XCONTENT_DATA& data) { - device_id = data.device_id; - content_type = data.content_type; - display_name = xe::load_and_swap(data.display_name); - file_name = xe::load_and_swap(data.file_name); - } - - void Write(XCONTENT_DATA* data) const { - data->device_id = device_id; - data->content_type = content_type; - xe::string_util::copy_and_swap_truncating( - data->display_name_chars, display_name, - xe::countof(data->display_name_chars)); - xe::string_util::copy_maybe_truncating< - string_util::Safety::IKnowWhatIAmDoing>(data->file_name, file_name, - xe::countof(data->file_name)); - } -}; - -struct ContentAggregateData { - uint32_t device_id; - uint32_t content_type; - std::u16string display_name; - std::string file_name; - uint32_t title_id; - - ContentAggregateData() = default; - - explicit ContentAggregateData(const XCONTENT_AGGREGATE_DATA& data) { - device_id = data.device_id; - content_type = data.content_type; - display_name = xe::load_and_swap(data.display_name); - file_name = xe::load_and_swap(data.file_name); - title_id = data.title_id; - } - - void Write(XCONTENT_AGGREGATE_DATA* data) const { - data->device_id = device_id; - data->content_type = content_type; - xe::string_util::copy_and_swap_truncating( - data->display_name_chars, display_name, - xe::countof(data->display_name_chars)); - xe::string_util::copy_maybe_truncating< - string_util::Safety::IKnowWhatIAmDoing>(data->file_name, file_name, - xe::countof(data->file_name)); - data->title_id = title_id; + bool operator==(const XCONTENT_AGGREGATE_DATA& other) const { + // Package is located via device_id/title_id/content_type/file_name, so only + // need to compare those + return device_id == other.device_id && title_id == other.title_id && + content_type == other.content_type && + file_name() == other.file_name(); } }; +static_assert_size(XCONTENT_AGGREGATE_DATA, 0x138); class ContentPackage { public: ContentPackage(KernelState* kernel_state, const std::string_view root_name, - const ContentData& data, + const XCONTENT_DATA& data, const std::filesystem::path& package_path); ~ContentPackage(); - const ContentData& GetPackageContentData() const { return content_data_; } + const XCONTENT_DATA& GetPackageContentData() const { return content_data_; } private: KernelState* kernel_state_; std::string root_name_; std::string device_path_; - ContentData content_data_; + XCONTENT_DATA content_data_; }; class ContentManager { @@ -140,30 +122,30 @@ class ContentManager { const std::filesystem::path& root_path); ~ContentManager(); - std::vector ListContent(uint32_t device_id, - uint32_t content_type); + std::vector ListContent(uint32_t device_id, + XContentType content_type); std::unique_ptr ResolvePackage( - const std::string_view root_name, const ContentData& data); + const std::string_view root_name, const XCONTENT_DATA& data); - bool ContentExists(const ContentData& data); + bool ContentExists(const XCONTENT_DATA& data); X_RESULT CreateContent(const std::string_view root_name, - const ContentData& data); + const XCONTENT_DATA& data); X_RESULT OpenContent(const std::string_view root_name, - const ContentData& data); + const XCONTENT_DATA& data); X_RESULT CloseContent(const std::string_view root_name); - X_RESULT GetContentThumbnail(const ContentData& data, + X_RESULT GetContentThumbnail(const XCONTENT_DATA& data, std::vector* buffer); - X_RESULT SetContentThumbnail(const ContentData& data, + X_RESULT SetContentThumbnail(const XCONTENT_DATA& data, std::vector buffer); - X_RESULT DeleteContent(const ContentData& data); + X_RESULT DeleteContent(const XCONTENT_DATA& data); std::filesystem::path ResolveGameUserContentPath(); - bool IsContentOpen(const ContentData& data) const; + bool IsContentOpen(const XCONTENT_DATA& data) const; void CloseOpenedFilesFromContent(const std::string_view root_name); private: - std::filesystem::path ResolvePackageRoot(uint32_t content_type); - std::filesystem::path ResolvePackagePath(const ContentData& data); + std::filesystem::path ResolvePackageRoot(XContentType content_type); + std::filesystem::path ResolvePackagePath(const XCONTENT_DATA& data); KernelState* kernel_state_; std::filesystem::path root_path_; diff --git a/src/xenia/kernel/xam/xam_content.cc b/src/xenia/kernel/xam/xam_content.cc index 0f8a365e8..5ba1d99aa 100644 --- a/src/xenia/kernel/xam/xam_content.cc +++ b/src/xenia/kernel/xam/xam_content.cc @@ -96,11 +96,12 @@ dword_result_t XamContentCreateEnumerator(dword_t user_index, dword_t device_id, if (!device_info || device_info->device_id == DummyDeviceId::HDD) { // Get all content data. auto content_datas = kernel_state()->content_manager()->ListContent( - static_cast(DummyDeviceId::HDD), content_type); + static_cast(DummyDeviceId::HDD), + XContentType(uint32_t(content_type))); for (const auto& content_data : content_datas) { auto item = reinterpret_cast(e->AppendItem()); assert_not_null(item); - content_data.Write(item); + *item = content_data; } } @@ -123,8 +124,7 @@ dword_result_t XamContentCreateEx(dword_t user_index, lpstring_t root_name, dword_t cache_size, qword_t content_size, lpvoid_t overlapped_ptr) { X_RESULT result = X_ERROR_INVALID_PARAMETER; - auto content_data = - static_cast(*content_data_ptr.as()); + auto content_data = *content_data_ptr.as(); auto content_manager = kernel_state()->content_manager(); bool create = false; @@ -277,14 +277,13 @@ dword_result_t XamContentGetCreator(dword_t user_index, lpunknown_t overlapped_ptr) { auto result = X_ERROR_SUCCESS; - auto content_data = - static_cast(*content_data_ptr.as()); + auto content_data = *content_data_ptr.as(); bool content_exists = kernel_state()->content_manager()->ContentExists(content_data); if (content_exists) { - if (content_data.content_type == 1) { + if (content_data.content_type == XContentType::kSavedGame) { // User always creates saves. *is_creator_ptr = 1; if (creator_xuid_ptr) { @@ -316,8 +315,7 @@ dword_result_t XamContentGetThumbnail(dword_t user_index, lpunknown_t overlapped_ptr) { assert_not_null(buffer_size_ptr); uint32_t buffer_size = *buffer_size_ptr; - auto content_data = - static_cast(*content_data_ptr.as()); + auto content_data = *content_data_ptr.as(); // Get thumbnail (if it exists). std::vector buffer; @@ -353,8 +351,7 @@ dword_result_t XamContentSetThumbnail(dword_t user_index, lpvoid_t content_data_ptr, lpvoid_t buffer_ptr, dword_t buffer_size, lpunknown_t overlapped_ptr) { - auto content_data = - static_cast(*content_data_ptr.as()); + auto content_data = *content_data_ptr.as(); // Buffer is PNG data. auto buffer = std::vector((uint8_t*)buffer_ptr, @@ -373,8 +370,7 @@ DECLARE_XAM_EXPORT1(XamContentSetThumbnail, kContent, kImplemented); dword_result_t XamContentDelete(dword_t user_index, lpvoid_t content_data_ptr, lpunknown_t overlapped_ptr) { - auto content_data = - static_cast(*content_data_ptr.as()); + auto content_data = *content_data_ptr.as(); auto result = kernel_state()->content_manager()->DeleteContent(content_data); diff --git a/src/xenia/kernel/xam/xam_content_aggregate.cc b/src/xenia/kernel/xam/xam_content_aggregate.cc index 39f4bb2b5..b7b9ba90b 100644 --- a/src/xenia/kernel/xam/xam_content_aggregate.cc +++ b/src/xenia/kernel/xam/xam_content_aggregate.cc @@ -22,14 +22,15 @@ namespace xe { namespace kernel { namespace xam { -void AddODDContentTest(object_ref e, uint32_t content_type) { +void AddODDContentTest(object_ref e, + XContentType content_type) { auto root_entry = kernel_state()->file_system()->ResolvePath( "game:\\Content\\0000000000000000"); if (!root_entry) { return; } - auto content_type_path = fmt::format("{:08X}", content_type); + auto content_type_path = fmt::format("{:08X}", uint32_t(content_type)); xe::filesystem::WildcardEngine title_find_engine; title_find_engine.SetRule("????????"); @@ -62,14 +63,11 @@ void AddODDContentTest(object_ref e, uint32_t content_type) { auto item = reinterpret_cast(e->AppendItem()); assert_not_null(item); - ContentAggregateData content_aggregate_data = {}; - content_aggregate_data.device_id = - static_cast(DummyDeviceId::ODD); - content_aggregate_data.content_type = content_type; - content_aggregate_data.display_name = to_utf16(content_entry->name()); - content_aggregate_data.file_name = content_entry->name(); - content_aggregate_data.title_id = title_id; - content_aggregate_data.Write(item); + item->device_id = static_cast(DummyDeviceId::ODD); + item->content_type = content_type; + item->set_display_name(to_utf16(content_entry->name())); + item->set_file_name(content_entry->name()); + item->title_id = title_id; } } } @@ -98,25 +96,22 @@ dword_result_t XamContentAggregateCreateEnumerator(qword_t xuid, extra->magic = kXObjSignature; extra->handle = e->handle(); + auto content_type_enum = XContentType(uint32_t(content_type)); + if (!device_info || device_info->device_type == DeviceType::HDD) { // Get all content data. auto content_datas = kernel_state()->content_manager()->ListContent( - static_cast(DummyDeviceId::HDD), content_type); + static_cast(DummyDeviceId::HDD), content_type_enum); for (const auto& content_data : content_datas) { auto item = reinterpret_cast(e->AppendItem()); assert_not_null(item); - ContentAggregateData content_aggregate_data = {}; - content_aggregate_data.device_id = content_data.device_id; - content_aggregate_data.content_type = content_data.content_type; - content_aggregate_data.display_name = content_data.display_name; - content_aggregate_data.file_name = content_data.file_name; - content_aggregate_data.title_id = kernel_state()->title_id(); - content_aggregate_data.Write(item); + *item = {content_data}; + item->title_id = kernel_state()->title_id(); } } if (!device_info || device_info->device_type == DeviceType::ODD) { - AddODDContentTest(e, content_type); + AddODDContentTest(e, content_type_enum); } XELOGD("XamContentAggregateCreateEnumerator: added {} items to enumerator", diff --git a/src/xenia/vfs/devices/stfs_xbox.h b/src/xenia/vfs/devices/stfs_xbox.h index 9378f1a86..ce755ea9c 100644 --- a/src/xenia/vfs/devices/stfs_xbox.h +++ b/src/xenia/vfs/devices/stfs_xbox.h @@ -36,43 +36,6 @@ enum class XContentPackageType : uint32_t { kLive = 0x4C495645, }; -enum XContentType : uint32_t { - kSavedGame = 0x00000001, - kMarketplaceContent = 0x00000002, - kPublisher = 0x00000003, - kXbox360Title = 0x00001000, - kIptvPauseBuffer = 0x00002000, - kXNACommunity = 0x00003000, - kInstalledGame = 0x00004000, - kXboxTitle = 0x00005000, - kSocialTitle = 0x00006000, - kGamesOnDemand = 0x00007000, - kSUStoragePack = 0x00008000, - kAvatarItem = 0x00009000, - kProfile = 0x00010000, - kGamerPicture = 0x00020000, - kTheme = 0x00030000, - kCacheFile = 0x00040000, - kStorageDownload = 0x00050000, - kXboxSavedGame = 0x00060000, - kXboxDownload = 0x00070000, - kGameDemo = 0x00080000, - kVideo = 0x00090000, - kGameTitle = 0x000A0000, - kInstaller = 0x000B0000, - kGameTrailer = 0x000C0000, - kArcadeTitle = 0x000D0000, - kXNA = 0x000E0000, - kLicenseStore = 0x000F0000, - kMovie = 0x00100000, - kTV = 0x00200000, - kMusicVideo = 0x00300000, - kGameVideo = 0x00400000, - kPodcastVideo = 0x00500000, - kViralVideo = 0x00600000, - kCommunityGame = 0x02000000, -}; - enum class XContentVolumeType : uint32_t { kStfs = 0, kSvod = 1, diff --git a/src/xenia/xbox.h b/src/xenia/xbox.h index bdf6e3f38..d27d37661 100644 --- a/src/xenia/xbox.h +++ b/src/xenia/xbox.h @@ -344,6 +344,43 @@ enum class XLanguage : uint32_t { kMaxLanguages = 13 }; +enum class XContentType : uint32_t { + kSavedGame = 0x00000001, + kMarketplaceContent = 0x00000002, + kPublisher = 0x00000003, + kXbox360Title = 0x00001000, + kIptvPauseBuffer = 0x00002000, + kXNACommunity = 0x00003000, + kInstalledGame = 0x00004000, + kXboxTitle = 0x00005000, + kSocialTitle = 0x00006000, + kGamesOnDemand = 0x00007000, + kSUStoragePack = 0x00008000, + kAvatarItem = 0x00009000, + kProfile = 0x00010000, + kGamerPicture = 0x00020000, + kTheme = 0x00030000, + kCacheFile = 0x00040000, + kStorageDownload = 0x00050000, + kXboxSavedGame = 0x00060000, + kXboxDownload = 0x00070000, + kGameDemo = 0x00080000, + kVideo = 0x00090000, + kGameTitle = 0x000A0000, + kInstaller = 0x000B0000, + kGameTrailer = 0x000C0000, + kArcadeTitle = 0x000D0000, + kXNA = 0x000E0000, + kLicenseStore = 0x000F0000, + kMovie = 0x00100000, + kTV = 0x00200000, + kMusicVideo = 0x00300000, + kGameVideo = 0x00400000, + kPodcastVideo = 0x00500000, + kViralVideo = 0x00600000, + kCommunityGame = 0x02000000, +}; + } // namespace xe // clang-format on From fbf9c4630f860d805168f68f76fae2ee7d38a33c Mon Sep 17 00:00:00 2001 From: emoose Date: Tue, 15 Jun 2021 22:24:18 +0100 Subject: [PATCH 10/15] [Kernel] Use XCONTENT_AGGREGATE_DATA in all ContentManager funcs Since AGGREGATE_DATA contains extra info about title_id, and can be easily converted to/from XCONTENT_DATA. --- src/xenia/kernel/xam/content_manager.cc | 48 ++++++++++-------- src/xenia/kernel/xam/content_manager.h | 49 +++++++++++++------ src/xenia/kernel/xam/xam_content.cc | 10 ++-- src/xenia/kernel/xam/xam_content_aggregate.cc | 3 +- 4 files changed, 68 insertions(+), 42 deletions(-) diff --git a/src/xenia/kernel/xam/content_manager.cc b/src/xenia/kernel/xam/content_manager.cc index 2b3aee4c9..a4367a48b 100644 --- a/src/xenia/kernel/xam/content_manager.cc +++ b/src/xenia/kernel/xam/content_manager.cc @@ -31,7 +31,7 @@ static int content_device_id_ = 0; ContentPackage::ContentPackage(KernelState* kernel_state, const std::string_view root_name, - const XCONTENT_DATA& data, + const XCONTENT_AGGREGATE_DATA& data, const std::filesystem::path& package_path) : kernel_state_(kernel_state), root_name_(root_name) { device_path_ = fmt::format("\\Device\\Content\\{0}\\", ++content_device_id_); @@ -58,8 +58,11 @@ ContentManager::ContentManager(KernelState* kernel_state, ContentManager::~ContentManager() = default; std::filesystem::path ContentManager::ResolvePackageRoot( - XContentType content_type) { - auto title_id_str = fmt::format("{:8X}", kernel_state_->title_id()); + XContentType content_type, uint32_t title_id) { + if (title_id == kCurrentlyRunningTitleId) { + title_id = kernel_state_->title_id(); + } + auto title_id_str = fmt::format("{:08X}", title_id); auto content_type_str = fmt::format("{:08X}", uint32_t(content_type)); // Package root path: @@ -68,31 +71,36 @@ std::filesystem::path ContentManager::ResolvePackageRoot( } std::filesystem::path ContentManager::ResolvePackagePath( - const XCONTENT_DATA& data) { + const XCONTENT_AGGREGATE_DATA& data) { // Content path: // content_root/title_id/content_type/data_file_name/ - auto package_root = ResolvePackageRoot(data.content_type); + auto package_root = ResolvePackageRoot(data.content_type, data.title_id); return package_root / xe::to_path(data.file_name()); } -std::vector ContentManager::ListContent( - uint32_t device_id, XContentType content_type) { - std::vector result; +std::vector ContentManager::ListContent( + uint32_t device_id, XContentType content_type, uint32_t title_id) { + std::vector result; + + if (title_id == kCurrentlyRunningTitleId) { + title_id = kernel_state_->title_id(); + } // Search path: // content_root/title_id/type_name/* - auto package_root = ResolvePackageRoot(content_type); + auto package_root = ResolvePackageRoot(content_type, title_id); auto file_infos = xe::filesystem::ListFiles(package_root); for (const auto& file_info : file_infos) { if (file_info.type != xe::filesystem::FileInfo::Type::kDirectory) { // Directories only. continue; } - XCONTENT_DATA content_data; + XCONTENT_AGGREGATE_DATA content_data; content_data.device_id = device_id; content_data.content_type = content_type; content_data.set_display_name(xe::path_to_utf16(file_info.name)); content_data.set_file_name(xe::path_to_utf8(file_info.name)); + content_data.title_id = title_id; result.emplace_back(std::move(content_data)); } @@ -100,7 +108,7 @@ std::vector ContentManager::ListContent( } std::unique_ptr ContentManager::ResolvePackage( - const std::string_view root_name, const XCONTENT_DATA& data) { + const std::string_view root_name, const XCONTENT_AGGREGATE_DATA& data) { auto package_path = ResolvePackagePath(data); if (!std::filesystem::exists(package_path)) { return nullptr; @@ -113,13 +121,13 @@ std::unique_ptr ContentManager::ResolvePackage( return package; } -bool ContentManager::ContentExists(const XCONTENT_DATA& data) { +bool ContentManager::ContentExists(const XCONTENT_AGGREGATE_DATA& data) { auto path = ResolvePackagePath(data); return std::filesystem::exists(path); } X_RESULT ContentManager::CreateContent(const std::string_view root_name, - const XCONTENT_DATA& data) { + const XCONTENT_AGGREGATE_DATA& data) { auto global_lock = global_critical_region_.Acquire(); if (open_packages_.count(string_key(root_name))) { @@ -146,7 +154,7 @@ X_RESULT ContentManager::CreateContent(const std::string_view root_name, } X_RESULT ContentManager::OpenContent(const std::string_view root_name, - const XCONTENT_DATA& data) { + const XCONTENT_AGGREGATE_DATA& data) { auto global_lock = global_critical_region_.Acquire(); if (open_packages_.count(string_key(root_name))) { @@ -185,8 +193,8 @@ X_RESULT ContentManager::CloseContent(const std::string_view root_name) { return X_ERROR_SUCCESS; } -X_RESULT ContentManager::GetContentThumbnail(const XCONTENT_DATA& data, - std::vector* buffer) { +X_RESULT ContentManager::GetContentThumbnail( + const XCONTENT_AGGREGATE_DATA& data, std::vector* buffer) { auto global_lock = global_critical_region_.Acquire(); auto package_path = ResolvePackagePath(data); auto thumb_path = package_path / kThumbnailFileName; @@ -204,8 +212,8 @@ X_RESULT ContentManager::GetContentThumbnail(const XCONTENT_DATA& data, } } -X_RESULT ContentManager::SetContentThumbnail(const XCONTENT_DATA& data, - std::vector buffer) { +X_RESULT ContentManager::SetContentThumbnail( + const XCONTENT_AGGREGATE_DATA& data, std::vector buffer) { auto global_lock = global_critical_region_.Acquire(); auto package_path = ResolvePackagePath(data); std::filesystem::create_directories(package_path); @@ -220,7 +228,7 @@ X_RESULT ContentManager::SetContentThumbnail(const XCONTENT_DATA& data, } } -X_RESULT ContentManager::DeleteContent(const XCONTENT_DATA& data) { +X_RESULT ContentManager::DeleteContent(const XCONTENT_AGGREGATE_DATA& data) { auto global_lock = global_critical_region_.Acquire(); if (IsContentOpen(data)) { @@ -245,7 +253,7 @@ std::filesystem::path ContentManager::ResolveGameUserContentPath() { return root_path_ / title_id / kGameUserContentDirName / user_name; } -bool ContentManager::IsContentOpen(const XCONTENT_DATA& data) const { +bool ContentManager::IsContentOpen(const XCONTENT_AGGREGATE_DATA& data) const { return std::any_of(open_packages_.cbegin(), open_packages_.cend(), [data](std::pair content) { return data == content.second->GetPackageContentData(); diff --git a/src/xenia/kernel/xam/content_manager.h b/src/xenia/kernel/xam/content_manager.h index f0ff9c5b9..b65c1f22c 100644 --- a/src/xenia/kernel/xam/content_manager.h +++ b/src/xenia/kernel/xam/content_manager.h @@ -31,6 +31,11 @@ namespace xe { namespace kernel { namespace xam { +// If set in XCONTENT_AGGREGATE_DATA, will be substituted with the running +// titles ID +// TODO: check if actual x360 kernel/xam has a value similar to this +constexpr uint32_t kCurrentlyRunningTitleId = 0xFFFFFFFF; + struct XCONTENT_DATA { be device_id; be content_type; @@ -90,6 +95,16 @@ static_assert_size(XCONTENT_DATA, 0x134); struct XCONTENT_AGGREGATE_DATA : XCONTENT_DATA { be title_id; + XCONTENT_AGGREGATE_DATA() = default; + XCONTENT_AGGREGATE_DATA(const XCONTENT_DATA& other) { + device_id = other.device_id; + content_type = other.content_type; + set_display_name(other.display_name()); + set_file_name(other.file_name()); + padding[0] = padding[1] = 0; + title_id = kCurrentlyRunningTitleId; + } + bool operator==(const XCONTENT_AGGREGATE_DATA& other) const { // Package is located via device_id/title_id/content_type/file_name, so only // need to compare those @@ -103,17 +118,19 @@ static_assert_size(XCONTENT_AGGREGATE_DATA, 0x138); class ContentPackage { public: ContentPackage(KernelState* kernel_state, const std::string_view root_name, - const XCONTENT_DATA& data, + const XCONTENT_AGGREGATE_DATA& data, const std::filesystem::path& package_path); ~ContentPackage(); - const XCONTENT_DATA& GetPackageContentData() const { return content_data_; } + const XCONTENT_AGGREGATE_DATA& GetPackageContentData() const { + return content_data_; + } private: KernelState* kernel_state_; std::string root_name_; std::string device_path_; - XCONTENT_DATA content_data_; + XCONTENT_AGGREGATE_DATA content_data_; }; class ContentManager { @@ -122,30 +139,32 @@ class ContentManager { const std::filesystem::path& root_path); ~ContentManager(); - std::vector ListContent(uint32_t device_id, - XContentType content_type); + std::vector ListContent(uint32_t device_id, + XContentType content_type, + uint32_t title_id = -1); std::unique_ptr ResolvePackage( - const std::string_view root_name, const XCONTENT_DATA& data); + const std::string_view root_name, const XCONTENT_AGGREGATE_DATA& data); - bool ContentExists(const XCONTENT_DATA& data); + bool ContentExists(const XCONTENT_AGGREGATE_DATA& data); X_RESULT CreateContent(const std::string_view root_name, - const XCONTENT_DATA& data); + const XCONTENT_AGGREGATE_DATA& data); X_RESULT OpenContent(const std::string_view root_name, - const XCONTENT_DATA& data); + const XCONTENT_AGGREGATE_DATA& data); X_RESULT CloseContent(const std::string_view root_name); - X_RESULT GetContentThumbnail(const XCONTENT_DATA& data, + X_RESULT GetContentThumbnail(const XCONTENT_AGGREGATE_DATA& data, std::vector* buffer); - X_RESULT SetContentThumbnail(const XCONTENT_DATA& data, + X_RESULT SetContentThumbnail(const XCONTENT_AGGREGATE_DATA& data, std::vector buffer); - X_RESULT DeleteContent(const XCONTENT_DATA& data); + X_RESULT DeleteContent(const XCONTENT_AGGREGATE_DATA& data); std::filesystem::path ResolveGameUserContentPath(); - bool IsContentOpen(const XCONTENT_DATA& data) const; + bool IsContentOpen(const XCONTENT_AGGREGATE_DATA& data) const; void CloseOpenedFilesFromContent(const std::string_view root_name); private: - std::filesystem::path ResolvePackageRoot(XContentType content_type); - std::filesystem::path ResolvePackagePath(const XCONTENT_DATA& data); + std::filesystem::path ResolvePackageRoot(XContentType content_type, + uint32_t title_id = -1); + std::filesystem::path ResolvePackagePath(const XCONTENT_AGGREGATE_DATA& data); KernelState* kernel_state_; std::filesystem::path root_path_; diff --git a/src/xenia/kernel/xam/xam_content.cc b/src/xenia/kernel/xam/xam_content.cc index 5ba1d99aa..3c52b2cbb 100644 --- a/src/xenia/kernel/xam/xam_content.cc +++ b/src/xenia/kernel/xam/xam_content.cc @@ -124,7 +124,7 @@ dword_result_t XamContentCreateEx(dword_t user_index, lpstring_t root_name, dword_t cache_size, qword_t content_size, lpvoid_t overlapped_ptr) { X_RESULT result = X_ERROR_INVALID_PARAMETER; - auto content_data = *content_data_ptr.as(); + XCONTENT_AGGREGATE_DATA content_data = *content_data_ptr.as(); auto content_manager = kernel_state()->content_manager(); bool create = false; @@ -277,7 +277,7 @@ dword_result_t XamContentGetCreator(dword_t user_index, lpunknown_t overlapped_ptr) { auto result = X_ERROR_SUCCESS; - auto content_data = *content_data_ptr.as(); + XCONTENT_AGGREGATE_DATA content_data = *content_data_ptr.as(); bool content_exists = kernel_state()->content_manager()->ContentExists(content_data); @@ -315,7 +315,7 @@ dword_result_t XamContentGetThumbnail(dword_t user_index, lpunknown_t overlapped_ptr) { assert_not_null(buffer_size_ptr); uint32_t buffer_size = *buffer_size_ptr; - auto content_data = *content_data_ptr.as(); + XCONTENT_AGGREGATE_DATA content_data = *content_data_ptr.as(); // Get thumbnail (if it exists). std::vector buffer; @@ -351,7 +351,7 @@ dword_result_t XamContentSetThumbnail(dword_t user_index, lpvoid_t content_data_ptr, lpvoid_t buffer_ptr, dword_t buffer_size, lpunknown_t overlapped_ptr) { - auto content_data = *content_data_ptr.as(); + XCONTENT_AGGREGATE_DATA content_data = *content_data_ptr.as(); // Buffer is PNG data. auto buffer = std::vector((uint8_t*)buffer_ptr, @@ -370,7 +370,7 @@ DECLARE_XAM_EXPORT1(XamContentSetThumbnail, kContent, kImplemented); dword_result_t XamContentDelete(dword_t user_index, lpvoid_t content_data_ptr, lpunknown_t overlapped_ptr) { - auto content_data = *content_data_ptr.as(); + XCONTENT_AGGREGATE_DATA content_data = *content_data_ptr.as(); auto result = kernel_state()->content_manager()->DeleteContent(content_data); diff --git a/src/xenia/kernel/xam/xam_content_aggregate.cc b/src/xenia/kernel/xam/xam_content_aggregate.cc index b7b9ba90b..043018b6c 100644 --- a/src/xenia/kernel/xam/xam_content_aggregate.cc +++ b/src/xenia/kernel/xam/xam_content_aggregate.cc @@ -105,8 +105,7 @@ dword_result_t XamContentAggregateCreateEnumerator(qword_t xuid, for (const auto& content_data : content_datas) { auto item = reinterpret_cast(e->AppendItem()); assert_not_null(item); - *item = {content_data}; - item->title_id = kernel_state()->title_id(); + *item = content_data; } } From c889a8af3f71897a1d12dd4642cb45f33529ad39 Mon Sep 17 00:00:00 2001 From: emoose Date: Tue, 15 Jun 2021 22:28:09 +0100 Subject: [PATCH 11/15] [CPU] Load alt-title-ids XEX header into XexModule::opt_alternate_title_ids_ --- src/xenia/cpu/xex_module.cc | 10 ++++++++++ src/xenia/cpu/xex_module.h | 7 +++++++ src/xenia/kernel/util/xex2_info.h | 7 +++++++ 3 files changed, 24 insertions(+) diff --git a/src/xenia/cpu/xex_module.cc b/src/xenia/cpu/xex_module.cc index 70861aa7e..83fbc2139 100644 --- a/src/xenia/cpu/xex_module.cc +++ b/src/xenia/cpu/xex_module.cc @@ -966,6 +966,16 @@ bool XexModule::LoadContinue() { return false; } + // Parse any "unsafe" headers into safer variants + xex2_opt_generic_u32* alternate_titleids; + if (GetOptHeader(xex2_header_keys::XEX_HEADER_ALTERNATE_TITLE_IDS, + &alternate_titleids)) { + auto count = alternate_titleids->count(); + for (uint32_t i = 0; i < count; i++) { + opt_alternate_title_ids_.push_back(alternate_titleids->values[i]); + } + } + // Scan and find the low/high addresses. // All code sections are continuous, so this should be easy. auto heap = memory()->LookupHeap(base_address_); diff --git a/src/xenia/cpu/xex_module.h b/src/xenia/cpu/xex_module.h index 9834a675f..cd8fc49c5 100644 --- a/src/xenia/cpu/xex_module.h +++ b/src/xenia/cpu/xex_module.h @@ -107,6 +107,10 @@ class XexModule : public xe::cpu::Module { return retval; } + std::vector opt_alternate_title_ids() const { + return opt_alternate_title_ids_; + } + const uint32_t base_address() const { return base_address_; } const bool is_dev_kit() const { return is_dev_kit_; } @@ -198,6 +202,9 @@ class XexModule : public xe::cpu::Module { import_libs_; // pre-loaded import libraries for ease of use std::vector pe_sections_; + // XEX_HEADER_ALTERNATE_TITLE_IDS loaded into a safe std::vector + std::vector opt_alternate_title_ids_; + uint8_t session_key_[0x10]; bool is_dev_kit_ = false; diff --git a/src/xenia/kernel/util/xex2_info.h b/src/xenia/kernel/util/xex2_info.h index a5b5e5a33..6b77476d8 100644 --- a/src/xenia/kernel/util/xex2_info.h +++ b/src/xenia/kernel/util/xex2_info.h @@ -529,6 +529,13 @@ struct xex2_import_library { } }; +struct xex2_opt_generic_u32 { + xe::be size; + xe::be values[1]; + + uint32_t count() const { return (size - 4) / 4; } +}; + struct xex2_opt_header { xe::be key; // 0x0 From 716d4c412a77240902985d1038cc2dc724095266 Mon Sep 17 00:00:00 2001 From: emoose Date: Tue, 15 Jun 2021 22:29:29 +0100 Subject: [PATCH 12/15] [Kernel] Let XamContentAggregate.. make use of XEX alt-title-ids header --- src/xenia/kernel/xam/xam_content_aggregate.cc | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/xenia/kernel/xam/xam_content_aggregate.cc b/src/xenia/kernel/xam/xam_content_aggregate.cc index 043018b6c..3de7b1566 100644 --- a/src/xenia/kernel/xam/xam_content_aggregate.cc +++ b/src/xenia/kernel/xam/xam_content_aggregate.cc @@ -11,6 +11,7 @@ #include "xenia/base/math.h" #include "xenia/base/string_util.h" #include "xenia/kernel/kernel_state.h" +#include "xenia/kernel/user_module.h" #include "xenia/kernel/util/shim_utils.h" #include "xenia/kernel/xam/xam_content_device.h" #include "xenia/kernel/xam/xam_private.h" @@ -99,13 +100,26 @@ dword_result_t XamContentAggregateCreateEnumerator(qword_t xuid, auto content_type_enum = XContentType(uint32_t(content_type)); if (!device_info || device_info->device_type == DeviceType::HDD) { - // Get all content data. - auto content_datas = kernel_state()->content_manager()->ListContent( - static_cast(DummyDeviceId::HDD), content_type_enum); - for (const auto& content_data : content_datas) { - auto item = reinterpret_cast(e->AppendItem()); - assert_not_null(item); - *item = content_data; + // Fetch any alternate title IDs defined in the XEX header + // (used by games to load saves from other titles, etc) + std::vector title_ids{kCurrentlyRunningTitleId}; + auto exe_module = kernel_state()->GetExecutableModule(); + if (exe_module && exe_module->xex_module()) { + const auto& alt_ids = exe_module->xex_module()->opt_alternate_title_ids(); + std::copy(alt_ids.cbegin(), alt_ids.cend(), + std::back_inserter(title_ids)); + } + + for (auto& title_id : title_ids) { + // Get all content data. + auto content_datas = kernel_state()->content_manager()->ListContent( + static_cast(DummyDeviceId::HDD), content_type_enum, + title_id); + for (const auto& content_data : content_datas) { + auto item = reinterpret_cast(e->AppendItem()); + assert_not_null(item); + *item = content_data; + } } } From 9503c9efaf3add6d3b4357678779810922c55397 Mon Sep 17 00:00:00 2001 From: emoose Date: Tue, 15 Jun 2021 22:33:45 +0100 Subject: [PATCH 13/15] [Kernel] Move title_id field in XCONTENT_AGGREGATE_DATA struct XamAppEnumerateContentAggregate seems to store title_id at 0x140, so I've moved title_id to there, now AGGREGATE_DATA seems to match the size of the struct used by XamContentCreateInternal. Also removed unneeded/useless checks inside XamAppEnumerateContentAggregate. --- src/xenia/kernel/xam/apps/xam_app.cc | 9 ++------- src/xenia/kernel/xam/content_manager.h | 4 +++- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/xenia/kernel/xam/apps/xam_app.cc b/src/xenia/kernel/xam/apps/xam_app.cc index a565a49ac..664e1e60f 100644 --- a/src/xenia/kernel/xam/apps/xam_app.cc +++ b/src/xenia/kernel/xam/apps/xam_app.cc @@ -59,13 +59,8 @@ X_HRESULT XamApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, uint32_t item_count = 0; auto result = e->WriteItems(data->buffer_ptr, buffer, data->buffer_size, &item_count); - assert_true(XSUCCEEDED(result)); - assert_true(item_count <= 1); - if (XSUCCEEDED(result) && item_count == 1) { - auto content_data = reinterpret_cast(buffer); - // TODO(gibbed): WTF? - *reinterpret_cast*>(&buffer[0x140]) = - content_data->title_id; + + if (result == X_ERROR_SUCCESS && item_count >= 1) { if (data->length_ptr) { auto length_ptr = memory_->TranslateVirtual*>(data->length_ptr); diff --git a/src/xenia/kernel/xam/content_manager.h b/src/xenia/kernel/xam/content_manager.h index b65c1f22c..4876bfd1c 100644 --- a/src/xenia/kernel/xam/content_manager.h +++ b/src/xenia/kernel/xam/content_manager.h @@ -93,6 +93,7 @@ struct XCONTENT_DATA { static_assert_size(XCONTENT_DATA, 0x134); struct XCONTENT_AGGREGATE_DATA : XCONTENT_DATA { + be unk134; // some titles store XUID here? be title_id; XCONTENT_AGGREGATE_DATA() = default; @@ -102,6 +103,7 @@ struct XCONTENT_AGGREGATE_DATA : XCONTENT_DATA { set_display_name(other.display_name()); set_file_name(other.file_name()); padding[0] = padding[1] = 0; + unk134 = 0; title_id = kCurrentlyRunningTitleId; } @@ -113,7 +115,7 @@ struct XCONTENT_AGGREGATE_DATA : XCONTENT_DATA { file_name() == other.file_name(); } }; -static_assert_size(XCONTENT_AGGREGATE_DATA, 0x138); +static_assert_size(XCONTENT_AGGREGATE_DATA, 0x148); class ContentPackage { public: From 89e26d7b2bd86f3420652ee069c1cd0bfa0d8781 Mon Sep 17 00:00:00 2001 From: emoose Date: Tue, 15 Jun 2021 22:38:59 +0100 Subject: [PATCH 14/15] [Kernel] XamContentCreate*: pass XCONTENT_DATA size along to main content-creation func This lets funcs like XamContentCreateInternal pass along a different XCONTENT_AGGREGATE_DATA struct, which the main func will know what to do with --- src/xenia/kernel/xam/xam_content.cc | 36 +++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/src/xenia/kernel/xam/xam_content.cc b/src/xenia/kernel/xam/xam_content.cc index 3c52b2cbb..b708b163b 100644 --- a/src/xenia/kernel/xam/xam_content.cc +++ b/src/xenia/kernel/xam/xam_content.cc @@ -117,14 +117,23 @@ dword_result_t XamContentCreateEnumerator(dword_t user_index, dword_t device_id, } DECLARE_XAM_EXPORT1(XamContentCreateEnumerator, kContent, kImplemented); -dword_result_t XamContentCreateEx(dword_t user_index, lpstring_t root_name, - lpvoid_t content_data_ptr, dword_t flags, +dword_result_t xeXamContentCreate(dword_t user_index, lpstring_t root_name, + lpvoid_t content_data_ptr, + dword_t content_data_size, dword_t flags, lpdword_t disposition_ptr, lpdword_t license_mask_ptr, dword_t cache_size, qword_t content_size, lpvoid_t overlapped_ptr) { X_RESULT result = X_ERROR_INVALID_PARAMETER; - XCONTENT_AGGREGATE_DATA content_data = *content_data_ptr.as(); + XCONTENT_AGGREGATE_DATA content_data; + if (content_data_size == sizeof(XCONTENT_DATA)) { + content_data = *content_data_ptr.as(); + } else if (content_data_size == sizeof(XCONTENT_AGGREGATE_DATA)) { + content_data = *content_data_ptr.as(); + } else { + assert_always(); + return result; + } auto content_manager = kernel_state()->content_manager(); bool create = false; @@ -210,6 +219,18 @@ dword_result_t XamContentCreateEx(dword_t user_index, lpstring_t root_name, return result; } } + +dword_result_t XamContentCreateEx(dword_t user_index, lpstring_t root_name, + lpvoid_t content_data_ptr, dword_t flags, + lpdword_t disposition_ptr, + lpdword_t license_mask_ptr, + dword_t cache_size, qword_t content_size, + lpvoid_t overlapped_ptr) { + return xeXamContentCreate(user_index, root_name, content_data_ptr, + sizeof(XCONTENT_DATA), flags, disposition_ptr, + license_mask_ptr, cache_size, content_size, + overlapped_ptr); +} DECLARE_XAM_EXPORT1(XamContentCreateEx, kContent, kImplemented); dword_result_t XamContentCreate(dword_t user_index, lpstring_t root_name, @@ -217,9 +238,9 @@ dword_result_t XamContentCreate(dword_t user_index, lpstring_t root_name, lpdword_t disposition_ptr, lpdword_t license_mask_ptr, lpvoid_t overlapped_ptr) { - return XamContentCreateEx(user_index, root_name, content_data_ptr, flags, - disposition_ptr, license_mask_ptr, 0, 0, - overlapped_ptr); + return xeXamContentCreate(user_index, root_name, content_data_ptr, + sizeof(XCONTENT_DATA), flags, disposition_ptr, + license_mask_ptr, 0, 0, overlapped_ptr); } DECLARE_XAM_EXPORT1(XamContentCreate, kContent, kImplemented); @@ -227,7 +248,8 @@ dword_result_t XamContentCreateInternal( lpstring_t root_name, lpvoid_t content_data_ptr, dword_t flags, lpdword_t disposition_ptr, lpdword_t license_mask_ptr, dword_t cache_size, qword_t content_size, lpvoid_t overlapped_ptr) { - return XamContentCreateEx(0xFE, root_name, content_data_ptr, flags, + return xeXamContentCreate(0xFE, root_name, content_data_ptr, + sizeof(XCONTENT_AGGREGATE_DATA), flags, disposition_ptr, license_mask_ptr, cache_size, content_size, overlapped_ptr); } From 2ac388527b18b347df2702a105b5d1686609f675 Mon Sep 17 00:00:00 2001 From: gibbed Date: Sat, 26 Jun 2021 00:56:18 -0500 Subject: [PATCH 15/15] Disable Edit and Continue for test suites. Disable Edit and Continue for test suites. Edit and Continue in MSVC can cause the __LINE__ macro to produce invalid values, which breaks the usability of Catch2 output on failed tests. --- tools/build/scripts/test_suite.lua | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tools/build/scripts/test_suite.lua b/tools/build/scripts/test_suite.lua index afb8a9b31..563624c09 100644 --- a/tools/build/scripts/test_suite.lua +++ b/tools/build/scripts/test_suite.lua @@ -34,6 +34,11 @@ local function combined_test_suite(test_suite_name, project_root, base_path, con project_root.."/src/xenia/base/main_"..platform_suffix..".cc", base_path.."/**_test.cc", }) + filter("toolset:msc") + -- Edit and Continue in MSVC can cause the __LINE__ macro to produce + -- invalid values, which breaks the usability of Catch2 output on + -- failed tests. + editAndContinue("Off") end local function split_test_suite(test_suite_name, project_root, base_path, config) @@ -58,6 +63,11 @@ local function split_test_suite(test_suite_name, project_root, base_path, config project_root.."/"..build_tools_src.."/test_suite_main.cc", file_path, }) + filter("toolset:msc") + -- Edit and Continue in MSVC can cause the __LINE__ macro to produce + -- invalid values, which breaks the usability of Catch2 output on + -- failed tests. + editAndContinue("Off") end end