diff --git a/src/xenia/kernel/xam/apps/xam_app.cc b/src/xenia/kernel/xam/apps/xam_app.cc index 1d57001c7..601387754 100644 --- a/src/xenia/kernel/xam/apps/xam_app.cc +++ b/src/xenia/kernel/xam/apps/xam_app.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2021 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -56,7 +56,12 @@ X_HRESULT XamApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr, if (data->buffer_size) { std::memset(buffer, 0, data->buffer_size); } - if (e->WriteItem(buffer)) { + uint32_t item_count = 0; + auto result = e->WriteItems(data->buffer_ptr, buffer, data->buffer_size, + &item_count); + assert_true(XSUCCEEDED(result)); + assert_true(item_count == 1); + if (XSUCCEEDED(result)) { auto content_data = reinterpret_cast(buffer); // TODO(gibbed): WTF? *reinterpret_cast*>(&buffer[0x140]) = diff --git a/src/xenia/kernel/xam/xam_enum.cc b/src/xenia/kernel/xam/xam_enum.cc index 9aec9b056..2a69884ca 100644 --- a/src/xenia/kernel/xam/xam_enum.cc +++ b/src/xenia/kernel/xam/xam_enum.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2020 Ben Vanik. All rights reserved. * + * Copyright 2021 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -27,8 +27,8 @@ 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 xeXamEnumerate(uint32_t handle, uint32_t flags, lpvoid_t buffer_ptr, + uint32_t buffer_size, uint32_t* items_returned, uint32_t overlapped_ptr) { assert_true(flags == 0); @@ -39,36 +39,26 @@ uint32_t xeXamEnumerate(uint32_t handle, uint32_t flags, void* buffer, if (!e) { result = X_ERROR_INVALID_HANDLE; } else { - size_t actual_buffer_length = buffer_length; - if (buffer_length == e->items_per_enumerate()) { - actual_buffer_length = e->item_size() * e->items_per_enumerate(); + size_t needed_buffer_size = e->item_size() * e->items_per_enumerate(); + + uint32_t actual_buffer_size = buffer_size; + if (buffer_size == e->items_per_enumerate()) { + actual_buffer_size = static_cast(needed_buffer_size); // 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(), + "Broken usage of XamEnumerate! buffer size={:X} vs actual " + "size={:X} (item size={:X}, items per enumerate={})", + buffer_size, actual_buffer_size, e->item_size(), e->items_per_enumerate()); } - std::memset(buffer, 0, actual_buffer_length); - - 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; + result = X_ERROR_INSUFFICIENT_BUFFER; + if (actual_buffer_size >= needed_buffer_size) { + buffer_ptr.Zero(actual_buffer_size); + result = + e->WriteItems(buffer_ptr.guest_address(), buffer_ptr.as(), + actual_buffer_size, &item_count); } } diff --git a/src/xenia/kernel/xam/xam_user.cc b/src/xenia/kernel/xam/xam_user.cc index 92a5142bd..e91717f5b 100644 --- a/src/xenia/kernel/xam/xam_user.cc +++ b/src/xenia/kernel/xam/xam_user.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2013 Ben Vanik. All rights reserved. * + * Copyright 2021 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -552,23 +552,162 @@ dword_result_t XamShowSigninUI(dword_t unk, dword_t unk_mask) { } DECLARE_XAM_EXPORT1(XamShowSigninUI, kUserProfiles, kStub); +// TODO(gibbed): probably a FILETIME/LARGE_INTEGER, unknown currently +struct X_ACHIEVEMENT_UNLOCK_TIME { + xe::be unk_0; + xe::be unk_4; +}; + +struct X_ACHIEVEMENT_DETAILS { + xe::be id; + xe::be label_ptr; + xe::be description_ptr; + xe::be unachieved_ptr; + xe::be image_id; + xe::be gamerscore; + X_ACHIEVEMENT_UNLOCK_TIME unlock_time; + xe::be flags; + + static const size_t kStringBufferSize = 464; +}; +static_assert_size(X_ACHIEVEMENT_DETAILS, 36); + +class XStaticAchievementEnumerator : public XEnumerator { + public: + struct AchievementDetails { + uint32_t id; + std::u16string label; + std::u16string description; + std::u16string unachieved; + uint32_t image_id; + uint32_t gamerscore; + struct { + uint32_t unk_0; + uint32_t unk_4; + } unlock_time; + uint32_t flags; + }; + + XStaticAchievementEnumerator(KernelState* kernel_state, + size_t items_per_enumerate, uint32_t flags) + : XEnumerator( + kernel_state, items_per_enumerate, + sizeof(X_ACHIEVEMENT_DETAILS) + + (!!(flags & 7) ? X_ACHIEVEMENT_DETAILS::kStringBufferSize : 0)), + flags_(flags) {} + + void AppendItem(AchievementDetails item) { + items_.push_back(std::move(item)); + } + + uint32_t WriteItems(uint32_t buffer_ptr, uint8_t* buffer_data, + uint32_t buffer_size, uint32_t* written_count) override { + size_t count = + std::min(items_.size() - current_item_, items_per_enumerate()); + if (!count) { + return X_ERROR_NO_MORE_FILES; + } + + size_t size = count * item_size(); + if (size > buffer_size) { + return X_ERROR_INSUFFICIENT_BUFFER; + } + + auto details = reinterpret_cast(buffer_data); + size_t string_offset = + items_per_enumerate() * sizeof(X_ACHIEVEMENT_DETAILS); + auto string_buffer = + StringBuffer{buffer_ptr + static_cast(string_offset), + &buffer_data[string_offset], + count * X_ACHIEVEMENT_DETAILS::kStringBufferSize}; + for (size_t i = 0, o = current_item_; i < count; ++i, ++current_item_) { + const auto& item = items_[current_item_]; + details[i].id = item.id; + details[i].label_ptr = + !!(flags_ & 1) ? AppendString(string_buffer, item.label) : 0; + details[i].description_ptr = + !!(flags_ & 2) ? AppendString(string_buffer, item.description) : 0; + details[i].unachieved_ptr = + !!(flags_ & 4) ? AppendString(string_buffer, item.unachieved) : 0; + details[i].image_id = item.image_id; + details[i].gamerscore = item.gamerscore; + details[i].unlock_time.unk_0 = item.unlock_time.unk_0; + details[i].unlock_time.unk_4 = item.unlock_time.unk_4; + details[i].flags = item.flags; + } + + if (written_count) { + *written_count = static_cast(count); + } + + return X_ERROR_SUCCESS; + } + + private: + struct StringBuffer { + uint32_t ptr; + uint8_t* data; + size_t remaining_bytes; + }; + + uint32_t AppendString(StringBuffer& sb, const std::u16string_view string) { + size_t count = string.length() + 1; + size_t size = count * sizeof(char16_t); + if (size > sb.remaining_bytes) { + assert_always(); + return 0; + } + auto ptr = sb.ptr; + string_util::copy_and_swap_truncating(reinterpret_cast(sb.data), + string, count); + sb.ptr += static_cast(size); + sb.data += size; + sb.remaining_bytes -= size; + return ptr; + } + + private: + uint32_t flags_; + std::vector items_; + size_t current_item_ = 0; +}; + dword_result_t XamUserCreateAchievementEnumerator(dword_t title_id, dword_t user_index, dword_t xuid, dword_t flags, dword_t offset, dword_t count, lpdword_t buffer_size_ptr, lpdword_t handle_ptr) { - if (buffer_size_ptr) { - *buffer_size_ptr = 500 * count; + if (!count || !buffer_size_ptr || !handle_ptr) { + return X_ERROR_INVALID_PARAMETER; } - auto e = object_ref( - new XStaticEnumerator(kernel_state(), count, 500)); + if (user_index >= 4) { + return X_ERROR_INVALID_PARAMETER; + } + + size_t entry_size = sizeof(X_ACHIEVEMENT_DETAILS); + if (flags & 7) { + entry_size += X_ACHIEVEMENT_DETAILS::kStringBufferSize; + } + + if (buffer_size_ptr) { + *buffer_size_ptr = static_cast(entry_size) * count; + } + + auto e = object_ref( + new XStaticAchievementEnumerator(kernel_state(), count, flags)); auto result = e->Initialize(user_index, 0xFB, 0xB000A, 0xB000B, 0); if (XFAILED(result)) { return result; } + for (uint8_t i = 0; i < count; ++i) { + auto item = XStaticAchievementEnumerator::AchievementDetails{ + i, u"Dummy Text", u"Dummy Text", u"Dummy Text"}; + e->AppendItem(item); + } + *handle_ptr = e->handle(); return X_ERROR_SUCCESS; } diff --git a/src/xenia/kernel/xenumerator.cc b/src/xenia/kernel/xenumerator.cc index 693d756e0..4b5ea7216 100644 --- a/src/xenia/kernel/xenumerator.cc +++ b/src/xenia/kernel/xenumerator.cc @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2014 Ben Vanik. All rights reserved. * + * Copyright 2021 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -49,5 +49,38 @@ X_STATUS XEnumerator::Initialize(uint32_t user_index, uint32_t app_id, return Initialize(user_index, app_id, message, message2, flags, 0, nullptr); } +uint8_t* XStaticEnumerator::AppendItem() { + buffer_.resize(++item_count_ * item_size()); + auto ptr = + const_cast(buffer_.data() + (item_count_ - 1) * item_size()); + return ptr; +} + +uint32_t XStaticEnumerator::WriteItems(uint32_t buffer_ptr, + uint8_t* buffer_data, + uint32_t buffer_size, + uint32_t* written_count) { + size_t count = std::min(item_count_ - current_item_, items_per_enumerate()); + if (!count) { + return X_ERROR_NO_MORE_FILES; + } + + size_t size = count * item_size(); + if (size > buffer_size) { + return X_ERROR_INSUFFICIENT_BUFFER; + } + + size_t offset = current_item_ * item_size(); + std::memcpy(buffer_data, buffer_.data() + offset, size); + + current_item_ += count; + + if (written_count) { + *written_count = static_cast(count); + } + + return X_ERROR_SUCCESS; +} + } // namespace kernel } // namespace xe diff --git a/src/xenia/kernel/xenumerator.h b/src/xenia/kernel/xenumerator.h index 0217b02e5..3af03241d 100644 --- a/src/xenia/kernel/xenumerator.h +++ b/src/xenia/kernel/xenumerator.h @@ -2,7 +2,7 @@ ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** - * Copyright 2014 Ben Vanik. All rights reserved. * + * Copyright 2021 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ @@ -10,6 +10,7 @@ #ifndef XENIA_KERNEL_XENUMERATOR_H_ #define XENIA_KERNEL_XENUMERATOR_H_ +#include #include #include @@ -61,18 +62,16 @@ class XEnumerator : public XObject { return result; } - virtual uint32_t item_count() const = 0; - virtual void WriteItems(uint8_t* buffer) = 0; - virtual bool WriteItem(uint8_t* buffer) = 0; + virtual uint32_t WriteItems(uint32_t buffer_ptr, uint8_t* buffer_data, + uint32_t buffer_size, + uint32_t* written_count) = 0; size_t item_size() const { return item_size_; } size_t items_per_enumerate() const { return items_per_enumerate_; } - size_t current_item() const { return current_item_; } - protected: - size_t items_per_enumerate_ = 0; - size_t item_size_ = 0; - size_t current_item_ = 0; + private: + size_t items_per_enumerate_; + size_t item_size_; }; class XStaticEnumerator : public XEnumerator { @@ -80,35 +79,19 @@ class XStaticEnumerator : public XEnumerator { XStaticEnumerator(KernelState* kernel_state, size_t items_per_enumerate, size_t item_size) : XEnumerator(kernel_state, items_per_enumerate, item_size), - item_count_(0) {} + item_count_(0), + current_item_(0) {} - uint32_t item_count() const override { return item_count_; } + size_t item_count() const { return item_count_; } - uint8_t* AppendItem() { - buffer_.resize(++item_count_ * item_size_); - auto ptr = - const_cast(buffer_.data() + (item_count_ - 1) * item_size_); - return ptr; - } + uint8_t* AppendItem(); - void WriteItems(uint8_t* buffer) override { - std::memcpy(buffer, buffer_.data(), item_count_ * item_size_); - } - - bool WriteItem(uint8_t* buffer) override { - if (current_item_ >= item_count_) { - return false; - } - - std::memcpy(buffer, buffer_.data() + current_item_ * item_size_, - item_size_); - current_item_++; - - return true; - } + uint32_t WriteItems(uint32_t buffer_ptr, uint8_t* buffer_data, + uint32_t buffer_size, uint32_t* written_count) override; private: - uint32_t item_count_; + size_t item_count_; + size_t current_item_; std::vector buffer_; };