[Kernel/XAM] Dummy X_ACHIEVEMENT_DETAILS support
Co-authored-by: gibbed <rick@gibbed.us>
This commit is contained in:
parent
57ce647bb3
commit
d2ca54d909
|
@ -2,7 +2,7 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
* Xenia : Xbox 360 Emulator Research Project *
|
* 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. *
|
* 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) {
|
if (data->buffer_size) {
|
||||||
std::memset(buffer, 0, 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<XCONTENT_AGGREGATE_DATA*>(buffer);
|
auto content_data = reinterpret_cast<XCONTENT_AGGREGATE_DATA*>(buffer);
|
||||||
// TODO(gibbed): WTF?
|
// TODO(gibbed): WTF?
|
||||||
*reinterpret_cast<be<uint32_t>*>(&buffer[0x140]) =
|
*reinterpret_cast<be<uint32_t>*>(&buffer[0x140]) =
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
* Xenia : Xbox 360 Emulator Research Project *
|
* 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. *
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
@ -27,8 +27,8 @@ namespace kernel {
|
||||||
namespace xam {
|
namespace xam {
|
||||||
|
|
||||||
// https://github.com/LestaD/SourceEngine2007/blob/master/se2007/engine/xboxsystem.cpp#L518
|
// 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 xeXamEnumerate(uint32_t handle, uint32_t flags, lpvoid_t buffer_ptr,
|
||||||
uint32_t buffer_length, uint32_t* items_returned,
|
uint32_t buffer_size, uint32_t* items_returned,
|
||||||
uint32_t overlapped_ptr) {
|
uint32_t overlapped_ptr) {
|
||||||
assert_true(flags == 0);
|
assert_true(flags == 0);
|
||||||
|
|
||||||
|
@ -39,36 +39,26 @@ uint32_t xeXamEnumerate(uint32_t handle, uint32_t flags, void* buffer,
|
||||||
if (!e) {
|
if (!e) {
|
||||||
result = X_ERROR_INVALID_HANDLE;
|
result = X_ERROR_INVALID_HANDLE;
|
||||||
} else {
|
} else {
|
||||||
size_t actual_buffer_length = buffer_length;
|
size_t needed_buffer_size = e->item_size() * e->items_per_enumerate();
|
||||||
if (buffer_length == e->items_per_enumerate()) {
|
|
||||||
actual_buffer_length = 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<uint32_t>(needed_buffer_size);
|
||||||
// Known culprits:
|
// Known culprits:
|
||||||
// Final Fight: Double Impact (saves)
|
// Final Fight: Double Impact (saves)
|
||||||
XELOGW(
|
XELOGW(
|
||||||
"Broken usage of XamEnumerate! buffer length={:X} vs actual "
|
"Broken usage of XamEnumerate! buffer size={:X} vs actual "
|
||||||
"length={:X} "
|
"size={:X} (item size={:X}, items per enumerate={})",
|
||||||
"(item size={:X}, items per enumerate={})",
|
buffer_size, actual_buffer_size, e->item_size(),
|
||||||
(uint32_t)buffer_length, actual_buffer_length, e->item_size(),
|
|
||||||
e->items_per_enumerate());
|
e->items_per_enumerate());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::memset(buffer, 0, actual_buffer_length);
|
result = X_ERROR_INSUFFICIENT_BUFFER;
|
||||||
|
if (actual_buffer_size >= needed_buffer_size) {
|
||||||
if (actual_buffer_length < e->item_size()) {
|
buffer_ptr.Zero(actual_buffer_size);
|
||||||
result = X_ERROR_INSUFFICIENT_BUFFER;
|
result =
|
||||||
} else if (e->current_item() >= e->item_count()) {
|
e->WriteItems(buffer_ptr.guest_address(), buffer_ptr.as<uint8_t*>(),
|
||||||
result = X_ERROR_NO_MORE_FILES;
|
actual_buffer_size, &item_count);
|
||||||
} else {
|
|
||||||
auto item_buffer = static_cast<uint8_t*>(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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
* Xenia : Xbox 360 Emulator Research Project *
|
* 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. *
|
* 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);
|
DECLARE_XAM_EXPORT1(XamShowSigninUI, kUserProfiles, kStub);
|
||||||
|
|
||||||
|
// TODO(gibbed): probably a FILETIME/LARGE_INTEGER, unknown currently
|
||||||
|
struct X_ACHIEVEMENT_UNLOCK_TIME {
|
||||||
|
xe::be<uint32_t> unk_0;
|
||||||
|
xe::be<uint32_t> unk_4;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct X_ACHIEVEMENT_DETAILS {
|
||||||
|
xe::be<uint32_t> id;
|
||||||
|
xe::be<uint32_t> label_ptr;
|
||||||
|
xe::be<uint32_t> description_ptr;
|
||||||
|
xe::be<uint32_t> unachieved_ptr;
|
||||||
|
xe::be<uint32_t> image_id;
|
||||||
|
xe::be<uint32_t> gamerscore;
|
||||||
|
X_ACHIEVEMENT_UNLOCK_TIME unlock_time;
|
||||||
|
xe::be<uint32_t> 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<X_ACHIEVEMENT_DETAILS*>(buffer_data);
|
||||||
|
size_t string_offset =
|
||||||
|
items_per_enumerate() * sizeof(X_ACHIEVEMENT_DETAILS);
|
||||||
|
auto string_buffer =
|
||||||
|
StringBuffer{buffer_ptr + static_cast<uint32_t>(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<uint32_t>(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<char16_t*>(sb.data),
|
||||||
|
string, count);
|
||||||
|
sb.ptr += static_cast<uint32_t>(size);
|
||||||
|
sb.data += size;
|
||||||
|
sb.remaining_bytes -= size;
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32_t flags_;
|
||||||
|
std::vector<AchievementDetails> items_;
|
||||||
|
size_t current_item_ = 0;
|
||||||
|
};
|
||||||
|
|
||||||
dword_result_t XamUserCreateAchievementEnumerator(dword_t title_id,
|
dword_result_t XamUserCreateAchievementEnumerator(dword_t title_id,
|
||||||
dword_t user_index,
|
dword_t user_index,
|
||||||
dword_t xuid, dword_t flags,
|
dword_t xuid, dword_t flags,
|
||||||
dword_t offset, dword_t count,
|
dword_t offset, dword_t count,
|
||||||
lpdword_t buffer_size_ptr,
|
lpdword_t buffer_size_ptr,
|
||||||
lpdword_t handle_ptr) {
|
lpdword_t handle_ptr) {
|
||||||
if (buffer_size_ptr) {
|
if (!count || !buffer_size_ptr || !handle_ptr) {
|
||||||
*buffer_size_ptr = 500 * count;
|
return X_ERROR_INVALID_PARAMETER;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto e = object_ref<XStaticEnumerator>(
|
if (user_index >= 4) {
|
||||||
new XStaticEnumerator(kernel_state(), count, 500));
|
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<uint32_t>(entry_size) * count;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto e = object_ref<XStaticAchievementEnumerator>(
|
||||||
|
new XStaticAchievementEnumerator(kernel_state(), count, flags));
|
||||||
auto result = e->Initialize(user_index, 0xFB, 0xB000A, 0xB000B, 0);
|
auto result = e->Initialize(user_index, 0xFB, 0xB000A, 0xB000B, 0);
|
||||||
if (XFAILED(result)) {
|
if (XFAILED(result)) {
|
||||||
return 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();
|
*handle_ptr = e->handle();
|
||||||
return X_ERROR_SUCCESS;
|
return X_ERROR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
* Xenia : Xbox 360 Emulator Research Project *
|
* 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. *
|
* 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);
|
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<uint8_t*>(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<uint32_t>(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
return X_ERROR_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace kernel
|
} // namespace kernel
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
* Xenia : Xbox 360 Emulator Research Project *
|
* 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. *
|
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
@ -10,6 +10,7 @@
|
||||||
#ifndef XENIA_KERNEL_XENUMERATOR_H_
|
#ifndef XENIA_KERNEL_XENUMERATOR_H_
|
||||||
#define XENIA_KERNEL_XENUMERATOR_H_
|
#define XENIA_KERNEL_XENUMERATOR_H_
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -61,18 +62,16 @@ class XEnumerator : public XObject {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual uint32_t item_count() const = 0;
|
virtual uint32_t WriteItems(uint32_t buffer_ptr, uint8_t* buffer_data,
|
||||||
virtual void WriteItems(uint8_t* buffer) = 0;
|
uint32_t buffer_size,
|
||||||
virtual bool WriteItem(uint8_t* buffer) = 0;
|
uint32_t* written_count) = 0;
|
||||||
|
|
||||||
size_t item_size() const { return item_size_; }
|
size_t item_size() const { return item_size_; }
|
||||||
size_t items_per_enumerate() const { return items_per_enumerate_; }
|
size_t items_per_enumerate() const { return items_per_enumerate_; }
|
||||||
size_t current_item() const { return current_item_; }
|
|
||||||
|
|
||||||
protected:
|
private:
|
||||||
size_t items_per_enumerate_ = 0;
|
size_t items_per_enumerate_;
|
||||||
size_t item_size_ = 0;
|
size_t item_size_;
|
||||||
size_t current_item_ = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class XStaticEnumerator : public XEnumerator {
|
class XStaticEnumerator : public XEnumerator {
|
||||||
|
@ -80,35 +79,19 @@ class XStaticEnumerator : public XEnumerator {
|
||||||
XStaticEnumerator(KernelState* kernel_state, size_t items_per_enumerate,
|
XStaticEnumerator(KernelState* kernel_state, size_t items_per_enumerate,
|
||||||
size_t item_size)
|
size_t item_size)
|
||||||
: XEnumerator(kernel_state, items_per_enumerate, 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() {
|
uint8_t* AppendItem();
|
||||||
buffer_.resize(++item_count_ * item_size_);
|
|
||||||
auto ptr =
|
|
||||||
const_cast<uint8_t*>(buffer_.data() + (item_count_ - 1) * item_size_);
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void WriteItems(uint8_t* buffer) override {
|
uint32_t WriteItems(uint32_t buffer_ptr, uint8_t* buffer_data,
|
||||||
std::memcpy(buffer, buffer_.data(), item_count_ * item_size_);
|
uint32_t buffer_size, uint32_t* written_count) override;
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint32_t item_count_;
|
size_t item_count_;
|
||||||
|
size_t current_item_;
|
||||||
std::vector<uint8_t> buffer_;
|
std::vector<uint8_t> buffer_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue