diff --git a/src/xenia/base/mutex.h b/src/xenia/base/mutex.h index 98fb6c548..71d6bd26e 100644 --- a/src/xenia/base/mutex.h +++ b/src/xenia/base/mutex.h @@ -69,6 +69,11 @@ class global_critical_region { return std::unique_lock(mutex()); } + // Acquires a deferred lock on the global critical section. + inline std::unique_lock AcquireDeferred() { + return std::unique_lock(mutex(), std::defer_lock); + } + // Tries to acquire a lock on the glboal critical section. // Check owns_lock() to see if the lock was successfully acquired. inline std::unique_lock TryAcquire() { diff --git a/src/xenia/base/string_util.h b/src/xenia/base/string_util.h index adb2012af..59d6e90f6 100644 --- a/src/xenia/base/string_util.h +++ b/src/xenia/base/string_util.h @@ -34,6 +34,11 @@ namespace xe { namespace string_util { +enum class Safety { + IDontKnowWhatIAmDoing, + IKnowWhatIAmDoing, +}; + inline size_t copy_truncating(char* dest, const std::string_view source, size_t dest_buffer_count) { if (!dest_buffer_count) { @@ -68,6 +73,44 @@ inline size_t copy_and_swap_truncating(char16_t* dest, return chars_copied; } +template +inline size_t copy_maybe_truncating(char* dest, const std::string_view source, + size_t dest_buffer_count) { + static_assert(safety == Safety::IKnowWhatIAmDoing); + if (!dest_buffer_count) { + return 0; + } + size_t chars_copied = std::min(source.size(), dest_buffer_count); + std::memcpy(dest, source.data(), chars_copied); + return chars_copied; +} + +template +inline size_t copy_maybe_truncating(char16_t* dest, + const std::u16string_view source, + size_t dest_buffer_count) { + static_assert(safety == Safety::IKnowWhatIAmDoing); + if (!dest_buffer_count) { + return 0; + } + size_t chars_copied = std::min(source.size(), dest_buffer_count); + std::memcpy(dest, source.data(), chars_copied * sizeof(char16_t)); + return chars_copied; +} + +template +inline size_t copy_and_swap_maybe_truncating(char16_t* dest, + const std::u16string_view source, + size_t dest_buffer_count) { + static_assert(safety == Safety::IKnowWhatIAmDoing); + if (!dest_buffer_count) { + return 0; + } + size_t chars_copied = std::min(source.size(), dest_buffer_count); + xe::copy_and_swap(dest, source.data(), chars_copied); + return chars_copied; +} + inline std::string to_hex_string(uint32_t value) { return fmt::format("{:08X}", value); } diff --git a/src/xenia/cpu/backend/x64/x64_sequences.cc b/src/xenia/cpu/backend/x64/x64_sequences.cc index f6a688b09..37a05c60b 100644 --- a/src/xenia/cpu/backend/x64/x64_sequences.cc +++ b/src/xenia/cpu/backend/x64/x64_sequences.cc @@ -2460,7 +2460,11 @@ struct LOG2_F32 : Sequence> { } static void Emit(X64Emitter& e, const EmitArgType& i) { assert_always(); - e.lea(e.GetNativeParam(0), e.StashXmm(0, i.src1)); + if (i.src1.is_constant) { + e.lea(e.GetNativeParam(0), e.StashConstantXmm(0, i.src1.constant())); + } else { + e.lea(e.GetNativeParam(0), e.StashXmm(0, i.src1)); + } e.CallNativeSafe(reinterpret_cast(EmulateLog2)); e.vmovaps(i.dest, e.xmm0); } @@ -2474,7 +2478,11 @@ struct LOG2_F64 : Sequence> { } static void Emit(X64Emitter& e, const EmitArgType& i) { assert_always(); - e.lea(e.GetNativeParam(0), e.StashXmm(0, i.src1)); + if (i.src1.is_constant) { + e.lea(e.GetNativeParam(0), e.StashConstantXmm(0, i.src1.constant())); + } else { + e.lea(e.GetNativeParam(0), e.StashXmm(0, i.src1)); + } e.CallNativeSafe(reinterpret_cast(EmulateLog2)); e.vmovaps(i.dest, e.xmm0); } @@ -2489,7 +2497,11 @@ struct LOG2_V128 : Sequence> { return _mm_load_ps(values); } static void Emit(X64Emitter& e, const EmitArgType& i) { - e.lea(e.GetNativeParam(0), e.StashXmm(0, i.src1)); + if (i.src1.is_constant) { + e.lea(e.GetNativeParam(0), e.StashConstantXmm(0, i.src1.constant())); + } else { + e.lea(e.GetNativeParam(0), e.StashXmm(0, i.src1)); + } e.CallNativeSafe(reinterpret_cast(EmulateLog2)); e.vmovaps(i.dest, e.xmm0); } diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc index aca6bd52a..44f284d34 100644 --- a/src/xenia/emulator.cc +++ b/src/xenia/emulator.cc @@ -184,7 +184,10 @@ X_STATUS Emulator::Setup( if (input_driver_factory) { auto input_drivers = input_driver_factory(display_window_); for (size_t i = 0; i < input_drivers.size(); ++i) { - input_system_->AddDriver(std::move(input_drivers[i])); + auto& input_driver = input_drivers[i]; + input_driver->set_is_active_callback( + []() -> bool { return !xe::kernel::xam::xeXamIsUIActive(); }); + input_system_->AddDriver(std::move(input_driver)); } } diff --git a/src/xenia/hid/input_driver.h b/src/xenia/hid/input_driver.h index 6de322ab1..aef8cffeb 100644 --- a/src/xenia/hid/input_driver.h +++ b/src/xenia/hid/input_driver.h @@ -10,7 +10,10 @@ #ifndef XENIA_HID_INPUT_DRIVER_H_ #define XENIA_HID_INPUT_DRIVER_H_ +#include + #include "xenia/hid/input.h" +#include "xenia/ui/window.h" #include "xenia/xbox.h" namespace xe { @@ -38,10 +41,22 @@ class InputDriver { virtual X_RESULT GetKeystroke(uint32_t user_index, uint32_t flags, X_INPUT_KEYSTROKE* out_keystroke) = 0; + void set_is_active_callback(std::function is_active_callback) { + is_active_callback_ = is_active_callback; + } + + private: + xe::ui::Window* window_ = nullptr; + std::function is_active_callback_ = nullptr; + protected: explicit InputDriver(xe::ui::Window* window); - xe::ui::Window* window_ = nullptr; + xe::ui::Window* window() const { return window_; } + + bool is_active() const { + return !is_active_callback_ || is_active_callback_(); + } }; } // namespace hid diff --git a/src/xenia/hid/sdl/sdl_input_driver.cc b/src/xenia/hid/sdl/sdl_input_driver.cc index 470892588..fb77397da 100644 --- a/src/xenia/hid/sdl/sdl_input_driver.cc +++ b/src/xenia/hid/sdl/sdl_input_driver.cc @@ -65,7 +65,7 @@ X_STATUS SDLInputDriver::Setup() { // SDL_PumpEvents should only be run in the thread that initialized SDL - we // are hijacking the window loop thread for that. - window_->loop()->PostSynchronous([&]() { + window()->loop()->PostSynchronous([&]() { if (!xe::helper::sdl::SDLHelper::Prepare()) { return; } @@ -385,7 +385,7 @@ X_RESULT SDLInputDriver::GetKeystroke(uint32_t users, uint32_t flags, } void SDLInputDriver::OnControllerDeviceAdded(SDL_Event* event) { - assert(window_->loop()->is_on_loop_thread()); + assert(window()->loop()->is_on_loop_thread()); std::unique_lock guard(controllers_mutex_); // Open the controller. @@ -424,7 +424,7 @@ void SDLInputDriver::OnControllerDeviceAdded(SDL_Event* event) { } void SDLInputDriver::OnControllerDeviceRemoved(SDL_Event* event) { - assert(window_->loop()->is_on_loop_thread()); + assert(window()->loop()->is_on_loop_thread()); std::unique_lock guard(controllers_mutex_); // Find the disconnected gamecontroller and close it. @@ -436,7 +436,7 @@ void SDLInputDriver::OnControllerDeviceRemoved(SDL_Event* event) { } void SDLInputDriver::OnControllerDeviceAxisMotion(SDL_Event* event) { - assert(window_->loop()->is_on_loop_thread()); + assert(window()->loop()->is_on_loop_thread()); std::unique_lock guard(controllers_mutex_); auto [found, i] = GetControllerIndexFromInstanceID(event->caxis.which); @@ -469,7 +469,7 @@ void SDLInputDriver::OnControllerDeviceAxisMotion(SDL_Event* event) { } void SDLInputDriver::OnControllerDeviceButtonChanged(SDL_Event* event) { - assert(window_->loop()->is_on_loop_thread()); + assert(window()->loop()->is_on_loop_thread()); std::unique_lock guard(controllers_mutex_); // Define a lookup table to map between SDL and XInput button codes. @@ -569,7 +569,7 @@ void SDLInputDriver::QueueControllerUpdate() { bool is_queued = false; sdl_pumpevents_queued_.compare_exchange_strong(is_queued, true); if (!is_queued) { - window_->loop()->Post([this]() { + window()->loop()->Post([this]() { SDL_PumpEvents(); sdl_pumpevents_queued_ = false; }); diff --git a/src/xenia/hid/winkey/winkey_input_driver.cc b/src/xenia/hid/winkey/winkey_input_driver.cc index 30ce09309..18e192578 100644 --- a/src/xenia/hid/winkey/winkey_input_driver.cc +++ b/src/xenia/hid/winkey/winkey_input_driver.cc @@ -21,7 +21,11 @@ namespace winkey { WinKeyInputDriver::WinKeyInputDriver(xe::ui::Window* window) : InputDriver(window), packet_number_(1) { // Register a key listener. - window_->on_key_down.AddListener([this](ui::KeyEvent* evt) { + window->on_key_down.AddListener([this](ui::KeyEvent* evt) { + if (!is_active()) { + return; + } + auto global_lock = global_critical_region_.Acquire(); KeyEvent key; @@ -31,7 +35,11 @@ WinKeyInputDriver::WinKeyInputDriver(xe::ui::Window* window) key.repeat_count = evt->repeat_count(); key_events_.push(key); }); - window_->on_key_up.AddListener([this](ui::KeyEvent* evt) { + window->on_key_up.AddListener([this](ui::KeyEvent* evt) { + if (!is_active()) { + return; + } + auto global_lock = global_critical_region_.Acquire(); KeyEvent key; @@ -88,7 +96,7 @@ X_RESULT WinKeyInputDriver::GetState(uint32_t user_index, int16_t thumb_rx = 0; int16_t thumb_ry = 0; - if (window_->has_focus()) { + if (window()->has_focus() && is_active()) { if (IS_KEY_TOGGLED(VK_CAPITAL) || IS_KEY_DOWN(VK_SHIFT)) { // dpad toggled if (IS_KEY_DOWN('A')) { @@ -227,6 +235,10 @@ X_RESULT WinKeyInputDriver::GetKeystroke(uint32_t user_index, uint32_t flags, return X_ERROR_DEVICE_NOT_CONNECTED; } + if (!is_active()) { + return X_ERROR_EMPTY; + } + X_RESULT result = X_ERROR_EMPTY; uint16_t virtual_key = 0; diff --git a/src/xenia/kernel/kernel_state.cc b/src/xenia/kernel/kernel_state.cc index 3d7df1824..a482b2ca4 100644 --- a/src/xenia/kernel/kernel_state.cc +++ b/src/xenia/kernel/kernel_state.cc @@ -326,16 +326,20 @@ void KernelState::SetExecutableModule(object_ref module) { // As we run guest callbacks the debugger must be able to suspend us. dispatch_thread_->set_can_debugger_suspend(true); + auto global_lock = global_critical_region_.AcquireDeferred(); while (dispatch_thread_running_) { - auto global_lock = global_critical_region_.Acquire(); + global_lock.lock(); if (dispatch_queue_.empty()) { dispatch_cond_.wait(global_lock); if (!dispatch_thread_running_) { + global_lock.unlock(); break; } } auto fn = std::move(dispatch_queue_.front()); dispatch_queue_.pop_front(); + global_lock.unlock(); + fn(); } return 0; @@ -637,9 +641,8 @@ void KernelState::UnregisterNotifyListener(XNotifyListener* listener) { void KernelState::BroadcastNotification(XNotificationID id, uint32_t data) { auto global_lock = global_critical_region_.Acquire(); - for (auto it = notify_listeners_.begin(); it != notify_listeners_.end(); - ++it) { - (*it)->EnqueueNotification(id, data); + for (const auto& notify_listener : notify_listeners_) { + notify_listener->EnqueueNotification(id, data); } } @@ -657,6 +660,7 @@ void KernelState::CompleteOverlappedEx(uint32_t overlapped_ptr, X_RESULT result, X_HANDLE event_handle = XOverlappedGetEvent(ptr); if (event_handle) { auto ev = object_table()->LookupObject(event_handle); + assert_not_null(ev); if (ev) { ev->Set(0, false); } @@ -692,24 +696,62 @@ void KernelState::CompleteOverlappedImmediateEx(uint32_t overlapped_ptr, void KernelState::CompleteOverlappedDeferred( std::function completion_callback, uint32_t overlapped_ptr, - X_RESULT result) { + X_RESULT result, std::function pre_callback, + std::function post_callback) { CompleteOverlappedDeferredEx(std::move(completion_callback), overlapped_ptr, - result, result, 0); + result, result, 0, pre_callback, post_callback); } void KernelState::CompleteOverlappedDeferredEx( std::function completion_callback, uint32_t overlapped_ptr, - X_RESULT result, uint32_t extended_error, uint32_t length) { + X_RESULT result, uint32_t extended_error, uint32_t length, + std::function pre_callback, std::function post_callback) { + CompleteOverlappedDeferredEx( + [completion_callback, result, extended_error, length]( + uint32_t& cb_extended_error, uint32_t& cb_length) -> X_RESULT { + completion_callback(); + cb_extended_error = extended_error; + cb_length = length; + return result; + }, + overlapped_ptr, pre_callback, post_callback); +} + +void KernelState::CompleteOverlappedDeferred( + std::function completion_callback, uint32_t overlapped_ptr, + std::function pre_callback, std::function post_callback) { + CompleteOverlappedDeferredEx( + [completion_callback](uint32_t& extended_error, + uint32_t& length) -> X_RESULT { + auto result = completion_callback(); + extended_error = static_cast(result); + length = 0; + return result; + }, + overlapped_ptr, pre_callback, post_callback); +} + +void KernelState::CompleteOverlappedDeferredEx( + std::function completion_callback, + uint32_t overlapped_ptr, std::function pre_callback, + std::function post_callback) { auto ptr = memory()->TranslateVirtual(overlapped_ptr); XOverlappedSetResult(ptr, X_ERROR_IO_PENDING); XOverlappedSetContext(ptr, XThread::GetCurrentThreadHandle()); auto global_lock = global_critical_region_.Acquire(); - dispatch_queue_.push_back([this, completion_callback, overlapped_ptr, result, - extended_error, length]() { + dispatch_queue_.push_back([this, completion_callback, overlapped_ptr, + pre_callback, post_callback]() { + if (pre_callback) { + pre_callback(); + } xe::threading::Sleep( std::chrono::milliseconds(kDeferredOverlappedDelayMillis)); - completion_callback(); + uint32_t extended_error, length; + auto result = completion_callback(extended_error, length); CompleteOverlappedEx(overlapped_ptr, result, extended_error, length); + if (post_callback) { + post_callback(); + } }); dispatch_cond_.notify_all(); } diff --git a/src/xenia/kernel/kernel_state.h b/src/xenia/kernel/kernel_state.h index 32d4e2a91..2b4187a07 100644 --- a/src/xenia/kernel/kernel_state.h +++ b/src/xenia/kernel/kernel_state.h @@ -167,14 +167,29 @@ class KernelState { void CompleteOverlapped(uint32_t overlapped_ptr, X_RESULT result); void CompleteOverlappedEx(uint32_t overlapped_ptr, X_RESULT result, uint32_t extended_error, uint32_t length); + void CompleteOverlappedImmediate(uint32_t overlapped_ptr, X_RESULT result); void CompleteOverlappedImmediateEx(uint32_t overlapped_ptr, X_RESULT result, uint32_t extended_error, uint32_t length); - void CompleteOverlappedDeferred(std::function completion_callback, - uint32_t overlapped_ptr, X_RESULT result); - void CompleteOverlappedDeferredEx(std::function completion_callback, - uint32_t overlapped_ptr, X_RESULT result, - uint32_t extended_error, uint32_t length); + + void CompleteOverlappedDeferred( + std::function completion_callback, uint32_t overlapped_ptr, + X_RESULT result, std::function pre_callback = nullptr, + std::function post_callback = nullptr); + void CompleteOverlappedDeferredEx( + std::function completion_callback, uint32_t overlapped_ptr, + X_RESULT result, uint32_t extended_error, uint32_t length, + std::function pre_callback = nullptr, + std::function post_callback = nullptr); + + void CompleteOverlappedDeferred( + std::function completion_callback, uint32_t overlapped_ptr, + std::function pre_callback = nullptr, + std::function post_callback = nullptr); + void CompleteOverlappedDeferredEx( + std::function completion_callback, + uint32_t overlapped_ptr, std::function pre_callback = nullptr, + std::function post_callback = nullptr); bool Save(ByteStream* stream); bool Restore(ByteStream* stream); diff --git a/src/xenia/kernel/xam/app_manager.cc b/src/xenia/kernel/xam/app_manager.cc index a8c5a9501..b9727eb4b 100644 --- a/src/xenia/kernel/xam/app_manager.cc +++ b/src/xenia/kernel/xam/app_manager.cc @@ -37,22 +37,22 @@ void AppManager::RegisterApp(std::unique_ptr app) { apps_.push_back(std::move(app)); } -X_RESULT AppManager::DispatchMessageSync(uint32_t app_id, uint32_t message, - uint32_t buffer_ptr, - uint32_t buffer_length) { - const auto& it = app_lookup_.find(app_id); - if (it == app_lookup_.end()) { - return X_STATUS_UNSUCCESSFUL; - } - return it->second->DispatchMessageSync(message, buffer_ptr, buffer_length); -} - -X_RESULT AppManager::DispatchMessageAsync(uint32_t app_id, uint32_t message, +X_HRESULT AppManager::DispatchMessageSync(uint32_t app_id, uint32_t message, uint32_t buffer_ptr, uint32_t buffer_length) { const auto& it = app_lookup_.find(app_id); if (it == app_lookup_.end()) { - return X_ERROR_NOT_FOUND; + return X_E_NOTFOUND; + } + return it->second->DispatchMessageSync(message, buffer_ptr, buffer_length); +} + +X_HRESULT AppManager::DispatchMessageAsync(uint32_t app_id, uint32_t message, + uint32_t buffer_ptr, + uint32_t buffer_length) { + const auto& it = app_lookup_.find(app_id); + if (it == app_lookup_.end()) { + return X_E_NOTFOUND; } return it->second->DispatchMessageSync(message, buffer_ptr, buffer_length); } diff --git a/src/xenia/kernel/xam/app_manager.h b/src/xenia/kernel/xam/app_manager.h index 8a7655406..40c7ddf53 100644 --- a/src/xenia/kernel/xam/app_manager.h +++ b/src/xenia/kernel/xam/app_manager.h @@ -32,8 +32,8 @@ class App { public: uint32_t app_id() const { return app_id_; } - virtual X_RESULT DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, - uint32_t buffer_length) = 0; + virtual X_HRESULT DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, + uint32_t buffer_length) = 0; virtual ~App() = default; @@ -51,10 +51,10 @@ class AppManager { void RegisterApp(std::unique_ptr app); - X_RESULT DispatchMessageSync(uint32_t app_id, uint32_t message, - uint32_t buffer_ptr, uint32_t buffer_length); - X_RESULT DispatchMessageAsync(uint32_t app_id, uint32_t message, + X_HRESULT DispatchMessageSync(uint32_t app_id, uint32_t message, uint32_t buffer_ptr, uint32_t buffer_length); + X_HRESULT DispatchMessageAsync(uint32_t app_id, uint32_t message, + uint32_t buffer_ptr, uint32_t buffer_length); private: std::vector> apps_; diff --git a/src/xenia/kernel/xam/apps/xam_app.cc b/src/xenia/kernel/xam/apps/xam_app.cc index 975c8e42e..1d57001c7 100644 --- a/src/xenia/kernel/xam/apps/xam_app.cc +++ b/src/xenia/kernel/xam/apps/xam_app.cc @@ -11,6 +11,8 @@ #include "xenia/base/logging.h" #include "xenia/base/threading.h" +#include "xenia/kernel/kernel_state.h" +#include "xenia/kernel/xenumerator.h" namespace xe { namespace kernel { @@ -19,11 +21,55 @@ namespace apps { XamApp::XamApp(KernelState* kernel_state) : App(kernel_state, 0xFE) {} -X_RESULT XamApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, - uint32_t buffer_length) { +X_HRESULT XamApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, + uint32_t buffer_length) { // NOTE: buffer_length may be zero or valid. auto buffer = memory_->TranslateVirtual(buffer_ptr); switch (message) { + case 0x0002000E: { + struct message_data { + xe::be user_index; + xe::be unk_04; + xe::be extra_ptr; + xe::be buffer_ptr; + xe::be buffer_size; + xe::be unk_14; + xe::be length_ptr; + xe::be unk_1C; + }* data = reinterpret_cast(buffer); + XELOGD( + "XamAppEnumerateContentAggregate({}, {:08X}, {:08X}, {:08X}, {}, " + "{:08X}, {:08X}, {:08X})", + (uint32_t)data->user_index, (uint32_t)data->unk_04, + (uint32_t)data->extra_ptr, (uint32_t)data->buffer_ptr, + (uint32_t)data->buffer_size, (uint32_t)data->unk_14, + (uint32_t)data->length_ptr, (uint32_t)data->unk_1C); + auto extra = memory_->TranslateVirtual( + data->extra_ptr); + auto buffer = memory_->TranslateVirtual(data->buffer_ptr); + auto e = kernel_state_->object_table()->LookupObject( + extra->handle); + if (!e || !buffer || !extra) { + return X_E_INVALIDARG; + } + assert_true(extra->magic == 'XEN\0'); + if (data->buffer_size) { + std::memset(buffer, 0, data->buffer_size); + } + if (e->WriteItem(buffer)) { + auto content_data = reinterpret_cast(buffer); + // TODO(gibbed): WTF? + *reinterpret_cast*>(&buffer[0x140]) = + content_data->title_id; + if (data->length_ptr) { + auto length_ptr = + memory_->TranslateVirtual*>(data->length_ptr); + *length_ptr = 1; + } + return X_E_SUCCESS; + } + return X_E_NO_MORE_FILES; + } case 0x00020021: { struct message_data { char unk_00[64]; @@ -37,11 +83,11 @@ X_RESULT XamApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, XELOGD("XamApp(0x00020021)('{}', {:08X}, {:08X}, {:08X})", data->unk_00, (uint32_t)data->unk_40, (uint32_t)data->unk_44, (uint32_t)data->unk_48); - return X_ERROR_SUCCESS; + return X_E_SUCCESS; } case 0x00021012: { XELOGD("XamApp(0x00021012)"); - return X_ERROR_SUCCESS; + return X_E_SUCCESS; } case 0x00022005: { struct message_data { @@ -53,14 +99,14 @@ X_RESULT XamApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, auto adr = *unk; XELOGD("XamApp(0x00022005)(%.8X, %.8X)", (uint32_t)data->unk_00, (uint32_t)data->unk_04); - return X_ERROR_SUCCESS; + return X_E_SUCCESS; } } XELOGE( "Unimplemented XAM message app={:08X}, msg={:08X}, arg1={:08X}, " "arg2={:08X}", app_id(), message, buffer_ptr, buffer_length); - return X_STATUS_UNSUCCESSFUL; + return X_E_FAIL; } } // namespace apps diff --git a/src/xenia/kernel/xam/apps/xam_app.h b/src/xenia/kernel/xam/apps/xam_app.h index 6e653f384..6f9e52008 100644 --- a/src/xenia/kernel/xam/apps/xam_app.h +++ b/src/xenia/kernel/xam/apps/xam_app.h @@ -22,8 +22,8 @@ class XamApp : public App { public: explicit XamApp(KernelState* kernel_state); - X_RESULT DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, - uint32_t buffer_length) override; + X_HRESULT DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, + uint32_t buffer_length) override; }; } // namespace apps diff --git a/src/xenia/kernel/xam/apps/xgi_app.cc b/src/xenia/kernel/xam/apps/xgi_app.cc index b87a906c5..c77665209 100644 --- a/src/xenia/kernel/xam/apps/xgi_app.cc +++ b/src/xenia/kernel/xam/apps/xgi_app.cc @@ -21,8 +21,8 @@ XgiApp::XgiApp(KernelState* kernel_state) : App(kernel_state, 0xFB) {} // http://mb.mirage.org/bugzilla/xliveless/main.c -X_RESULT XgiApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, - uint32_t buffer_length) { +X_HRESULT XgiApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, + uint32_t buffer_length) { // NOTE: buffer_length may be zero or valid. auto buffer = memory_->TranslateVirtual(buffer_ptr); switch (message) { @@ -38,7 +38,7 @@ X_RESULT XgiApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, uint32_t context_value = xe::load_and_swap(buffer + 20); XELOGD("XGIUserSetContextEx({:08X}, {:08X}, {:08X})", user_index, context_id, context_value); - return X_ERROR_SUCCESS; + return X_E_SUCCESS; } case 0x000B0007: { uint32_t user_index = xe::load_and_swap(buffer + 0); @@ -47,7 +47,7 @@ X_RESULT XgiApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, uint32_t value_ptr = xe::load_and_swap(buffer + 24); XELOGD("XGIUserSetPropertyEx({:08X}, {:08X}, {}, {:08X})", user_index, property_id, value_size, value_ptr); - return X_ERROR_SUCCESS; + return X_E_SUCCESS; } case 0x000B0008: { assert_true(!buffer_length || buffer_length == 8); @@ -55,7 +55,7 @@ X_RESULT XgiApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, uint32_t achievements_ptr = xe::load_and_swap(buffer + 4); XELOGD("XGIUserWriteAchievements({:08X}, {:08X})", achievement_count, achievements_ptr); - return X_ERROR_SUCCESS; + return X_E_SUCCESS; } case 0x000B0010: { assert_true(!buffer_length || buffer_length == 28); @@ -77,7 +77,7 @@ X_RESULT XgiApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, "{:08X})", session_ptr, flags, num_slots_public, num_slots_private, user_xuid, session_info_ptr, nonce_ptr); - return X_STATUS_SUCCESS; + return X_E_SUCCESS; } case 0x000B0011: { // TODO(DrChat): Figure out what this is again @@ -93,7 +93,7 @@ X_RESULT XgiApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, assert_zero(unk_0); XELOGD("XGISessionJoinLocal({:08X}, {}, {}, {:08X}, {:08X})", session_ptr, user_count, unk_0, user_index_array, private_slots_array); - return X_STATUS_SUCCESS; + return X_E_SUCCESS; } case 0x000B0041: { assert_true(!buffer_length || buffer_length == 32); @@ -110,18 +110,18 @@ X_RESULT XgiApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, if (context) { xe::store_and_swap(context + 4, value); } - return X_ERROR_FUNCTION_FAILED; + return X_E_FAIL; } case 0x000B0071: { XELOGD("XGI 0x000B0071, unimplemented"); - return X_ERROR_SUCCESS; + return X_E_SUCCESS; } } XELOGE( "Unimplemented XGI message app={:08X}, msg={:08X}, arg1={:08X}, " "arg2={:08X}", app_id(), message, buffer_ptr, buffer_length); - return X_STATUS_UNSUCCESSFUL; + return X_E_FAIL; } } // namespace apps diff --git a/src/xenia/kernel/xam/apps/xgi_app.h b/src/xenia/kernel/xam/apps/xgi_app.h index accac54a3..dc1d4bac2 100644 --- a/src/xenia/kernel/xam/apps/xgi_app.h +++ b/src/xenia/kernel/xam/apps/xgi_app.h @@ -22,8 +22,8 @@ class XgiApp : public App { public: explicit XgiApp(KernelState* kernel_state); - X_RESULT DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, - uint32_t buffer_length) override; + X_HRESULT DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, + uint32_t buffer_length) override; }; } // namespace apps diff --git a/src/xenia/kernel/xam/apps/xlivebase_app.cc b/src/xenia/kernel/xam/apps/xlivebase_app.cc index e20261b51..d35692ca8 100644 --- a/src/xenia/kernel/xam/apps/xlivebase_app.cc +++ b/src/xenia/kernel/xam/apps/xlivebase_app.cc @@ -22,9 +22,9 @@ XLiveBaseApp::XLiveBaseApp(KernelState* kernel_state) // http://mb.mirage.org/bugzilla/xliveless/main.c -X_RESULT XLiveBaseApp::DispatchMessageSync(uint32_t message, - uint32_t buffer_ptr, - uint32_t buffer_length) { +X_HRESULT XLiveBaseApp::DispatchMessageSync(uint32_t message, + uint32_t buffer_ptr, + uint32_t buffer_length) { // NOTE: buffer_length may be zero or valid. auto buffer = memory_->TranslateVirtual(buffer_ptr); switch (message) { @@ -33,13 +33,13 @@ X_RESULT XLiveBaseApp::DispatchMessageSync(uint32_t message, assert_true(!buffer_length || buffer_length == 4); XELOGD("XLiveBaseGetLogonId({:08X})", buffer_ptr); xe::store_and_swap(buffer + 0, 1); // ? - return X_ERROR_SUCCESS; + return X_E_SUCCESS; } case 0x00058006: { assert_true(!buffer_length || buffer_length == 4); XELOGD("XLiveBaseGetNatType({:08X})", buffer_ptr); xe::store_and_swap(buffer + 0, 1); // XONLINE_NAT_OPEN - return X_ERROR_SUCCESS; + return X_E_SUCCESS; } case 0x00058020: { // 0x00058004 is called right before this. @@ -48,12 +48,12 @@ X_RESULT XLiveBaseApp::DispatchMessageSync(uint32_t message, // buffer_length seems to be the same ptr sent to 0x00058004. XELOGD("XLiveBaseFriendsCreateEnumerator({:08X}, {:08X}) unimplemented", buffer_ptr, buffer_length); - return X_STATUS_UNSUCCESSFUL; + return X_E_FAIL; } case 0x00058023: { XELOGD("XliveBaseUnk58023({:08X}, {:08X}) unimplemented", buffer_ptr, buffer_length); - return X_STATUS_UNSUCCESSFUL; + return X_E_FAIL; } case 0x00058046: { // Required to be successful for Forza 4 to detect signed-in profile @@ -61,14 +61,14 @@ X_RESULT XLiveBaseApp::DispatchMessageSync(uint32_t message, // input XELOGD("XLiveBaseUnk58046({:08X}, {:08X}) unimplemented", buffer_ptr, buffer_length); - return X_ERROR_SUCCESS; + return X_E_SUCCESS; } } XELOGE( "Unimplemented XLIVEBASE message app={:08X}, msg={:08X}, arg1={:08X}, " "arg2={:08X}", app_id(), message, buffer_ptr, buffer_length); - return X_STATUS_UNSUCCESSFUL; + return X_E_FAIL; } } // namespace apps diff --git a/src/xenia/kernel/xam/apps/xlivebase_app.h b/src/xenia/kernel/xam/apps/xlivebase_app.h index 0068ec781..bd1fee3d5 100644 --- a/src/xenia/kernel/xam/apps/xlivebase_app.h +++ b/src/xenia/kernel/xam/apps/xlivebase_app.h @@ -22,8 +22,8 @@ class XLiveBaseApp : public App { public: explicit XLiveBaseApp(KernelState* kernel_state); - X_RESULT DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, - uint32_t buffer_length) override; + X_HRESULT DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, + uint32_t buffer_length) override; }; } // namespace apps diff --git a/src/xenia/kernel/xam/apps/xmp_app.cc b/src/xenia/kernel/xam/apps/xmp_app.cc index e048703af..8529f9523 100644 --- a/src/xenia/kernel/xam/apps/xmp_app.cc +++ b/src/xenia/kernel/xam/apps/xmp_app.cc @@ -30,7 +30,7 @@ XmpApp::XmpApp(KernelState* kernel_state) next_playlist_handle_(1), next_song_handle_(1) {} -X_RESULT XmpApp::XMPGetStatus(uint32_t state_ptr) { +X_HRESULT XmpApp::XMPGetStatus(uint32_t state_ptr) { // Some stupid games will hammer this on a thread - induce a delay // here to keep from starving real threads. xe::threading::Sleep(std::chrono::milliseconds(1)); @@ -38,15 +38,13 @@ X_RESULT XmpApp::XMPGetStatus(uint32_t state_ptr) { XELOGD("XMPGetStatus({:08X})", state_ptr); xe::store_and_swap(memory_->TranslateVirtual(state_ptr), static_cast(state_)); - return X_ERROR_SUCCESS; + return X_E_SUCCESS; } -X_RESULT XmpApp::XMPCreateTitlePlaylist(uint32_t songs_ptr, uint32_t song_count, - uint32_t playlist_name_ptr, - const std::u16string& playlist_name, - uint32_t flags, - uint32_t out_song_handles, - uint32_t out_playlist_handle) { +X_HRESULT XmpApp::XMPCreateTitlePlaylist( + uint32_t songs_ptr, uint32_t song_count, uint32_t playlist_name_ptr, + const std::u16string& playlist_name, uint32_t flags, + uint32_t out_song_handles, uint32_t out_playlist_handle) { XELOGD( "XMPCreateTitlePlaylist({:08X}, {:08X}, {:08X}({}), {:08X}, {:08X}, " "{:08X})", @@ -96,16 +94,16 @@ X_RESULT XmpApp::XMPCreateTitlePlaylist(uint32_t songs_ptr, uint32_t song_count, auto global_lock = global_critical_region_.Acquire(); playlists_.insert({playlist->handle, playlist.get()}); playlist.release(); - return X_ERROR_SUCCESS; + return X_E_SUCCESS; } -X_RESULT XmpApp::XMPDeleteTitlePlaylist(uint32_t playlist_handle) { +X_HRESULT XmpApp::XMPDeleteTitlePlaylist(uint32_t playlist_handle) { XELOGD("XMPDeleteTitlePlaylist({:08X})", playlist_handle); auto global_lock = global_critical_region_.Acquire(); auto it = playlists_.find(playlist_handle); if (it == playlists_.end()) { XELOGE("Playlist {:08X} not found", playlist_handle); - return X_ERROR_NOT_FOUND; + return X_E_NOTFOUND; } auto playlist = it->second; if (playlist == active_playlist_) { @@ -113,11 +111,11 @@ X_RESULT XmpApp::XMPDeleteTitlePlaylist(uint32_t playlist_handle) { } playlists_.erase(it); delete playlist; - return X_ERROR_SUCCESS; + return X_E_SUCCESS; } -X_RESULT XmpApp::XMPPlayTitlePlaylist(uint32_t playlist_handle, - uint32_t song_handle) { +X_HRESULT XmpApp::XMPPlayTitlePlaylist(uint32_t playlist_handle, + uint32_t song_handle) { XELOGD("XMPPlayTitlePlaylist({:08X}, {:08X})", playlist_handle, song_handle); Playlist* playlist = nullptr; { @@ -125,7 +123,7 @@ X_RESULT XmpApp::XMPPlayTitlePlaylist(uint32_t playlist_handle, auto it = playlists_.find(playlist_handle); if (it == playlists_.end()) { XELOGE("Playlist {:08X} not found", playlist_handle); - return X_ERROR_NOT_FOUND; + return X_E_NOTFOUND; } playlist = it->second; } @@ -133,7 +131,7 @@ X_RESULT XmpApp::XMPPlayTitlePlaylist(uint32_t playlist_handle, if (disabled_) { // Ignored because we aren't enabled? XELOGW("Ignoring XMPPlayTitlePlaylist because disabled"); - return X_ERROR_SUCCESS; + return X_E_SUCCESS; } // Start playlist? @@ -143,53 +141,53 @@ X_RESULT XmpApp::XMPPlayTitlePlaylist(uint32_t playlist_handle, state_ = State::kPlaying; OnStateChanged(); kernel_state_->BroadcastNotification(kMsgPlaybackBehaviorChanged, 1); - return X_ERROR_SUCCESS; + return X_E_SUCCESS; } -X_RESULT XmpApp::XMPContinue() { +X_HRESULT XmpApp::XMPContinue() { XELOGD("XMPContinue()"); if (state_ == State::kPaused) { state_ = State::kPlaying; } OnStateChanged(); - return X_ERROR_SUCCESS; + return X_E_SUCCESS; } -X_RESULT XmpApp::XMPStop(uint32_t unk) { +X_HRESULT XmpApp::XMPStop(uint32_t unk) { assert_zero(unk); XELOGD("XMPStop({:08X})", unk); active_playlist_ = nullptr; // ? active_song_index_ = 0; state_ = State::kIdle; OnStateChanged(); - return X_ERROR_SUCCESS; + return X_E_SUCCESS; } -X_RESULT XmpApp::XMPPause() { +X_HRESULT XmpApp::XMPPause() { XELOGD("XMPPause()"); if (state_ == State::kPlaying) { state_ = State::kPaused; } OnStateChanged(); - return X_ERROR_SUCCESS; + return X_E_SUCCESS; } -X_RESULT XmpApp::XMPNext() { +X_HRESULT XmpApp::XMPNext() { XELOGD("XMPNext()"); if (!active_playlist_) { - return X_ERROR_NOT_FOUND; + return X_E_NOTFOUND; } state_ = State::kPlaying; active_song_index_ = (active_song_index_ + 1) % active_playlist_->songs.size(); OnStateChanged(); - return X_ERROR_SUCCESS; + return X_E_SUCCESS; } -X_RESULT XmpApp::XMPPrevious() { +X_HRESULT XmpApp::XMPPrevious() { XELOGD("XMPPrevious()"); if (!active_playlist_) { - return X_ERROR_NOT_FOUND; + return X_E_NOTFOUND; } state_ = State::kPlaying; if (!active_song_index_) { @@ -198,7 +196,7 @@ X_RESULT XmpApp::XMPPrevious() { --active_song_index_; } OnStateChanged(); - return X_ERROR_SUCCESS; + return X_E_SUCCESS; } void XmpApp::OnStateChanged() { @@ -206,8 +204,8 @@ void XmpApp::OnStateChanged() { static_cast(state_)); } -X_RESULT XmpApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, - uint32_t buffer_length) { +X_HRESULT XmpApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, + uint32_t buffer_length) { // NOTE: buffer_length may be zero or valid. auto buffer = memory_->TranslateVirtual(buffer_ptr); switch (message) { @@ -271,7 +269,7 @@ X_RESULT XmpApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, repeat_mode_ = static_cast(uint32_t(args->repeat_mode)); unknown_flags_ = args->flags; kernel_state_->BroadcastNotification(kMsgPlaybackBehaviorChanged, 0); - return X_ERROR_SUCCESS; + return X_E_SUCCESS; } case 0x00070009: { assert_true(!buffer_length || buffer_length == 8); @@ -293,7 +291,7 @@ X_RESULT XmpApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, XELOGD("XMPGetVolume({:08X})", uint32_t(args->volume_ptr)); xe::store_and_swap(memory_->TranslateVirtual(args->volume_ptr), volume_); - return X_ERROR_SUCCESS; + return X_E_SUCCESS; } case 0x0007000C: { assert_true(!buffer_length || buffer_length == 8); @@ -306,7 +304,7 @@ X_RESULT XmpApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, assert_true(args->xmp_client == 0x00000002); XELOGD("XMPSetVolume({:g})", float(args->value)); volume_ = args->value; - return X_ERROR_SUCCESS; + return X_E_SUCCESS; } case 0x0007000D: { assert_true(!buffer_length || buffer_length == 36); @@ -358,7 +356,7 @@ X_RESULT XmpApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, XELOGE("XMPGetInfo?({:08X}, {:08X})", uint32_t(args->unk_ptr), uint32_t(args->info_ptr)); if (!active_playlist_) { - return X_STATUS_UNSUCCESSFUL; + return X_E_FAIL; } auto& song = active_playlist_->songs[active_song_index_]; xe::store_and_swap(info + 0, song->handle); @@ -372,7 +370,7 @@ X_RESULT XmpApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, xe::store_and_swap(info + 4 + 572 + 204, song->duration_ms); xe::store_and_swap(info + 4 + 572 + 208, static_cast(song->format)); - return X_ERROR_SUCCESS; + return X_E_SUCCESS; } case 0x00070013: { assert_true(!buffer_length || buffer_length == 8); @@ -409,7 +407,7 @@ X_RESULT XmpApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, XMPStop(0); } kernel_state_->BroadcastNotification(kMsgDisableChanged, disabled_); - return X_ERROR_SUCCESS; + return X_E_SUCCESS; } case 0x0007001B: { // XMPGetPlaybackController @@ -432,7 +430,7 @@ X_RESULT XmpApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, // Atrain spawns a thread 82437FD0 to call this in a tight loop forever. xe::threading::Sleep(std::chrono::milliseconds(10)); - return X_ERROR_SUCCESS; + return X_E_SUCCESS; } case 0x00070029: { // XMPGetPlaybackBehavior @@ -464,7 +462,7 @@ X_RESULT XmpApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, xe::store_and_swap(memory_->TranslateVirtual(args->unk3_ptr), unknown_flags_); } - return X_ERROR_SUCCESS; + return X_E_SUCCESS; } case 0x0007002E: { assert_true(!buffer_length || buffer_length == 12); @@ -482,20 +480,20 @@ X_RESULT XmpApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, // We don't use the storage, so just fudge the number. xe::store_and_swap(memory_->TranslateVirtual(args->size_ptr), 4 + uint32_t(args->song_count) * 128); - return X_ERROR_SUCCESS; + return X_E_SUCCESS; } case 0x0007003D: { // XMPCaptureOutput - not sure how this works :/ XELOGD("XMPCaptureOutput(...)"); assert_always("XMP output not unimplemented"); - return X_STATUS_UNSUCCESSFUL; + return X_E_FAIL; } } XELOGE( "Unimplemented XMP message app={:08X}, msg={:08X}, arg1={:08X}, " "arg2={:08X}", app_id(), message, buffer_ptr, buffer_length); - return X_STATUS_UNSUCCESSFUL; + return X_E_FAIL; } } // namespace apps diff --git a/src/xenia/kernel/xam/apps/xmp_app.h b/src/xenia/kernel/xam/apps/xmp_app.h index de341a143..cb4966a00 100644 --- a/src/xenia/kernel/xam/apps/xmp_app.h +++ b/src/xenia/kernel/xam/apps/xmp_app.h @@ -68,23 +68,24 @@ class XmpApp : public App { explicit XmpApp(KernelState* kernel_state); - X_RESULT XMPGetStatus(uint32_t status_ptr); + X_HRESULT XMPGetStatus(uint32_t status_ptr); - X_RESULT XMPCreateTitlePlaylist(uint32_t songs_ptr, uint32_t song_count, - uint32_t playlist_name_ptr, - const std::u16string& playlist_name, - uint32_t flags, uint32_t out_song_handles, - uint32_t out_playlist_handle); - X_RESULT XMPDeleteTitlePlaylist(uint32_t playlist_handle); - X_RESULT XMPPlayTitlePlaylist(uint32_t playlist_handle, uint32_t song_handle); - X_RESULT XMPContinue(); - X_RESULT XMPStop(uint32_t unk); - X_RESULT XMPPause(); - X_RESULT XMPNext(); - X_RESULT XMPPrevious(); + X_HRESULT XMPCreateTitlePlaylist(uint32_t songs_ptr, uint32_t song_count, + uint32_t playlist_name_ptr, + const std::u16string& playlist_name, + uint32_t flags, uint32_t out_song_handles, + uint32_t out_playlist_handle); + X_HRESULT XMPDeleteTitlePlaylist(uint32_t playlist_handle); + X_HRESULT XMPPlayTitlePlaylist(uint32_t playlist_handle, + uint32_t song_handle); + X_HRESULT XMPContinue(); + X_HRESULT XMPStop(uint32_t unk); + X_HRESULT XMPPause(); + X_HRESULT XMPNext(); + X_HRESULT XMPPrevious(); - X_RESULT DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, - uint32_t buffer_length) override; + X_HRESULT DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, + uint32_t buffer_length) override; private: static const uint32_t kMsgStateChanged = 0x0A000001; diff --git a/src/xenia/kernel/xam/content_manager.cc b/src/xenia/kernel/xam/content_manager.cc index 235e550b3..105ff7422 100644 --- a/src/xenia/kernel/xam/content_manager.cc +++ b/src/xenia/kernel/xam/content_manager.cc @@ -30,7 +30,7 @@ static int content_device_id_ = 0; ContentPackage::ContentPackage(KernelState* kernel_state, const std::string_view root_name, - const XCONTENT_DATA& data, + const ContentData& 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_); @@ -88,16 +88,16 @@ std::filesystem::path ContentManager::ResolvePackageRoot( } std::filesystem::path ContentManager::ResolvePackagePath( - const XCONTENT_DATA& data) { + const ContentData& data) { // Content path: // content_root/title_id/type_name/data_file_name/ auto package_root = ResolvePackageRoot(data.content_type); 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, + uint32_t content_type) { + std::vector result; // Search path: // content_root/title_id/type_name/* @@ -108,7 +108,7 @@ std::vector ContentManager::ListContent(uint32_t device_id, // Directories only. continue; } - XCONTENT_DATA content_data; + ContentData 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); @@ -120,7 +120,7 @@ std::vector ContentManager::ListContent(uint32_t device_id, } std::unique_ptr ContentManager::ResolvePackage( - const std::string_view root_name, const XCONTENT_DATA& data) { + const std::string_view root_name, const ContentData& data) { auto package_path = ResolvePackagePath(data); if (!std::filesystem::exists(package_path)) { return nullptr; @@ -133,13 +133,13 @@ std::unique_ptr ContentManager::ResolvePackage( return package; } -bool ContentManager::ContentExists(const XCONTENT_DATA& data) { +bool ContentManager::ContentExists(const ContentData& 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 ContentData& data) { auto global_lock = global_critical_region_.Acquire(); if (open_packages_.count(string_key(root_name))) { @@ -166,7 +166,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 ContentData& data) { auto global_lock = global_critical_region_.Acquire(); if (open_packages_.count(string_key(root_name))) { @@ -204,7 +204,7 @@ X_RESULT ContentManager::CloseContent(const std::string_view root_name) { return X_ERROR_SUCCESS; } -X_RESULT ContentManager::GetContentThumbnail(const XCONTENT_DATA& data, +X_RESULT ContentManager::GetContentThumbnail(const ContentData& data, std::vector* buffer) { auto global_lock = global_critical_region_.Acquire(); auto package_path = ResolvePackagePath(data); @@ -223,7 +223,7 @@ X_RESULT ContentManager::GetContentThumbnail(const XCONTENT_DATA& data, } } -X_RESULT ContentManager::SetContentThumbnail(const XCONTENT_DATA& data, +X_RESULT ContentManager::SetContentThumbnail(const ContentData& data, std::vector buffer) { auto global_lock = global_critical_region_.Acquire(); auto package_path = ResolvePackagePath(data); @@ -239,7 +239,7 @@ X_RESULT ContentManager::SetContentThumbnail(const XCONTENT_DATA& data, } } -X_RESULT ContentManager::DeleteContent(const XCONTENT_DATA& data) { +X_RESULT ContentManager::DeleteContent(const ContentData& data) { auto global_lock = global_critical_region_.Acquire(); auto package_path = ResolvePackagePath(data); diff --git a/src/xenia/kernel/xam/content_manager.h b/src/xenia/kernel/xam/content_manager.h index 5d0cb7b24..a69c63592 100644 --- a/src/xenia/kernel/xam/content_manager.h +++ b/src/xenia/kernel/xam/content_manager.h @@ -18,6 +18,7 @@ #include "xenia/base/memory.h" #include "xenia/base/mutex.h" #include "xenia/base/string_key.h" +#include "xenia/base/string_util.h" #include "xenia/xbox.h" namespace xe { @@ -31,32 +32,91 @@ namespace kernel { namespace xam { struct XCONTENT_DATA { - static const size_t kSize = 4 + 4 + 128 * 2 + 42 + 2; // = 306 + 2b padding + be device_id; + 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); + +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]; + uint8_t padding[2]; + be title_id; +}; +static_assert_size(XCONTENT_AGGREGATE_DATA, 312); + +struct ContentData { uint32_t device_id; uint32_t content_type; - std::u16string display_name; // 128 chars + std::u16string display_name; std::string file_name; - XCONTENT_DATA() = default; - explicit XCONTENT_DATA(const uint8_t* ptr) { - device_id = xe::load_and_swap(ptr + 0); - content_type = xe::load_and_swap(ptr + 4); - display_name = xe::load_and_swap(ptr + 8); - file_name = xe::load_and_swap(ptr + 8 + 128 * 2); + ContentData() = default; + + 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(uint8_t* ptr) { - xe::store_and_swap(ptr + 0, device_id); - xe::store_and_swap(ptr + 4, content_type); - xe::store_and_swap(ptr + 8, display_name); - xe::store_and_swap(ptr + 8 + 128 * 2, 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; } }; class ContentPackage { public: ContentPackage(KernelState* kernel_state, const std::string_view root_name, - const XCONTENT_DATA& data, + const ContentData& data, const std::filesystem::path& package_path); ~ContentPackage(); @@ -72,28 +132,28 @@ 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, + uint32_t content_type); std::unique_ptr ResolvePackage( - const std::string_view root_name, const XCONTENT_DATA& data); + const std::string_view root_name, const ContentData& data); - bool ContentExists(const XCONTENT_DATA& data); + bool ContentExists(const ContentData& data); X_RESULT CreateContent(const std::string_view root_name, - const XCONTENT_DATA& data); + const ContentData& data); X_RESULT OpenContent(const std::string_view root_name, - const XCONTENT_DATA& data); + const ContentData& data); X_RESULT CloseContent(const std::string_view root_name); - X_RESULT GetContentThumbnail(const XCONTENT_DATA& data, + X_RESULT GetContentThumbnail(const ContentData& data, std::vector* buffer); - X_RESULT SetContentThumbnail(const XCONTENT_DATA& data, + X_RESULT SetContentThumbnail(const ContentData& data, std::vector buffer); - X_RESULT DeleteContent(const XCONTENT_DATA& data); + X_RESULT DeleteContent(const ContentData& data); std::filesystem::path ResolveGameUserContentPath(); private: std::filesystem::path ResolvePackageRoot(uint32_t content_type); - std::filesystem::path ResolvePackagePath(const XCONTENT_DATA& data); + std::filesystem::path ResolvePackagePath(const ContentData& 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 0e74c4dd8..4de097fb7 100644 --- a/src/xenia/kernel/xam/xam_content.cc +++ b/src/xenia/kernel/xam/xam_content.cc @@ -9,8 +9,10 @@ #include "xenia/base/logging.h" #include "xenia/base/math.h" +#include "xenia/base/string_util.h" #include "xenia/kernel/kernel_state.h" #include "xenia/kernel/util/shim_utils.h" +#include "xenia/kernel/xam/xam_content_device.h" #include "xenia/kernel/xam/xam_private.h" #include "xenia/kernel/xenumerator.h" #include "xenia/xbox.h" @@ -29,33 +31,6 @@ namespace xe { namespace kernel { namespace xam { -struct DeviceInfo { - uint32_t device_id; - uint32_t device_type; - uint64_t total_bytes; - uint64_t free_bytes; - char16_t name[28]; -}; - -// TODO(gibbed): real information. -// -// Until we expose real information about a HDD device, we -// claim there is 3GB free on a 4GB dummy HDD. -// -// There is a possibility that certain games are bugged in that -// they incorrectly only look at the lower 32-bits of free_bytes, -// when it is a 64-bit value. Which means any size above ~4GB -// will not be recognized properly. -#define ONE_GB (1024ull * 1024ull * 1024ull) -static const DeviceInfo dummy_device_info_ = { - 0x00000001, // id - 1, // 1=HDD - 20ull * ONE_GB, // 20GB - 3ull * ONE_GB, // 3GB, so it looks a little used. - u"Dummy HDD", -}; -#undef ONE_GB - dword_result_t XamContentGetLicenseMask(lpdword_t mask_ptr, lpunknown_t overlapped_ptr) { // Each bit in the mask represents a granted license. Available licenses @@ -73,78 +48,10 @@ dword_result_t XamContentGetLicenseMask(lpdword_t mask_ptr, } DECLARE_XAM_EXPORT2(XamContentGetLicenseMask, kContent, kStub, kHighFrequency); -dword_result_t XamContentGetDeviceName(dword_t device_id, - lpu16string_t name_buffer, - dword_t name_capacity) { - if ((device_id & 0x0000000F) != dummy_device_info_.device_id) { - return X_ERROR_DEVICE_NOT_CONNECTED; - } - - auto name = std::u16string(dummy_device_info_.name); - if (name_capacity < name.size() + 1) { - return X_ERROR_INSUFFICIENT_BUFFER; - } - - xe::store_and_swap(name_buffer, name); - ((char16_t*)name_buffer)[name.size()] = 0; - return X_ERROR_SUCCESS; -} -DECLARE_XAM_EXPORT1(XamContentGetDeviceName, kContent, kImplemented); - -dword_result_t XamContentGetDeviceState(dword_t device_id, - lpunknown_t overlapped_ptr) { - if ((device_id & 0x0000000F) != dummy_device_info_.device_id) { - if (overlapped_ptr) { - kernel_state()->CompleteOverlappedImmediateEx( - overlapped_ptr, X_ERROR_FUNCTION_FAILED, X_ERROR_DEVICE_NOT_CONNECTED, - 0); - return X_ERROR_IO_PENDING; - } else { - return X_ERROR_DEVICE_NOT_CONNECTED; - } - } - - if (overlapped_ptr) { - kernel_state()->CompleteOverlappedImmediate(overlapped_ptr, - X_ERROR_SUCCESS); - return X_ERROR_IO_PENDING; - } else { - return X_ERROR_SUCCESS; - } -} -DECLARE_XAM_EXPORT1(XamContentGetDeviceState, kContent, kStub); - -typedef struct { - xe::be device_id; - xe::be device_type; - xe::be total_bytes; - xe::be free_bytes; - xe::be name[28]; -} X_CONTENT_DEVICE_DATA; -static_assert_size(X_CONTENT_DEVICE_DATA, 0x50); - -dword_result_t XamContentGetDeviceData( - dword_t device_id, pointer_t device_data) { - if ((device_id & 0x0000000F) != dummy_device_info_.device_id) { - // TODO(benvanik): memset 0 the data? - return X_ERROR_DEVICE_NOT_CONNECTED; - } - - device_data.Zero(); - const auto& device_info = dummy_device_info_; - device_data->device_id = device_info.device_id; - device_data->device_type = device_info.device_type; - device_data->total_bytes = device_info.total_bytes; - device_data->free_bytes = device_info.free_bytes; - xe::store_and_swap(&device_data->name[0], device_info.name); - return X_ERROR_SUCCESS; -} -DECLARE_XAM_EXPORT1(XamContentGetDeviceData, kContent, kImplemented); - dword_result_t XamContentResolve(dword_t user_index, lpvoid_t content_data_ptr, lpunknown_t buffer_ptr, dword_t buffer_size, dword_t unk1, dword_t unk2, dword_t unk3) { - auto content_data = XCONTENT_DATA((uint8_t*)content_data_ptr); + auto content_data = content_data_ptr.as(); // Result of buffer_ptr is sent to RtlInitAnsiString. // buffer_size is usually 260 (max path). @@ -164,8 +71,9 @@ dword_result_t XamContentCreateEnumerator(dword_t user_index, dword_t device_id, lpdword_t buffer_size_ptr, lpdword_t handle_out) { assert_not_null(handle_out); - if ((device_id && (device_id & 0x0000000F) != dummy_device_info_.device_id) || - !handle_out) { + + auto device_info = device_id == 0 ? nullptr : GetDummyDeviceInfo(device_id); + if ((device_id && device_info == nullptr) || !handle_out) { if (buffer_size_ptr) { *buffer_size_ptr = 0; } @@ -175,22 +83,29 @@ dword_result_t XamContentCreateEnumerator(dword_t user_index, dword_t device_id, } if (buffer_size_ptr) { - *buffer_size_ptr = (uint32_t)XCONTENT_DATA::kSize * items_per_enumerate; + *buffer_size_ptr = sizeof(XCONTENT_DATA) * items_per_enumerate; } - auto e = new XStaticEnumerator(kernel_state(), items_per_enumerate, - XCONTENT_DATA::kSize); - e->Initialize(); + auto e = object_ref(new XStaticEnumerator( + kernel_state(), items_per_enumerate, sizeof(XCONTENT_DATA))); + auto result = e->Initialize(0xFF, 0xFE, 0x20005, 0x20007, 0); + if (XFAILED(result)) { + return result; + } - // Get all content data. - auto content_datas = kernel_state()->content_manager()->ListContent( - device_id ? static_cast(device_id) - : dummy_device_info_.device_id, - content_type); - for (auto& content_data : content_datas) { - auto ptr = e->AppendItem(); - assert_not_null(ptr); - content_data.Write(ptr); + 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); + for (const auto& content_data : content_datas) { + auto item = reinterpret_cast(e->AppendItem()); + assert_not_null(item); + content_data.Write(item); + } + } + + if (!device_info || device_info->device_id == DummyDeviceId::ODD) { + // TODO(gibbed): disc drive content } XELOGD("XamContentCreateEnumerator: added {} items to enumerator", @@ -201,36 +116,6 @@ dword_result_t XamContentCreateEnumerator(dword_t user_index, dword_t device_id, } DECLARE_XAM_EXPORT1(XamContentCreateEnumerator, kContent, kImplemented); -dword_result_t XamContentCreateDeviceEnumerator(dword_t content_type, - dword_t content_flags, - dword_t max_count, - lpdword_t buffer_size_ptr, - lpdword_t handle_out) { - assert_not_null(handle_out); - - if (buffer_size_ptr) { - *buffer_size_ptr = sizeof(DeviceInfo) * max_count; - } - - auto e = new XStaticEnumerator(kernel_state(), max_count, sizeof(DeviceInfo)); - e->Initialize(); - - // Copy our dummy device into the enumerator - DeviceInfo* dev = (DeviceInfo*)e->AppendItem(); - if (dev) { - xe::store_and_swap(&dev->device_id, dummy_device_info_.device_id); - xe::store_and_swap(&dev->device_type, dummy_device_info_.device_type); - xe::store_and_swap(&dev->total_bytes, dummy_device_info_.total_bytes); - xe::store_and_swap(&dev->free_bytes, dummy_device_info_.free_bytes); - xe::copy_and_swap(dev->name, dummy_device_info_.name, - xe::countof(dev->name)); - } - - *handle_out = e->handle(); - return X_ERROR_SUCCESS; -} -DECLARE_XAM_EXPORT1(XamContentCreateDeviceEnumerator, kNone, kImplemented); - dword_result_t XamContentCreateEx(dword_t user_index, lpstring_t root_name, lpvoid_t content_data_ptr, dword_t flags, lpdword_t disposition_ptr, @@ -238,7 +123,8 @@ 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 = XCONTENT_DATA((uint8_t*)content_data_ptr); + auto content_data = + static_cast(*content_data_ptr.as()); auto content_manager = kernel_state()->content_manager(); bool create = false; @@ -390,7 +276,8 @@ dword_result_t XamContentGetCreator(dword_t user_index, lpunknown_t overlapped_ptr) { auto result = X_ERROR_SUCCESS; - auto content_data = XCONTENT_DATA((uint8_t*)content_data_ptr); + auto content_data = + static_cast(*content_data_ptr.as()); if (content_data.content_type == 1) { // User always creates saves. @@ -421,7 +308,8 @@ 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 = XCONTENT_DATA((uint8_t*)content_data_ptr); + auto content_data = + static_cast(*content_data_ptr.as()); // Get thumbnail (if it exists). std::vector buffer; @@ -457,7 +345,8 @@ 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 = XCONTENT_DATA((uint8_t*)content_data_ptr); + auto content_data = + static_cast(*content_data_ptr.as()); // Buffer is PNG data. auto buffer = std::vector((uint8_t*)buffer_ptr, @@ -476,7 +365,8 @@ 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 = XCONTENT_DATA((uint8_t*)content_data_ptr); + auto content_data = + static_cast(*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 new file mode 100644 index 000000000..9e002723e --- /dev/null +++ b/src/xenia/kernel/xam/xam_content_aggregate.cc @@ -0,0 +1,135 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2020 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/base/logging.h" +#include "xenia/base/math.h" +#include "xenia/base/string_util.h" +#include "xenia/kernel/kernel_state.h" +#include "xenia/kernel/util/shim_utils.h" +#include "xenia/kernel/xam/xam_content_device.h" +#include "xenia/kernel/xam/xam_private.h" +#include "xenia/kernel/xenumerator.h" +#include "xenia/vfs/file.h" +#include "xenia/xbox.h" + +namespace xe { +namespace kernel { +namespace xam { + +void AddODDContentTest(object_ref e, uint32_t 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); + + xe::filesystem::WildcardEngine title_find_engine; + title_find_engine.SetRule("????????"); + + xe::filesystem::WildcardEngine content_find_engine; + content_find_engine.SetRule("????????????????"); + + size_t title_find_index = 0; + vfs::Entry* title_entry; + for (;;) { + title_entry = + root_entry->IterateChildren(title_find_engine, &title_find_index); + if (!title_entry) { + break; + } + + auto title_id = + string_util::from_string(title_entry->name(), true); + + auto content_root_entry = title_entry->ResolvePath(content_type_path); + if (content_root_entry) { + size_t content_find_index = 0; + vfs::Entry* content_entry; + for (;;) { + content_entry = content_root_entry->IterateChildren( + content_find_engine, &content_find_index); + if (!content_entry) { + break; + } + + 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); + } + } + } +} + +dword_result_t XamContentAggregateCreateEnumerator(qword_t xuid, + dword_t device_id, + dword_t content_type, + unknown_t unk3, + lpdword_t handle_out) { + assert_not_null(handle_out); + + auto device_info = device_id == 0 ? nullptr : GetDummyDeviceInfo(device_id); + if ((device_id && device_info == nullptr) || !handle_out) { + return X_E_INVALIDARG; + } + + auto e = object_ref(new XStaticEnumerator( + kernel_state(), 1, sizeof(XCONTENT_AGGREGATE_DATA))); + X_KENUMERATOR_CONTENT_AGGREGATE* extra; + auto result = e->Initialize(0xFF, 0xFE, 0x2000E, 0x20010, 0, &extra); + if (XFAILED(result)) { + return result; + } + + extra->magic = 'XEN\0'; + extra->handle = e->handle(); + + 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); + 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 = 1u; + content_aggregate_data.Write(item); + } + } + + if (!device_info || device_info->device_type == DeviceType::ODD) { + AddODDContentTest(e, content_type); + } + + XELOGD("XamContentAggregateCreateEnumerator: added {} items to enumerator", + e->item_count()); + + *handle_out = e->handle(); + return X_ERROR_SUCCESS; +} +DECLARE_XAM_EXPORT1(XamContentAggregateCreateEnumerator, kContent, kStub); + +void RegisterContentAggregateExports(xe::cpu::ExportResolver* export_resolver, + KernelState* kernel_state) {} + +} // namespace xam +} // namespace kernel +} // namespace xe diff --git a/src/xenia/kernel/xam/xam_content_device.cc b/src/xenia/kernel/xam/xam_content_device.cc new file mode 100644 index 000000000..d0aaa34f1 --- /dev/null +++ b/src/xenia/kernel/xam/xam_content_device.cc @@ -0,0 +1,174 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2020 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/kernel/xam/xam_content_device.h" + +#include "xenia/base/logging.h" +#include "xenia/base/math.h" +#include "xenia/kernel/kernel_state.h" +#include "xenia/kernel/util/shim_utils.h" +#include "xenia/kernel/xam/xam_private.h" +#include "xenia/kernel/xenumerator.h" +#include "xenia/xbox.h" + +namespace xe { +namespace kernel { +namespace xam { + +// TODO(gibbed): real information. +// +// Until we expose real information about a HDD device, we +// claim there is 3GB free on a 4GB dummy HDD. +// +// There is a possibility that certain games are bugged in that +// they incorrectly only look at the lower 32-bits of free_bytes, +// when it is a 64-bit value. Which means any size above ~4GB +// will not be recognized properly. +#define ONE_GB (1024ull * 1024ull * 1024ull) + +static const DummyDeviceInfo dummy_hdd_device_info_ = { + DummyDeviceId::HDD, DeviceType::HDD, + 20ull * ONE_GB, // 20GB + 3ull * ONE_GB, // 3GB, so it looks a little used. + u"Dummy HDD", +}; +static const DummyDeviceInfo dummy_odd_device_info_ = { + DummyDeviceId::ODD, DeviceType::ODD, + 7ull * ONE_GB, // 7GB (rough maximum) + 0ull * ONE_GB, // read-only FS, so no free space + u"Dummy ODD", +}; +static const DummyDeviceInfo* dummy_device_infos_[] = { + &dummy_hdd_device_info_, + &dummy_odd_device_info_, +}; +#undef ONE_GB + +const DummyDeviceInfo* GetDummyDeviceInfo(uint32_t device_id) { + const auto& begin = std::begin(dummy_device_infos_); + const auto& end = std::end(dummy_device_infos_); + auto it = std::find_if(begin, end, [device_id](const auto& item) { + return static_cast(item->device_id) == device_id; + }); + return it == end ? nullptr : *it; +} + +dword_result_t XamContentGetDeviceName(dword_t device_id, + lpu16string_t name_buffer, + dword_t name_capacity) { + auto device_info = GetDummyDeviceInfo(device_id); + if (device_info == nullptr) { + return X_ERROR_DEVICE_NOT_CONNECTED; + } + auto name = std::u16string(device_info->name); + if (name_capacity < name.size() + 1) { + return X_ERROR_INSUFFICIENT_BUFFER; + } + xe::string_util::copy_and_swap_truncating(name_buffer, name, name_capacity); + return X_ERROR_SUCCESS; +} +DECLARE_XAM_EXPORT1(XamContentGetDeviceName, kContent, kImplemented); + +dword_result_t XamContentGetDeviceState(dword_t device_id, + lpunknown_t overlapped_ptr) { + auto device_info = GetDummyDeviceInfo(device_id); + if (device_info == nullptr) { + if (overlapped_ptr) { + kernel_state()->CompleteOverlappedImmediateEx( + overlapped_ptr, X_ERROR_FUNCTION_FAILED, X_ERROR_DEVICE_NOT_CONNECTED, + 0); + return X_ERROR_IO_PENDING; + } else { + return X_ERROR_DEVICE_NOT_CONNECTED; + } + } + if (overlapped_ptr) { + kernel_state()->CompleteOverlappedImmediate(overlapped_ptr, + X_ERROR_SUCCESS); + return X_ERROR_IO_PENDING; + } else { + return X_ERROR_SUCCESS; + } +} +DECLARE_XAM_EXPORT1(XamContentGetDeviceState, kContent, kStub); + +typedef struct { + xe::be device_id; + xe::be device_type; + xe::be total_bytes; + xe::be free_bytes; + union { + xe::be name[28]; + char16_t name_chars[28]; + }; +} X_CONTENT_DEVICE_DATA; +static_assert_size(X_CONTENT_DEVICE_DATA, 0x50); + +dword_result_t XamContentGetDeviceData( + dword_t device_id, pointer_t device_data) { + auto device_info = GetDummyDeviceInfo(device_id); + if (device_info == nullptr) { + return X_ERROR_DEVICE_NOT_CONNECTED; + } + device_data.Zero(); + device_data->device_id = static_cast(device_info->device_id); + device_data->device_type = static_cast(device_info->device_type); + device_data->total_bytes = device_info->total_bytes; + device_data->free_bytes = device_info->free_bytes; + xe::string_util::copy_and_swap_truncating( + device_data->name_chars, device_info->name, + xe::countof(device_data->name_chars)); + return X_ERROR_SUCCESS; +} +DECLARE_XAM_EXPORT1(XamContentGetDeviceData, kContent, kImplemented); + +dword_result_t XamContentCreateDeviceEnumerator(dword_t content_type, + dword_t content_flags, + dword_t max_count, + lpdword_t buffer_size_ptr, + lpdword_t handle_out) { + assert_not_null(handle_out); + + if (buffer_size_ptr) { + *buffer_size_ptr = sizeof(X_CONTENT_DEVICE_DATA) * max_count; + } + + auto e = object_ref(new XStaticEnumerator( + kernel_state(), max_count, sizeof(X_CONTENT_DEVICE_DATA))); + auto result = e->Initialize(0xFE, 0xFE, 0x2000A, 0x20009, 0); + if (XFAILED(result)) { + return result; + } + + for (const auto& device_info : dummy_device_infos_) { + // Copy our dummy device into the enumerator + auto device_data = (X_CONTENT_DEVICE_DATA*)e->AppendItem(); + if (device_data) { + device_data->device_id = static_cast(device_info->device_id); + device_data->device_type = + static_cast(device_info->device_type); + device_data->total_bytes = device_info->total_bytes; + device_data->free_bytes = device_info->free_bytes; + xe::string_util::copy_and_swap_truncating( + device_data->name_chars, device_info->name, + xe::countof(device_data->name_chars)); + } + } + + *handle_out = e->handle(); + return X_ERROR_SUCCESS; +} +DECLARE_XAM_EXPORT1(XamContentCreateDeviceEnumerator, kNone, kImplemented); + +void RegisterContentDeviceExports(xe::cpu::ExportResolver* export_resolver, + KernelState* kernel_state) {} + +} // namespace xam +} // namespace kernel +} // namespace xe diff --git a/src/xenia/kernel/xam/xam_content_device.h b/src/xenia/kernel/xam/xam_content_device.h new file mode 100644 index 000000000..c22f29543 --- /dev/null +++ b/src/xenia/kernel/xam/xam_content_device.h @@ -0,0 +1,43 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2020 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_KERNEL_XAM_XAM_CONTENT_DEVICE_H_ +#define XENIA_KERNEL_XAM_XAM_CONTENT_DEVICE_H_ + +#include "xenia/xbox.h" + +namespace xe { +namespace kernel { +namespace xam { + +enum class DeviceType : uint32_t { + HDD = 1, + ODD = 4, +}; + +enum class DummyDeviceId : uint32_t { + HDD = 1, + ODD = 2, +}; + +struct DummyDeviceInfo { + DummyDeviceId device_id; + DeviceType device_type; + uint64_t total_bytes; + uint64_t free_bytes; + const std::u16string_view name; +}; + +const DummyDeviceInfo* GetDummyDeviceInfo(uint32_t device_id); + +} // namespace xam +} // namespace kernel +} // namespace xe + +#endif // XENIA_KERNEL_XAM_XAM_CONTENT_DEVICE_H_ diff --git a/src/xenia/kernel/xam/xam_enum.cc b/src/xenia/kernel/xam/xam_enum.cc new file mode 100644 index 000000000..2cab56ab7 --- /dev/null +++ b/src/xenia/kernel/xam/xam_enum.cc @@ -0,0 +1,144 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2020 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/base/logging.h" +#include "xenia/base/string_util.h" +#include "xenia/kernel/kernel_state.h" +#include "xenia/kernel/util/shim_utils.h" +#include "xenia/kernel/xam/xam_module.h" +#include "xenia/kernel/xam/xam_private.h" +#include "xenia/kernel/xenumerator.h" +#include "xenia/xbox.h" + +#if XE_PLATFORM_WIN32 +#include "xenia/base/platform_win.h" +#endif + +#include "third_party/fmt/include/fmt/format.h" + +namespace xe { +namespace kernel { +namespace xam { + +// https://github.com/LestaD/SourceEngine2007/blob/master/se2007/engine/xboxsystem.cpp#L518 +uint32_t xeXamEnumerate(uint32_t handle, uint32_t flags, void* buffer, + uint32_t buffer_length, uint32_t* items_returned, + uint32_t overlapped_ptr) { + assert_true(flags == 0); + + auto e = kernel_state()->object_table()->LookupObject(handle); + if (!e) { + if (overlapped_ptr) { + kernel_state()->CompleteOverlappedImmediateEx( + overlapped_ptr, X_ERROR_INVALID_HANDLE, X_ERROR_INVALID_HANDLE, 0); + return X_ERROR_IO_PENDING; + } else { + return X_ERROR_INVALID_HANDLE; + } + } + + size_t actual_buffer_length = buffer_length; + if (buffer_length == e->items_per_enumerate()) { + actual_buffer_length = e->item_size() * e->items_per_enumerate(); + // Known culprits: + // Final Fight: Double Impact (saves) + XELOGW( + "Broken usage of XamEnumerate! buffer length={:X} vs actual " + "length={:X} " + "(item size={:X}, items per enumerate={})", + (uint32_t)buffer_length, actual_buffer_length, e->item_size(), + e->items_per_enumerate()); + } + + std::memset(buffer, 0, actual_buffer_length); + + X_RESULT result; + uint32_t item_count = 0; + + if (actual_buffer_length < e->item_size()) { + result = X_ERROR_INSUFFICIENT_BUFFER; + } else if (e->current_item() >= e->item_count()) { + result = X_ERROR_NO_MORE_FILES; + } else { + auto item_buffer = static_cast(buffer); + auto max_items = actual_buffer_length / e->item_size(); + while (max_items--) { + if (!e->WriteItem(item_buffer)) { + break; + } + item_buffer += e->item_size(); + item_count++; + } + result = X_ERROR_SUCCESS; + } + + if (items_returned) { + assert_true(!overlapped_ptr); + *items_returned = result == X_ERROR_SUCCESS ? item_count : 0; + return result; + } else if (overlapped_ptr) { + assert_true(!items_returned); + kernel_state()->CompleteOverlappedImmediateEx( + overlapped_ptr, + result == X_ERROR_SUCCESS ? X_ERROR_SUCCESS : X_ERROR_FUNCTION_FAILED, + X_HRESULT_FROM_WIN32(result), + result == X_ERROR_SUCCESS ? item_count : 0); + return X_ERROR_IO_PENDING; + } else { + assert_always(); + return X_ERROR_INVALID_PARAMETER; + } +} + +dword_result_t XamEnumerate(dword_t handle, dword_t flags, lpvoid_t buffer, + dword_t buffer_length, lpdword_t items_returned, + pointer_t overlapped) { + uint32_t dummy; + auto result = xeXamEnumerate(handle, flags, buffer, buffer_length, + !overlapped ? &dummy : nullptr, overlapped); + if (!overlapped && items_returned) { + *items_returned = dummy; + } + return result; +} +DECLARE_XAM_EXPORT1(XamEnumerate, kNone, kImplemented); + +dword_result_t XamCreateEnumeratorHandle(unknown_t unk1, unknown_t unk2, + unknown_t unk3, unknown_t unk4, + unknown_t unk5, unknown_t unk6, + unknown_t unk7, unknown_t unk8) { + return X_ERROR_INVALID_PARAMETER; +} +DECLARE_XAM_EXPORT1(XamCreateEnumeratorHandle, kNone, kStub); + +dword_result_t XamGetPrivateEnumStructureFromHandle(dword_t handle, + lpdword_t out_object_ptr) { + auto e = kernel_state()->object_table()->LookupObject(handle); + if (!e) { + return X_STATUS_INVALID_HANDLE; + } + + // Caller takes the reference. + // It's released in ObDereferenceObject. + e->RetainHandle(); + + if (out_object_ptr.guest_address()) { + *out_object_ptr = e->guest_object(); + } + + return X_STATUS_SUCCESS; +} +DECLARE_XAM_EXPORT1(XamGetPrivateEnumStructureFromHandle, kNone, kStub); + +void RegisterEnumExports(xe::cpu::ExportResolver* export_resolver, + KernelState* kernel_state) {} + +} // namespace xam +} // namespace kernel +} // namespace xe diff --git a/src/xenia/kernel/xam/xam_info.cc b/src/xenia/kernel/xam/xam_info.cc index a08ab60aa..438b8a8d0 100644 --- a/src/xenia/kernel/xam/xam_info.cc +++ b/src/xenia/kernel/xam/xam_info.cc @@ -337,91 +337,6 @@ dword_result_t XamFree(lpdword_t ptr) { } DECLARE_XAM_EXPORT1(XamFree, kMemory, kImplemented); -// https://github.com/LestaD/SourceEngine2007/blob/master/se2007/engine/xboxsystem.cpp#L518 -dword_result_t XamEnumerate(dword_t handle, dword_t flags, lpvoid_t buffer, - dword_t buffer_length, lpdword_t items_returned, - pointer_t overlapped) { - assert_true(flags == 0); - - auto e = kernel_state()->object_table()->LookupObject(handle); - if (!e) { - if (overlapped) { - kernel_state()->CompleteOverlappedImmediateEx( - overlapped, X_ERROR_INVALID_HANDLE, X_ERROR_INVALID_HANDLE, 0); - return X_ERROR_IO_PENDING; - } else { - return X_ERROR_INVALID_HANDLE; - } - } - - size_t actual_buffer_length = (uint32_t)buffer_length; - if (buffer_length == e->items_per_enumerate()) { - actual_buffer_length = e->item_size() * e->items_per_enumerate(); - // Known culprits: - // Final Fight: Double Impact (saves) - XELOGW( - "Broken usage of XamEnumerate! buffer length={:X} vs actual " - "length={:X} " - "(item size={:X}, items per enumerate={})", - (uint32_t)buffer_length, actual_buffer_length, e->item_size(), - e->items_per_enumerate()); - } - - buffer.Zero(actual_buffer_length); - - X_RESULT result; - uint32_t item_count = 0; - - if (actual_buffer_length < e->item_size()) { - result = X_ERROR_INSUFFICIENT_BUFFER; - } else if (e->current_item() >= e->item_count()) { - result = X_ERROR_NO_MORE_FILES; - } else { - auto item_buffer = buffer.as(); - auto max_items = actual_buffer_length / e->item_size(); - while (max_items--) { - if (!e->WriteItem(item_buffer)) { - break; - } - item_buffer += e->item_size(); - item_count++; - } - result = X_ERROR_SUCCESS; - } - - if (items_returned) { - assert_true(!overlapped); - *items_returned = result == X_ERROR_SUCCESS ? item_count : 0; - return result; - } else if (overlapped) { - assert_true(!items_returned); - kernel_state()->CompleteOverlappedImmediateEx( - overlapped, - result == X_ERROR_SUCCESS ? X_ERROR_SUCCESS : X_ERROR_FUNCTION_FAILED, - X_HRESULT_FROM_WIN32(result), - result == X_ERROR_SUCCESS ? item_count : 0); - return X_ERROR_IO_PENDING; - } else { - assert_always(); - return X_ERROR_INVALID_PARAMETER; - } -} -DECLARE_XAM_EXPORT1(XamEnumerate, kNone, kImplemented); - -dword_result_t XamCreateEnumeratorHandle(unknown_t unk1, unknown_t unk2, - unknown_t unk3, unknown_t unk4, - unknown_t unk5, unknown_t unk6, - unknown_t unk7, unknown_t unk8) { - return X_ERROR_INVALID_PARAMETER; -} -DECLARE_XAM_EXPORT1(XamCreateEnumeratorHandle, kNone, kStub); - -dword_result_t XamGetPrivateEnumStructureFromHandle(unknown_t unk1, - unknown_t unk2) { - return X_ERROR_INVALID_PARAMETER; -} -DECLARE_XAM_EXPORT1(XamGetPrivateEnumStructureFromHandle, kNone, kStub); - dword_result_t XamQueryLiveHiveW(lpu16string_t name, lpvoid_t out_buf, dword_t out_size, dword_t type /* guess */) { return X_STATUS_INVALID_PARAMETER_1; diff --git a/src/xenia/kernel/xam/xam_module.cc b/src/xenia/kernel/xam/xam_module.cc index 1bb7dbdc6..7689d3bf8 100644 --- a/src/xenia/kernel/xam/xam_module.cc +++ b/src/xenia/kernel/xam/xam_module.cc @@ -19,24 +19,18 @@ namespace xe { namespace kernel { namespace xam { +std::atomic xam_dialogs_shown_ = {0}; + +bool xeXamIsUIActive() { return xam_dialogs_shown_ > 0; } + XamModule::XamModule(Emulator* emulator, KernelState* kernel_state) : KernelModule(kernel_state, "xe:\\xam.xex"), loader_data_() { RegisterExportTable(export_resolver_); - // Register all exported functions. - RegisterAvatarExports(export_resolver_, kernel_state_); - RegisterContentExports(export_resolver_, kernel_state_); - RegisterInfoExports(export_resolver_, kernel_state_); - RegisterInputExports(export_resolver_, kernel_state_); - RegisterLocaleExports(export_resolver_, kernel_state_); - RegisterMsgExports(export_resolver_, kernel_state_); - RegisterNetExports(export_resolver_, kernel_state_); - RegisterNotifyExports(export_resolver_, kernel_state_); - RegisterNuiExports(export_resolver_, kernel_state_); - RegisterUIExports(export_resolver_, kernel_state_); - RegisterUserExports(export_resolver_, kernel_state_); - RegisterVideoExports(export_resolver_, kernel_state_); - RegisterVoiceExports(export_resolver_, kernel_state_); +#define XE_MODULE_EXPORT_GROUP(m, n) \ + Register##n##Exports(export_resolver_, kernel_state_); +#include "xam_module_export_groups.inc" +#undef XE_MODULE_EXPORT_GROUP } std::vector xam_exports(4096); diff --git a/src/xenia/kernel/xam/xam_module.h b/src/xenia/kernel/xam/xam_module.h index 8c9fd6550..6c5d88cbb 100644 --- a/src/xenia/kernel/xam/xam_module.h +++ b/src/xenia/kernel/xam/xam_module.h @@ -21,6 +21,8 @@ namespace xe { namespace kernel { namespace xam { +bool xeXamIsUIActive(); + class XamModule : public KernelModule { public: XamModule(Emulator* emulator, KernelState* kernel_state); diff --git a/src/xenia/kernel/xam/xam_module_export_groups.inc b/src/xenia/kernel/xam/xam_module_export_groups.inc new file mode 100644 index 000000000..d01464f93 --- /dev/null +++ b/src/xenia/kernel/xam/xam_module_export_groups.inc @@ -0,0 +1,29 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2020 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +// This is a partial file designed to be included by other files when +// constructing various tables. + +XE_MODULE_EXPORT_GROUP(xam, Avatar) +XE_MODULE_EXPORT_GROUP(xam, Content) +XE_MODULE_EXPORT_GROUP(xam, ContentAggregate) +XE_MODULE_EXPORT_GROUP(xam, ContentDevice) +XE_MODULE_EXPORT_GROUP(xam, Enum) +XE_MODULE_EXPORT_GROUP(xam, Info) +XE_MODULE_EXPORT_GROUP(xam, Input) +XE_MODULE_EXPORT_GROUP(xam, Locale) +XE_MODULE_EXPORT_GROUP(xam, Msg) +XE_MODULE_EXPORT_GROUP(xam, Net) +XE_MODULE_EXPORT_GROUP(xam, Notify) +XE_MODULE_EXPORT_GROUP(xam, NUI) +XE_MODULE_EXPORT_GROUP(xam, Task) +XE_MODULE_EXPORT_GROUP(xam, UI) +XE_MODULE_EXPORT_GROUP(xam, User) +XE_MODULE_EXPORT_GROUP(xam, Video) +XE_MODULE_EXPORT_GROUP(xam, Voice) diff --git a/src/xenia/kernel/xam/xam_msg.cc b/src/xenia/kernel/xam/xam_msg.cc index 5bcefa25d..aedac0573 100644 --- a/src/xenia/kernel/xam/xam_msg.cc +++ b/src/xenia/kernel/xam/xam_msg.cc @@ -11,7 +11,9 @@ #include "xenia/kernel/kernel_state.h" #include "xenia/kernel/util/shim_utils.h" #include "xenia/kernel/xam/xam_private.h" +#include "xenia/kernel/xboxkrnl/xboxkrnl_error.h" #include "xenia/kernel/xevent.h" +#include "xenia/kernel/xthread.h" #include "xenia/xbox.h" namespace xe { @@ -40,39 +42,49 @@ dword_result_t XMsgSystemProcessCall(dword_t app, dword_t message, } DECLARE_XAM_EXPORT1(XMsgSystemProcessCall, kNone, kImplemented); -dword_result_t XMsgStartIORequest(dword_t app, dword_t message, - pointer_t overlapped_ptr, - dword_t buffer, dword_t buffer_length) { +struct XMSGSTARTIOREQUEST_UNKNOWNARG { + be unk_0; + be unk_1; +}; + +X_HRESULT xeXMsgStartIORequestEx(uint32_t app, uint32_t message, + uint32_t overlapped_ptr, uint32_t buffer_ptr, + uint32_t buffer_length, + XMSGSTARTIOREQUEST_UNKNOWNARG* unknown) { auto result = kernel_state()->app_manager()->DispatchMessageAsync( - app, message, buffer, buffer_length); - if (result == X_ERROR_NOT_FOUND) { - XELOGE("XMsgStartIORequest: app {:08X} undefined", app); + app, message, buffer_ptr, buffer_length); + if (result == X_E_NOTFOUND) { + XELOGE("XMsgStartIORequestEx: app {:08X} undefined", app); + result = X_E_INVALIDARG; + XThread::SetLastError(X_ERROR_NOT_FOUND); } if (overlapped_ptr) { kernel_state()->CompleteOverlappedImmediate(overlapped_ptr, result); result = X_ERROR_IO_PENDING; } + if (result == X_ERROR_SUCCESS || X_ERROR_IO_PENDING) { + XThread::SetLastError(0); + } return result; } -DECLARE_XAM_EXPORT1(XMsgStartIORequest, kNone, kImplemented); -dword_result_t XMsgStartIORequestEx(dword_t app, dword_t message, - pointer_t overlapped_ptr, - dword_t buffer, dword_t buffer_length, - lpdword_t unknown_ptr) { - auto result = kernel_state()->app_manager()->DispatchMessageAsync( - app, message, buffer, buffer_length); - if (result == X_ERROR_NOT_FOUND) { - XELOGE("XMsgStartIORequestEx: app {:08X} undefined", app); - } - if (overlapped_ptr) { - kernel_state()->CompleteOverlappedImmediate(overlapped_ptr, result); - result = X_ERROR_IO_PENDING; - } - return result; +dword_result_t XMsgStartIORequestEx( + dword_t app, dword_t message, pointer_t overlapped_ptr, + dword_t buffer_ptr, dword_t buffer_length, + pointer_t unknown_ptr) { + return xeXMsgStartIORequestEx(app, message, overlapped_ptr, buffer_ptr, + buffer_length, unknown_ptr); } DECLARE_XAM_EXPORT1(XMsgStartIORequestEx, kNone, kImplemented); +dword_result_t XMsgStartIORequest(dword_t app, dword_t message, + pointer_t overlapped_ptr, + dword_t buffer_ptr, dword_t buffer_length) { + return xeXMsgStartIORequestEx(app, message, overlapped_ptr, buffer_ptr, + buffer_length, nullptr); +} +DECLARE_XAM_EXPORT1(XMsgStartIORequest, kNone, kImplemented); + dword_result_t XMsgCancelIORequest(pointer_t overlapped_ptr, dword_t wait) { X_HANDLE event_handle = XOverlappedGetEvent(overlapped_ptr); @@ -88,9 +100,42 @@ dword_result_t XMsgCancelIORequest(pointer_t overlapped_ptr, } DECLARE_XAM_EXPORT1(XMsgCancelIORequest, kNone, kImplemented); +dword_result_t XMsgCompleteIORequest(pointer_t overlapped_ptr, + dword_t result, dword_t extended_error, + dword_t length) { + kernel_state()->CompleteOverlappedImmediateEx(overlapped_ptr, result, + extended_error, length); + return X_ERROR_SUCCESS; +} +DECLARE_XAM_EXPORT2(XMsgCompleteIORequest, kNone, kImplemented, kSketchy); + +dword_result_t XamGetOverlappedResult(pointer_t overlapped_ptr, + lpdword_t length_ptr, dword_t unknown) { + uint32_t result; + if (overlapped_ptr->result != X_ERROR_IO_PENDING) { + result = overlapped_ptr->result; + } else if (!overlapped_ptr->event) { + result = X_ERROR_IO_INCOMPLETE; + } else { + auto ev = kernel_state()->object_table()->LookupObject( + overlapped_ptr->event); + result = ev->Wait(3, 1, 0, nullptr); + if (XSUCCEEDED(result)) { + result = overlapped_ptr->result; + } else { + result = xboxkrnl::xeRtlNtStatusToDosError(result); + } + } + if (XSUCCEEDED(result) && length_ptr) { + *length_ptr = overlapped_ptr->length; + } + return result; +} +DECLARE_XAM_EXPORT2(XamGetOverlappedResult, kNone, kImplemented, kSketchy); + void RegisterMsgExports(xe::cpu::ExportResolver* export_resolver, KernelState* kernel_state) {} } // namespace xam } // namespace kernel -} // namespace xe \ No newline at end of file +} // namespace xe diff --git a/src/xenia/kernel/xam/xam_notify.cc b/src/xenia/kernel/xam/xam_notify.cc index b5b0a44d0..32fb2635d 100644 --- a/src/xenia/kernel/xam/xam_notify.cc +++ b/src/xenia/kernel/xam/xam_notify.cc @@ -18,27 +18,35 @@ namespace xe { namespace kernel { namespace xam { -dword_result_t XamNotifyCreateListenerInternal(qword_t mask, dword_t unk, - dword_t one) { - // r4=1 may indicate user process? +uint32_t xeXamNotifyCreateListener(uint64_t mask, uint32_t is_system, + uint32_t max_version) { + assert_true(max_version < 11); + + if (max_version > 10) { + max_version = 10; + } auto listener = object_ref(new XNotifyListener(kernel_state())); - listener->Initialize(mask); + listener->Initialize(mask, max_version); // Handle ref is incremented, so return that. uint32_t handle = listener->handle(); return handle; } -DECLARE_XAM_EXPORT2(XamNotifyCreateListenerInternal, kNone, kImplemented, - kSketchy); -dword_result_t XamNotifyCreateListener(qword_t mask, dword_t one) { - return XamNotifyCreateListenerInternal(mask, 0, one); +dword_result_t XamNotifyCreateListener(qword_t mask, dword_t max_version) { + return xeXamNotifyCreateListener(mask, 0, max_version); } DECLARE_XAM_EXPORT1(XamNotifyCreateListener, kNone, kImplemented); +dword_result_t XamNotifyCreateListenerInternal(qword_t mask, dword_t is_system, + dword_t max_version) { + return xeXamNotifyCreateListener(mask, is_system, max_version); +} +DECLARE_XAM_EXPORT1(XamNotifyCreateListenerInternal, kNone, kImplemented); + // https://github.com/CodeAsm/ffplay360/blob/master/Common/AtgSignIn.cpp dword_result_t XNotifyGetNext(dword_t handle, dword_t match_id, lpdword_t id_ptr, lpdword_t param_ptr) { @@ -47,14 +55,15 @@ dword_result_t XNotifyGetNext(dword_t handle, dword_t match_id, } if (!id_ptr) { - return X_ERROR_INVALID_PARAMETER; + return 0; } *id_ptr = 0; + // Grab listener. auto listener = kernel_state()->object_table()->LookupObject(handle); if (!listener) { - return X_ERROR_INVALID_HANDLE; + return 0; } bool dequeued = false; diff --git a/src/xenia/kernel/xam/xam_nui.cc b/src/xenia/kernel/xam/xam_nui.cc index 3fbeb7663..2fc1a2a52 100644 --- a/src/xenia/kernel/xam/xam_nui.cc +++ b/src/xenia/kernel/xam/xam_nui.cc @@ -64,7 +64,7 @@ dword_result_t XamShowNuiTroubleshooterUI(unknown_t unk1, unknown_t unk2, } DECLARE_XAM_EXPORT1(XamShowNuiTroubleshooterUI, kNone, kStub); -void RegisterNuiExports(xe::cpu::ExportResolver* export_resolver, +void RegisterNUIExports(xe::cpu::ExportResolver* export_resolver, KernelState* kernel_state) {} } // namespace xam diff --git a/src/xenia/kernel/xam/xam_private.h b/src/xenia/kernel/xam/xam_private.h index b82f44bfd..9122bc21c 100644 --- a/src/xenia/kernel/xam/xam_private.h +++ b/src/xenia/kernel/xam/xam_private.h @@ -18,26 +18,16 @@ namespace xe { namespace kernel { namespace xam { +bool xeXamIsUIActive(); + xe::cpu::Export* RegisterExport_xam(xe::cpu::Export* export_entry); // Registration functions, one per file. -#define DECLARE_REGISTER_EXPORTS(n) \ +#define XE_MODULE_EXPORT_GROUP(m, n) \ void Register##n##Exports(xe::cpu::ExportResolver* export_resolver, \ - KernelState* kernel_state) -DECLARE_REGISTER_EXPORTS(Avatar); -DECLARE_REGISTER_EXPORTS(Content); -DECLARE_REGISTER_EXPORTS(Info); -DECLARE_REGISTER_EXPORTS(Input); -DECLARE_REGISTER_EXPORTS(Locale); -DECLARE_REGISTER_EXPORTS(Msg); -DECLARE_REGISTER_EXPORTS(Net); -DECLARE_REGISTER_EXPORTS(Notify); -DECLARE_REGISTER_EXPORTS(Nui); -DECLARE_REGISTER_EXPORTS(UI); -DECLARE_REGISTER_EXPORTS(User); -DECLARE_REGISTER_EXPORTS(Video); -DECLARE_REGISTER_EXPORTS(Voice); -#undef DECLARE_REGISTER_EXPORTS + KernelState* kernel_state); +#include "xam_module_export_groups.inc" +#undef XE_MODULE_EXPORT_GROUP } // namespace xam } // namespace kernel diff --git a/src/xenia/kernel/xam/xam_task.cc b/src/xenia/kernel/xam/xam_task.cc new file mode 100644 index 000000000..facac462c --- /dev/null +++ b/src/xenia/kernel/xam/xam_task.cc @@ -0,0 +1,66 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2020 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/base/logging.h" +#include "xenia/base/string_util.h" +#include "xenia/cpu/processor.h" +#include "xenia/kernel/kernel_state.h" +#include "xenia/kernel/util/shim_utils.h" +#include "xenia/kernel/xam/xam_module.h" +#include "xenia/kernel/xam/xam_private.h" +#include "xenia/kernel/xthread.h" +#include "xenia/xbox.h" + +#if XE_PLATFORM_WIN32 +#include "xenia/base/platform_win.h" +#endif + +#include "third_party/fmt/include/fmt/format.h" + +namespace xe { +namespace kernel { +namespace xam { + +struct XTASK_MESSAGE { + be unknown_00; + be unknown_04; + be unknown_08; + be callback_arg_ptr; + be event_handle; + be unknown_14; + be task_handle; +}; +static_assert_size(XTASK_MESSAGE, 0x1C); + +dword_result_t XamTaskSchedule(lpvoid_t callback, + pointer_t message, + dword_t unknown, lpdword_t handle_ptr) { + assert_zero(unknown); + + // TODO(gibbed): figure out what this is for + *handle_ptr = 12345; + + XELOGW("!! Executing scheduled task ({:08X}) synchronously, PROBABLY BAD !! ", + callback.guest_address()); + + // TODO(gibbed): this is supposed to be async... let's cheat. + auto thread_state = XThread::GetCurrentThread()->thread_state(); + uint64_t args[] = {message.guest_address()}; + auto result = kernel_state()->processor()->Execute(thread_state, callback, + args, xe::countof(args)); + return X_STATUS_SUCCESS; +} +DECLARE_XAM_EXPORT2(XamTaskSchedule, kNone, kImplemented, kSketchy); + +void RegisterTaskExports(xe::cpu::ExportResolver* export_resolver, + KernelState* kernel_state) {} + +} // namespace xam +} // namespace kernel +} // namespace xe diff --git a/src/xenia/kernel/xam/xam_ui.cc b/src/xenia/kernel/xam/xam_ui.cc index 4f1348a69..0b17135ae 100644 --- a/src/xenia/kernel/xam/xam_ui.cc +++ b/src/xenia/kernel/xam/xam_ui.cc @@ -23,31 +23,196 @@ namespace xe { namespace kernel { namespace xam { -std::atomic xam_dialogs_shown_ = {0}; +// TODO(gibbed): This is all one giant WIP that seems to work better than the +// previous immediate synchronous completion of dialogs. +// +// The deferred execution of dialog handling is done in such a way that there is +// a pre-, peri- (completion), and post- callback steps. +// +// pre(); +// result = completion(); +// CompleteOverlapped(result); +// post(); +// +// There are games that are batshit insane enough to wait for the X_OVERLAPPED +// to be completed (ie not X_ERROR_PENDING) before creating a listener to +// receive a notification, which is why we have distinct pre- and post- steps. +// +// We deliberately delay the XN_SYS_UI = false notification to give games time +// to create a listener (if they're insane enough do this). -dword_result_t XamIsUIActive() { return xam_dialogs_shown_ > 0 ? 1 : 0; } +extern std::atomic xam_dialogs_shown_; + +class XamDialog : public xe::ui::ImGuiDialog { + public: + void set_close_callback(std::function close_callback) { + close_callback_ = close_callback; + } + + protected: + XamDialog(xe::ui::Window* window) : xe::ui::ImGuiDialog(window) {} + + void OnClose() override { + if (close_callback_) { + close_callback_(); + } + } + + private: + std::function close_callback_ = nullptr; +}; + +template +X_RESULT xeXamDispatchDialog(T* dialog, + std::function close_callback, + uint32_t overlapped) { + auto pre = []() { + // Broadcast XN_SYS_UI = true + kernel_state()->BroadcastNotification(0x9, true); + }; + auto run = [dialog, close_callback]() -> X_RESULT { + X_RESULT result; + dialog->set_close_callback([&dialog, &result, &close_callback]() { + result = close_callback(dialog); + }); + xe::threading::Fence fence; + kernel_state()->emulator()->display_window()->loop()->PostSynchronous( + [&dialog, &fence]() { dialog->Then(&fence); }); + ++xam_dialogs_shown_; + fence.Wait(); + --xam_dialogs_shown_; + // dialog should be deleted at this point! + return result; + }; + auto post = []() { + xe::threading::Sleep(std::chrono::milliseconds(100)); + // Broadcast XN_SYS_UI = false + kernel_state()->BroadcastNotification(0x9, false); + }; + if (!overlapped) { + pre(); + auto result = run(); + post(); + return result; + } else { + kernel_state()->CompleteOverlappedDeferred(run, overlapped, pre, post); + return X_ERROR_IO_PENDING; + } +} + +template +X_RESULT xeXamDispatchDialogEx( + T* dialog, std::function close_callback, + uint32_t overlapped) { + auto pre = []() { + // Broadcast XN_SYS_UI = true + kernel_state()->BroadcastNotification(0x9, true); + }; + auto run = [dialog, close_callback](uint32_t& extended_error, + uint32_t& length) -> X_RESULT { + auto display_window = kernel_state()->emulator()->display_window(); + X_RESULT result; + dialog->set_close_callback( + [&dialog, &result, &extended_error, &length, &close_callback]() { + result = close_callback(dialog, extended_error, length); + }); + xe::threading::Fence fence; + display_window->loop()->PostSynchronous( + [&dialog, &fence]() { dialog->Then(&fence); }); + ++xam_dialogs_shown_; + fence.Wait(); + --xam_dialogs_shown_; + // dialog should be deleted at this point! + return result; + }; + auto post = []() { + xe::threading::Sleep(std::chrono::milliseconds(100)); + // Broadcast XN_SYS_UI = false + kernel_state()->BroadcastNotification(0x9, false); + }; + if (!overlapped) { + pre(); + uint32_t extended_error, length; + auto result = run(extended_error, length); + post(); + // TODO(gibbed): do something with extended_error/length? + return result; + } else { + kernel_state()->CompleteOverlappedDeferredEx(run, overlapped, pre, post); + return X_ERROR_IO_PENDING; + } +} + +X_RESULT xeXamDispatchHeadless(std::function run_callback, + uint32_t overlapped) { + auto pre = []() { + // Broadcast XN_SYS_UI = true + kernel_state()->BroadcastNotification(0x9, true); + }; + auto post = []() { + xe::threading::Sleep(std::chrono::milliseconds(100)); + // Broadcast XN_SYS_UI = false + kernel_state()->BroadcastNotification(0x9, false); + }; + if (!overlapped) { + pre(); + auto result = run_callback(); + post(); + return result; + } else { + kernel_state()->CompleteOverlappedDeferred(run_callback, overlapped, pre, + post); + return X_ERROR_IO_PENDING; + } +} + +X_RESULT xeXamDispatchHeadlessEx( + std::function run_callback, + uint32_t overlapped) { + auto pre = []() { + // Broadcast XN_SYS_UI = true + kernel_state()->BroadcastNotification(0x9, true); + }; + auto post = []() { + xe::threading::Sleep(std::chrono::milliseconds(100)); + // Broadcast XN_SYS_UI = false + kernel_state()->BroadcastNotification(0x9, false); + }; + if (!overlapped) { + pre(); + uint32_t extended_error, length; + auto result = run_callback(extended_error, length); + post(); + // TODO(gibbed): do something with extended_error/length? + return result; + } else { + kernel_state()->CompleteOverlappedDeferredEx(run_callback, overlapped, pre, + post); + return X_ERROR_IO_PENDING; + } +} + +dword_result_t XamIsUIActive() { return xeXamIsUIActive(); } DECLARE_XAM_EXPORT2(XamIsUIActive, kUI, kImplemented, kHighFrequency); -class MessageBoxDialog : public xe::ui::ImGuiDialog { +class MessageBoxDialog : public XamDialog { public: - MessageBoxDialog(xe::ui::Window* window, std::u16string title, - std::u16string description, - std::vector buttons, uint32_t default_button, - uint32_t* out_chosen_button) - : ImGuiDialog(window), - title_(xe::to_utf8(title)), - description_(xe::to_utf8(description)), + MessageBoxDialog(xe::ui::Window* window, std::string title, + std::string description, std::vector buttons, + uint32_t default_button) + : XamDialog(window), + title_(title), + description_(description), buttons_(std::move(buttons)), default_button_(default_button), - out_chosen_button_(out_chosen_button) { + chosen_button_(default_button) { if (!title_.size()) { title_ = "Message Box"; } - if (out_chosen_button) { - *out_chosen_button = default_button; - } } + uint32_t chosen_button() const { return chosen_button_; } + void OnDraw(ImGuiIO& io) override { bool first_draw = false; if (!has_opened_) { @@ -64,11 +229,8 @@ class MessageBoxDialog : public xe::ui::ImGuiDialog { ImGui::SetKeyboardFocusHere(); } for (size_t i = 0; i < buttons_.size(); ++i) { - auto button_name = xe::to_utf8(buttons_[i]); - if (ImGui::Button(button_name.c_str())) { - if (out_chosen_button_) { - *out_chosen_button_ = static_cast(i); - } + if (ImGui::Button(buttons_[i].c_str())) { + chosen_button_ = static_cast(i); ImGui::CloseCurrentPopup(); Close(); } @@ -86,9 +248,9 @@ class MessageBoxDialog : public xe::ui::ImGuiDialog { bool has_opened_ = false; std::string title_; std::string description_; - std::vector buttons_; + std::vector buttons_; uint32_t default_button_ = 0; - uint32_t* out_chosen_button_ = nullptr; + uint32_t chosen_button_ = 0; }; // https://www.se7ensins.com/forums/threads/working-xshowmessageboxui.844116/ @@ -97,86 +259,71 @@ dword_result_t XamShowMessageBoxUI(dword_t user_index, lpu16string_t title_ptr, lpdword_t button_ptrs, dword_t active_button, dword_t flags, lpdword_t result_ptr, pointer_t overlapped) { - std::u16string title; + std::string title; if (title_ptr) { - title = title_ptr.value(); + title = xe::to_utf8(title_ptr.value()); } else { - title = u""; // TODO(gibbed): default title based on flags? + title = ""; // TODO(gibbed): default title based on flags? } - auto text = text_ptr.value(); - std::vector buttons; - std::u16string all_buttons; - for (uint32_t j = 0; j < button_count; ++j) { - uint32_t button_ptr = button_ptrs[j]; + std::vector buttons; + for (uint32_t i = 0; i < button_count; ++i) { + uint32_t button_ptr = button_ptrs[i]; auto button = xe::load_and_swap( kernel_state()->memory()->TranslateVirtual(button_ptr)); - all_buttons.append(button); - if (j + 1 < button_count) { - all_buttons.append(u" | "); - } - buttons.push_back(button); + buttons.push_back(xe::to_utf8(button)); } - // Broadcast XN_SYS_UI = true - kernel_state()->BroadcastNotification(0x9, true); - - uint32_t chosen_button; + X_RESULT result; if (cvars::headless) { // Auto-pick the focused button. - chosen_button = active_button; + auto run = [result_ptr, active_button]() -> X_RESULT { + *result_ptr = static_cast(active_button); + return X_ERROR_SUCCESS; + }; + result = xeXamDispatchHeadless(run, overlapped); } else { + // TODO(benvanik): setup icon states. + switch (flags & 0xF) { + case 0: + // config.pszMainIcon = nullptr; + break; + case 1: + // config.pszMainIcon = TD_ERROR_ICON; + break; + case 2: + // config.pszMainIcon = TD_WARNING_ICON; + break; + case 3: + // config.pszMainIcon = TD_INFORMATION_ICON; + break; + } + auto close = [result_ptr](MessageBoxDialog* dialog) -> X_RESULT { + *result_ptr = dialog->chosen_button(); + return X_ERROR_SUCCESS; + }; auto display_window = kernel_state()->emulator()->display_window(); - xe::threading::Fence fence; - display_window->loop()->PostSynchronous([&]() { - // TODO(benvanik): setup icon states. - switch (flags & 0xF) { - case 0: - // config.pszMainIcon = nullptr; - break; - case 1: - // config.pszMainIcon = TD_ERROR_ICON; - break; - case 2: - // config.pszMainIcon = TD_WARNING_ICON; - break; - case 3: - // config.pszMainIcon = TD_INFORMATION_ICON; - break; - } - (new MessageBoxDialog(display_window, title, text, buttons, active_button, - &chosen_button)) - ->Then(&fence); - }); - ++xam_dialogs_shown_; - fence.Wait(); - --xam_dialogs_shown_; - } - *result_ptr = chosen_button; - - // Broadcast XN_SYS_UI = false - kernel_state()->BroadcastNotification(0x9, false); - - if (overlapped) { - kernel_state()->CompleteOverlappedImmediate(overlapped, X_ERROR_SUCCESS); - return X_ERROR_IO_PENDING; - } else { - return X_ERROR_SUCCESS; + result = xeXamDispatchDialog( + new MessageBoxDialog(display_window, title, + xe::to_utf8(text_ptr.value()), buttons, + active_button), + close, overlapped); } + return result; } DECLARE_XAM_EXPORT1(XamShowMessageBoxUI, kUI, kImplemented); -class KeyboardInputDialog : public xe::ui::ImGuiDialog { +class KeyboardInputDialog : public XamDialog { public: - KeyboardInputDialog(xe::ui::Window* window, std::u16string title, - std::u16string description, std::u16string default_text, - std::u16string* out_text, size_t max_length) - : ImGuiDialog(window), - title_(xe::to_utf8(title)), - description_(xe::to_utf8(description)), - default_text_(xe::to_utf8(default_text)), - out_text_(out_text), - max_length_(max_length) { + KeyboardInputDialog(xe::ui::Window* window, std::string title, + std::string description, std::string default_text, + size_t max_length) + : XamDialog(window), + title_(title), + description_(description), + default_text_(default_text), + max_length_(max_length), + text_buffer_() { if (!title_.size()) { if (!description_.size()) { title_ = "Keyboard Input"; @@ -185,14 +332,15 @@ class KeyboardInputDialog : public xe::ui::ImGuiDialog { description_ = ""; } } - if (out_text_) { - *out_text_ = default_text; - } + text_ = default_text; text_buffer_.resize(max_length); xe::string_util::copy_truncating(text_buffer_.data(), default_text_, text_buffer_.size()); } + const std::string& text() const { return text_; } + bool cancelled() const { return cancelled_; } + void OnDraw(ImGuiIO& io) override { bool first_draw = false; if (!has_opened_) { @@ -210,23 +358,21 @@ class KeyboardInputDialog : public xe::ui::ImGuiDialog { } if (ImGui::InputText("##body", text_buffer_.data(), text_buffer_.size(), ImGuiInputTextFlags_EnterReturnsTrue)) { - if (out_text_) { - *out_text_ = xe::to_utf16( - std::string_view(text_buffer_.data(), text_buffer_.size())); - } + text_ = std::string(text_buffer_.data(), text_buffer_.size()); + cancelled_ = false; ImGui::CloseCurrentPopup(); Close(); } if (ImGui::Button("OK")) { - if (out_text_) { - *out_text_ = xe::to_utf16( - std::string_view(text_buffer_.data(), text_buffer_.size())); - } + text_ = std::string(text_buffer_.data(), text_buffer_.size()); + cancelled_ = false; ImGui::CloseCurrentPopup(); Close(); } ImGui::SameLine(); if (ImGui::Button("Cancel")) { + text_ = ""; + cancelled_ = true; ImGui::CloseCurrentPopup(); Close(); } @@ -242,9 +388,10 @@ class KeyboardInputDialog : public xe::ui::ImGuiDialog { std::string title_; std::string description_; std::string default_text_; - std::u16string* out_text_ = nullptr; - std::vector text_buffer_; size_t max_length_ = 0; + std::vector text_buffer_; + std::string text_ = ""; + bool cancelled_ = true; }; // https://www.se7ensins.com/forums/threads/release-how-to-use-xshowkeyboardui-release.906568/ @@ -257,58 +404,51 @@ dword_result_t XamShowKeyboardUI(dword_t user_index, dword_t flags, return X_ERROR_INVALID_PARAMETER; } - // Broadcast XN_SYS_UI = true - kernel_state()->BroadcastNotification(0x9, true); + assert_not_null(overlapped); + auto buffer_size = static_cast(buffer_length) * 2; + + X_RESULT result; if (cvars::headless) { - // Redirect default_text back into the buffer. - std::memset(buffer, 0, buffer_length * 2); - if (default_text) { - xe::store_and_swap(buffer, default_text.value()); - } - - // Broadcast XN_SYS_UI = false - kernel_state()->BroadcastNotification(0x9, false); - - if (overlapped) { - kernel_state()->CompleteOverlappedImmediate(overlapped, X_ERROR_SUCCESS); - return X_ERROR_IO_PENDING; - } else { + auto run = [default_text, buffer, buffer_length, + buffer_size]() -> X_RESULT { + // Redirect default_text back into the buffer. + if (!default_text) { + std::memset(buffer, 0, buffer_size); + } else { + string_util::copy_and_swap_truncating(buffer, default_text.value(), + buffer_length); + } return X_ERROR_SUCCESS; - } - } - - std::u16string out_text; - - auto display_window = kernel_state()->emulator()->display_window(); - xe::threading::Fence fence; - display_window->loop()->PostSynchronous([&]() { - (new KeyboardInputDialog(display_window, title ? title.value() : u"", - description ? description.value() : u"", - default_text ? default_text.value() : u"", - &out_text, buffer_length)) - ->Then(&fence); - }); - ++xam_dialogs_shown_; - fence.Wait(); - --xam_dialogs_shown_; - - // Zero the output buffer. - std::memset(buffer, 0, buffer_length * 2); - - // Truncate the string. - out_text = out_text.substr(0, buffer_length - 1); - xe::store_and_swap(buffer, out_text); - - // Broadcast XN_SYS_UI = false - kernel_state()->BroadcastNotification(0x9, false); - - if (overlapped) { - kernel_state()->CompleteOverlappedImmediate(overlapped, X_ERROR_SUCCESS); - return X_ERROR_IO_PENDING; + }; + result = xeXamDispatchHeadless(run, overlapped); } else { - return X_ERROR_SUCCESS; + auto close = [buffer, buffer_length](KeyboardInputDialog* dialog, + uint32_t& extended_error, + uint32_t& length) -> X_RESULT { + if (dialog->cancelled()) { + extended_error = X_ERROR_CANCELLED; + length = 0; + return X_ERROR_SUCCESS; + } else { + // Zero the output buffer. + auto text = xe::to_utf16(dialog->text()); + string_util::copy_and_swap_truncating(buffer, text, buffer_length); + extended_error = X_ERROR_SUCCESS; + length = 0; + return X_ERROR_SUCCESS; + } + }; + auto display_window = kernel_state()->emulator()->display_window(); + result = xeXamDispatchDialogEx( + new KeyboardInputDialog( + display_window, title ? xe::to_utf8(title.value()) : "", + description ? xe::to_utf8(description.value()) : "", + default_text ? xe::to_utf8(default_text.value()) : "", + buffer_length), + close, overlapped); } + return result; } DECLARE_XAM_EXPORT1(XamShowKeyboardUI, kUI, kImplemented); @@ -317,19 +457,13 @@ dword_result_t XamShowDeviceSelectorUI(dword_t user_index, dword_t content_type, qword_t total_requested, lpdword_t device_id_ptr, pointer_t overlapped) { - // NOTE: 0x00000001 is our dummy device ID from xam_content.cc - *device_id_ptr = 0x00000001; - - // Broadcast XN_SYS_UI = true followed by XN_SYS_UI = false - kernel_state()->BroadcastNotification(0x9, true); - kernel_state()->BroadcastNotification(0x9, false); - - if (overlapped) { - kernel_state()->CompleteOverlappedImmediate(overlapped, X_ERROR_SUCCESS); - return X_ERROR_IO_PENDING; - } else { - return X_ERROR_SUCCESS; - } + return xeXamDispatchHeadless( + [device_id_ptr]() -> X_RESULT { + // NOTE: 0x00000001 is our dummy device ID from xam_content.cc + *device_id_ptr = 0x00000001; + return X_ERROR_SUCCESS; + }, + overlapped); } DECLARE_XAM_EXPORT1(XamShowDeviceSelectorUI, kUI, kImplemented); @@ -339,20 +473,14 @@ void XamShowDirtyDiscErrorUI(dword_t user_index) { exit(1); return; } - auto display_window = kernel_state()->emulator()->display_window(); - xe::threading::Fence fence; - display_window->loop()->PostSynchronous([&]() { - xe::ui::ImGuiDialog::ShowMessageBox( - display_window, "Disc Read Error", - "There's been an issue reading content from the game disc.\nThis is " - "likely caused by bad or unimplemented file IO calls.") - ->Then(&fence); - }); - ++xam_dialogs_shown_; - fence.Wait(); - --xam_dialogs_shown_; - + xeXamDispatchDialog( + new MessageBoxDialog( + display_window, "Disc Read Error", + "There's been an issue reading content from the game disc.\nThis is " + "likely caused by bad or unimplemented file IO calls.", + {"OK"}, 0), + [](MessageBoxDialog*) -> X_RESULT { return X_ERROR_SUCCESS; }, 0); // This is death, and should never return. // TODO(benvanik): cleaner exit. exit(1); diff --git a/src/xenia/kernel/xam/xam_user.cc b/src/xenia/kernel/xam/xam_user.cc index 8dc8ae492..92a5142bd 100644 --- a/src/xenia/kernel/xam/xam_user.cc +++ b/src/xenia/kernel/xam/xam_user.cc @@ -562,11 +562,14 @@ dword_result_t XamUserCreateAchievementEnumerator(dword_t title_id, *buffer_size_ptr = 500 * count; } - auto e = new XStaticEnumerator(kernel_state(), count, 500); - e->Initialize(); + auto e = object_ref( + new XStaticEnumerator(kernel_state(), count, 500)); + auto result = e->Initialize(user_index, 0xFB, 0xB000A, 0xB000B, 0); + if (XFAILED(result)) { + return result; + } *handle_ptr = e->handle(); - return X_ERROR_SUCCESS; } DECLARE_XAM_EXPORT1(XamUserCreateAchievementEnumerator, kUserProfiles, diff --git a/src/xenia/kernel/xenumerator.cc b/src/xenia/kernel/xenumerator.cc index 5de0a3d12..693d756e0 100644 --- a/src/xenia/kernel/xenumerator.cc +++ b/src/xenia/kernel/xenumerator.cc @@ -20,7 +20,34 @@ XEnumerator::XEnumerator(KernelState* kernel_state, size_t items_per_enumerate, XEnumerator::~XEnumerator() = default; -void XEnumerator::Initialize() {} +X_STATUS XEnumerator::Initialize(uint32_t user_index, uint32_t app_id, + uint32_t message, uint32_t message2, + uint32_t flags, uint32_t extra_size, + void** extra_buffer) { + auto native_object = CreateNative(sizeof(X_KENUMERATOR) + extra_size); + if (!native_object) { + return X_STATUS_NO_MEMORY; + } + auto guest_object = reinterpret_cast(native_object); + guest_object->app_id = app_id; + guest_object->message = message; + guest_object->message2 = message2; + guest_object->user_index = user_index; + guest_object->items_per_enumerate = + static_cast(items_per_enumerate_); + guest_object->flags = flags; + if (extra_buffer) { + *extra_buffer = + !extra_buffer ? nullptr : &native_object[sizeof(X_KENUMERATOR)]; + } + return X_STATUS_SUCCESS; +} + +X_STATUS XEnumerator::Initialize(uint32_t user_index, uint32_t app_id, + uint32_t message, uint32_t message2, + uint32_t flags) { + return Initialize(user_index, app_id, message, message2, flags, 0, nullptr); +} } // namespace kernel } // namespace xe diff --git a/src/xenia/kernel/xenumerator.h b/src/xenia/kernel/xenumerator.h index 189453813..0217b02e5 100644 --- a/src/xenia/kernel/xenumerator.h +++ b/src/xenia/kernel/xenumerator.h @@ -19,6 +19,21 @@ namespace xe { namespace kernel { +struct X_KENUMERATOR { + be app_id; + be message; + be message2; + be user_index; + be items_per_enumerate; + be flags; +}; +static_assert_size(X_KENUMERATOR, 0x18); + +struct X_KENUMERATOR_CONTENT_AGGREGATE { + be magic; + be handle; +}; + class XEnumerator : public XObject { public: static const XObject::Type kObjectType = XObject::Type::Enumerator; @@ -27,7 +42,24 @@ class XEnumerator : public XObject { size_t item_size); virtual ~XEnumerator(); - void Initialize(); + X_STATUS Initialize(uint32_t user_index, uint32_t app_id, uint32_t message, + uint32_t message2, uint32_t flags, uint32_t extra_size, + void** extra_buffer); + + X_STATUS Initialize(uint32_t user_index, uint32_t app_id, uint32_t message, + uint32_t message2, uint32_t flags); + + template + X_STATUS Initialize(uint32_t user_index, uint32_t app_id, uint32_t message, + uint32_t message2, uint32_t flags, T** extra) { + void* dummy; + auto result = Initialize(user_index, app_id, message, message2, flags, + static_cast(sizeof(T)), &dummy); + if (extra) { + *extra = XFAILED(result) ? nullptr : static_cast(dummy); + } + return result; + } virtual uint32_t item_count() const = 0; virtual void WriteItems(uint8_t* buffer) = 0; diff --git a/src/xenia/kernel/xnotifylistener.cc b/src/xenia/kernel/xnotifylistener.cc index be920f157..1ea9af976 100644 --- a/src/xenia/kernel/xnotifylistener.cc +++ b/src/xenia/kernel/xnotifylistener.cc @@ -10,6 +10,7 @@ #include "xenia/kernel/xnotifylistener.h" #include "xenia/base/byte_stream.h" +#include "xenia/base/logging.h" #include "xenia/kernel/kernel_state.h" namespace xe { @@ -20,21 +21,26 @@ XNotifyListener::XNotifyListener(KernelState* kernel_state) XNotifyListener::~XNotifyListener() {} -void XNotifyListener::Initialize(uint64_t mask) { +void XNotifyListener::Initialize(uint64_t mask, uint32_t max_version) { assert_false(wait_handle_); wait_handle_ = xe::threading::Event::CreateManualResetEvent(false); mask_ = mask; + max_version_ = max_version; kernel_state_->RegisterNotifyListener(this); } void XNotifyListener::EnqueueNotification(XNotificationID id, uint32_t data) { + auto key = XNotificationKey{id}; // Ignore if the notification doesn't match our mask. - if ((mask_ & uint64_t(1ULL << (id >> 25))) == 0) { + if ((mask_ & uint64_t(1ULL << key.mask_index)) == 0) { + return; + } + // Ignore if the notification is too new. + if (key.version > max_version_) { return; } - auto global_lock = global_critical_region_.Acquire(); notifications_.push_back(std::pair(id, data)); wait_handle_->Set(); @@ -60,16 +66,14 @@ bool XNotifyListener::DequeueNotification(XNotificationID* out_id, bool XNotifyListener::DequeueNotification(XNotificationID id, uint32_t* out_data) { auto global_lock = global_critical_region_.Acquire(); - bool dequeued = false; if (!notifications_.size()) { - return dequeued; + return false; } - + bool dequeued = false; for (auto it = notifications_.begin(); it != notifications_.end(); ++it) { if (it->first != id) { continue; } - dequeued = true; *out_data = it->second; notifications_.erase(it); @@ -85,12 +89,11 @@ bool XNotifyListener::Save(ByteStream* stream) { SaveObject(stream); stream->Write(mask_); + stream->Write(max_version_); stream->Write(notifications_.size()); - if (notifications_.size()) { - for (auto pair : notifications_) { - stream->Write(pair.first); - stream->Write(pair.second); - } + for (auto pair : notifications_) { + stream->Write(pair.first); + stream->Write(pair.second); } return true; @@ -102,7 +105,10 @@ object_ref XNotifyListener::Restore(KernelState* kernel_state, notify->kernel_state_ = kernel_state; notify->RestoreObject(stream); - notify->Initialize(stream->Read()); + + auto mask = stream->Read(); + auto max_version = stream->Read(); + notify->Initialize(mask, max_version); auto notification_count_ = stream->Read(); for (size_t i = 0; i < notification_count_; i++) { diff --git a/src/xenia/kernel/xnotifylistener.h b/src/xenia/kernel/xnotifylistener.h index 38c19b63b..0e694bd2e 100644 --- a/src/xenia/kernel/xnotifylistener.h +++ b/src/xenia/kernel/xnotifylistener.h @@ -21,6 +21,24 @@ namespace xe { namespace kernel { +union XNotificationKey { + struct { + uint32_t local_id : 16; + uint32_t version : 9; + uint32_t mask_index : 6; + uint32_t : 1; + }; + XNotificationID id; + + static constexpr XNotificationID get_id(uint8_t mask_index, + uint16_t local_id) { + XNotificationKey key = {}; + key.mask_index = mask_index; + key.local_id = local_id; + return key.id; + } +}; + class XNotifyListener : public XObject { public: static const XObject::Type kObjectType = XObject::Type::NotifyListener; @@ -29,8 +47,9 @@ class XNotifyListener : public XObject { ~XNotifyListener() override; uint64_t mask() const { return mask_; } + uint32_t max_version() const { return max_version_; } - void Initialize(uint64_t mask); + void Initialize(uint64_t mask, uint32_t max_version); void EnqueueNotification(XNotificationID id, uint32_t data); bool DequeueNotification(XNotificationID* out_id, uint32_t* out_data); @@ -50,6 +69,7 @@ class XNotifyListener : public XObject { xe::global_critical_region global_critical_region_; std::vector> notifications_; uint64_t mask_ = 0; + uint32_t max_version_ = 0; }; } // namespace kernel diff --git a/src/xenia/xbox.h b/src/xenia/xbox.h index 40ad730a1..62f4a1f65 100644 --- a/src/xenia/xbox.h +++ b/src/xenia/xbox.h @@ -94,6 +94,7 @@ typedef uint32_t X_RESULT; #define X_ERROR_BAD_ARGUMENTS X_RESULT_FROM_WIN32(0x000000A0L) #define X_ERROR_BUSY X_RESULT_FROM_WIN32(0x000000AAL) #define X_ERROR_ALREADY_EXISTS X_RESULT_FROM_WIN32(0x000000B7L) +#define X_ERROR_IO_INCOMPLETE X_RESULT_FROM_WIN32(0x000003E4L) #define X_ERROR_IO_PENDING X_RESULT_FROM_WIN32(0x000003E5L) #define X_ERROR_DEVICE_NOT_CONNECTED X_RESULT_FROM_WIN32(0x0000048FL) #define X_ERROR_NOT_FOUND X_RESULT_FROM_WIN32(0x00000490L) @@ -112,6 +113,8 @@ typedef uint32_t X_HRESULT; #define X_E_FALSE static_cast(0x80000000L) #define X_E_SUCCESS X_HRESULT_FROM_WIN32(X_ERROR_SUCCESS) +#define X_E_FAIL static_cast(0x80004005L) +#define X_E_NO_MORE_FILES X_HRESULT_FROM_WIN32(X_ERROR_NO_MORE_FILES) #define X_E_INVALIDARG X_HRESULT_FROM_WIN32(X_ERROR_INVALID_PARAMETER) #define X_E_DEVICE_NOT_CONNECTED X_HRESULT_FROM_WIN32(X_ERROR_DEVICE_NOT_CONNECTED) #define X_E_NOTFOUND X_HRESULT_FROM_WIN32(X_ERROR_NOT_FOUND) diff --git a/third_party/premake-core b/third_party/premake-core index df6096721..7eba28258 160000 --- a/third_party/premake-core +++ b/third_party/premake-core @@ -1 +1 @@ -Subproject commit df609672110ac07ff7ea6597911575c4365c2928 +Subproject commit 7eba2825887e49d3a72b30e0a7480bd427a5bab0 diff --git a/xb.ps1 b/xb.ps1 new file mode 100644 index 000000000..9e24cf384 --- /dev/null +++ b/xb.ps1 @@ -0,0 +1,24 @@ +function Write-FatalError($message) { + [Console]::ForegroundColor = 'red' + [Console]::Error.WriteLine($message) + [Console]::ResetColor() + Exit 1 +} + +$pythonPath = Get-Command python | Select-Object -ExpandProperty Definition + +# Check for 'python3' if 'python' isn't found +if ([string]::IsNullOrEmpty($pythonPath)) { + $pythonPath = Get-Command python3 | Select-Object -ExpandProperty Definition +} +# Neither found, error and exit +if ([string]::IsNullOrEmpty($pythonPath)) { + Write-FatalError "ERROR: no Python executable found on PATH.`nMake sure you can run 'python' or 'python3' in a Command Prompt." +} + +python -c "import sys; sys.exit(1 if not sys.version_info[:2] >= (3, 4) else 0)" +if ($LASTEXITCODE -gt 0) { + Write-FatalError "ERROR: Python version mismatch, not at least 3.4.`nFound Python executable was $($pythonPath)" +} + +python "$($PSScriptRoot)/xenia-build" $args \ No newline at end of file