Cleaned up XamUserReadProfileSettings. Also fixed writing out pointers for

setting data, fixing issues with games trying to read from zero page after
reading profile settings.
This commit is contained in:
gibbed 2015-06-05 18:52:53 -05:00
parent edf8d1a74d
commit 1d02ce63d3
2 changed files with 68 additions and 56 deletions

View File

@ -50,7 +50,7 @@ class UserProfile {
: setting_id(setting_id), type(type), size(size) {} : setting_id(setting_id), type(type), size(size) {}
virtual size_t extra_size() const { return 0; } virtual size_t extra_size() const { return 0; }
virtual size_t Append(uint8_t* user_data, uint8_t* buffer, virtual size_t Append(uint8_t* user_data, uint8_t* buffer,
size_t buffer_offset) { uint32_t buffer_ptr, size_t buffer_offset) {
xe::store_and_swap<uint8_t>(user_data + kTypeOffset, xe::store_and_swap<uint8_t>(user_data + kTypeOffset,
static_cast<uint8_t>(type)); static_cast<uint8_t>(type));
return buffer_offset; return buffer_offset;
@ -67,8 +67,8 @@ class UserProfile {
: Setting(setting_id, Type::INT32, 4), value(value) {} : Setting(setting_id, Type::INT32, 4), value(value) {}
int32_t value; int32_t value;
size_t Append(uint8_t* user_data, uint8_t* buffer, size_t Append(uint8_t* user_data, uint8_t* buffer,
size_t buffer_offset) override { uint32_t buffer_ptr, size_t buffer_offset) override {
buffer_offset = Setting::Append(user_data, buffer, buffer_offset); buffer_offset = Setting::Append(user_data, buffer, buffer_ptr, buffer_offset);
xe::store_and_swap<int32_t>(user_data + kValueOffset, value); xe::store_and_swap<int32_t>(user_data + kValueOffset, value);
return buffer_offset; return buffer_offset;
} }
@ -78,8 +78,8 @@ class UserProfile {
: Setting(setting_id, Type::INT64, 8), value(value) {} : Setting(setting_id, Type::INT64, 8), value(value) {}
int64_t value; int64_t value;
size_t Append(uint8_t* user_data, uint8_t* buffer, size_t Append(uint8_t* user_data, uint8_t* buffer,
size_t buffer_offset) override { uint32_t buffer_ptr, size_t buffer_offset) override {
buffer_offset = Setting::Append(user_data, buffer, buffer_offset); buffer_offset = Setting::Append(user_data, buffer, buffer_ptr, buffer_offset);
xe::store_and_swap<int64_t>(user_data + kValueOffset, value); xe::store_and_swap<int64_t>(user_data + kValueOffset, value);
return buffer_offset; return buffer_offset;
} }
@ -89,8 +89,8 @@ class UserProfile {
: Setting(setting_id, Type::DOUBLE, 8), value(value) {} : Setting(setting_id, Type::DOUBLE, 8), value(value) {}
double value; double value;
size_t Append(uint8_t* user_data, uint8_t* buffer, size_t Append(uint8_t* user_data, uint8_t* buffer,
size_t buffer_offset) override { uint32_t buffer_ptr, size_t buffer_offset) override {
buffer_offset = Setting::Append(user_data, buffer, buffer_offset); buffer_offset = Setting::Append(user_data, buffer, buffer_ptr, buffer_offset);
xe::store_and_swap<double>(user_data + kValueOffset, value); xe::store_and_swap<double>(user_data + kValueOffset, value);
return buffer_offset; return buffer_offset;
} }
@ -103,8 +103,8 @@ class UserProfile {
return value.empty() ? 0 : 2 * (static_cast<int32_t>(value.size()) + 1); return value.empty() ? 0 : 2 * (static_cast<int32_t>(value.size()) + 1);
} }
size_t Append(uint8_t* user_data, uint8_t* buffer, size_t Append(uint8_t* user_data, uint8_t* buffer,
size_t buffer_offset) override { uint32_t buffer_ptr, size_t buffer_offset) override {
buffer_offset = Setting::Append(user_data, buffer, buffer_offset); buffer_offset = Setting::Append(user_data, buffer, buffer_ptr, buffer_offset);
int32_t length; int32_t length;
if (value.empty()) { if (value.empty()) {
length = 0; length = 0;
@ -114,7 +114,7 @@ class UserProfile {
length = 2 * (static_cast<int32_t>(value.size()) + 1); length = 2 * (static_cast<int32_t>(value.size()) + 1);
xe::store_and_swap<int32_t>(user_data + kValueOffset, length); xe::store_and_swap<int32_t>(user_data + kValueOffset, length);
xe::store_and_swap<uint32_t>(user_data + kPointerOffset, xe::store_and_swap<uint32_t>(user_data + kPointerOffset,
static_cast<uint32_t>(buffer_offset)); buffer_ptr + static_cast<uint32_t>(buffer_offset));
memcpy(buffer + buffer_offset, value.data(), length); memcpy(buffer + buffer_offset, value.data(), length);
} }
return buffer_offset + length; return buffer_offset + length;
@ -125,8 +125,8 @@ class UserProfile {
: Setting(setting_id, Type::FLOAT, 4), value(value) {} : Setting(setting_id, Type::FLOAT, 4), value(value) {}
float value; float value;
size_t Append(uint8_t* user_data, uint8_t* buffer, size_t Append(uint8_t* user_data, uint8_t* buffer,
size_t buffer_offset) override { uint32_t buffer_ptr, size_t buffer_offset) override {
buffer_offset = Setting::Append(user_data, buffer, buffer_offset); buffer_offset = Setting::Append(user_data, buffer, buffer_ptr, buffer_offset);
xe::store_and_swap<float>(user_data + kValueOffset, value); xe::store_and_swap<float>(user_data + kValueOffset, value);
return buffer_offset; return buffer_offset;
} }
@ -139,8 +139,8 @@ class UserProfile {
return static_cast<int32_t>(value.size()); return static_cast<int32_t>(value.size());
} }
size_t Append(uint8_t* user_data, uint8_t* buffer, size_t Append(uint8_t* user_data, uint8_t* buffer,
size_t buffer_offset) override { uint32_t buffer_ptr, size_t buffer_offset) override {
buffer_offset = Setting::Append(user_data, buffer, buffer_offset); buffer_offset = Setting::Append(user_data, buffer, buffer_ptr, buffer_offset);
int32_t length; int32_t length;
if (value.empty()) { if (value.empty()) {
length = 0; length = 0;
@ -150,7 +150,7 @@ class UserProfile {
length = static_cast<int32_t>(value.size()); length = static_cast<int32_t>(value.size());
xe::store_and_swap<int32_t>(user_data + kValueOffset, length); xe::store_and_swap<int32_t>(user_data + kValueOffset, length);
xe::store_and_swap<uint32_t>(user_data + kPointerOffset, xe::store_and_swap<uint32_t>(user_data + kPointerOffset,
static_cast<uint32_t>(buffer_offset)); buffer_ptr + static_cast<uint32_t>(buffer_offset));
memcpy(buffer + buffer_offset, value.data(), length); memcpy(buffer + buffer_offset, value.data(), length);
} }
return buffer_offset + length; return buffer_offset + length;
@ -161,8 +161,8 @@ class UserProfile {
: Setting(setting_id, Type::DATETIME, 8), value(value) {} : Setting(setting_id, Type::DATETIME, 8), value(value) {}
int64_t value; int64_t value;
size_t Append(uint8_t* user_data, uint8_t* buffer, size_t Append(uint8_t* user_data, uint8_t* buffer,
size_t buffer_offset) override { uint32_t buffer_ptr, size_t buffer_offset) override {
buffer_offset = Setting::Append(user_data, buffer, buffer_offset); buffer_offset = Setting::Append(user_data, buffer, buffer_ptr, buffer_offset);
xe::store_and_swap<int64_t>(user_data + kValueOffset, value); xe::store_and_swap<int64_t>(user_data + kValueOffset, value);
return buffer_offset; return buffer_offset;
} }

View File

@ -96,6 +96,23 @@ SHIM_CALL XamUserGetName_shim(PPCContext* ppc_context,
} }
} }
typedef struct {
xe::be<uint32_t> setting_count;
xe::be<uint32_t> settings_ptr;
} X_USER_READ_PROFILE_SETTINGS;
static_assert_size(X_USER_READ_PROFILE_SETTINGS, 8);
typedef struct {
xe::be<uint32_t> from;
xe::be<uint32_t> unk04;
xe::be<uint32_t> user_index;
xe::be<uint32_t> unk0C;
xe::be<uint32_t> setting_id;
xe::be<uint32_t> unk14;
uint8_t setting_data[16];
} X_USER_READ_PROFILE_SETTING;
static_assert_size(X_USER_READ_PROFILE_SETTING, 40);
// http://freestyledash.googlecode.com/svn/trunk/Freestyle/Tools/Generic/xboxtools.cpp // http://freestyledash.googlecode.com/svn/trunk/Freestyle/Tools/Generic/xboxtools.cpp
SHIM_CALL XamUserReadProfileSettings_shim(PPCContext* ppc_context, SHIM_CALL XamUserReadProfileSettings_shim(PPCContext* ppc_context,
KernelState* kernel_state) { KernelState* kernel_state) {
@ -109,7 +126,7 @@ SHIM_CALL XamUserReadProfileSettings_shim(PPCContext* ppc_context,
uint32_t buffer_ptr = SHIM_GET_ARG_32(7); uint32_t buffer_ptr = SHIM_GET_ARG_32(7);
uint32_t overlapped_ptr = SHIM_GET_ARG_32(8); uint32_t overlapped_ptr = SHIM_GET_ARG_32(8);
uint32_t buffer_size = SHIM_MEM_32(buffer_size_ptr); uint32_t buffer_size = !buffer_size_ptr ? 0 : SHIM_MEM_32(buffer_size_ptr);
XELOGD( XELOGD(
"XamUserReadProfileSettings(%.8X, %d, %d, %d, %d, %.8X, %.8X(%d), %.8X, " "XamUserReadProfileSettings(%.8X, %d, %d, %d, %d, %.8X, %.8X(%d), %.8X, "
@ -117,13 +134,12 @@ SHIM_CALL XamUserReadProfileSettings_shim(PPCContext* ppc_context,
title_id, user_index, unk_0, unk_1, setting_count, setting_ids_ptr, title_id, user_index, unk_0, unk_1, setting_count, setting_ids_ptr,
buffer_size_ptr, buffer_size, buffer_ptr, overlapped_ptr); buffer_size_ptr, buffer_size, buffer_ptr, overlapped_ptr);
assert_zero(unk_0);
assert_zero(unk_1);
// Title ID = 0 means us. // Title ID = 0 means us.
// 0xfffe07d1 = profile? // 0xfffe07d1 = profile?
if (user_index == 255) {
user_index = 0;
}
if (user_index) { if (user_index) {
// Only support user 0. // Only support user 0.
if (overlapped_ptr) { if (overlapped_ptr) {
@ -135,30 +151,23 @@ SHIM_CALL XamUserReadProfileSettings_shim(PPCContext* ppc_context,
} }
return; return;
} }
const auto& user_profile = kernel_state->user_profile(); const auto& user_profile = kernel_state->user_profile();
// First call asks for size (fill buffer_size_ptr). // First call asks for size (fill buffer_size_ptr).
// Second call asks for buffer contents with that size. // Second call asks for buffer contents with that size.
// http://cs.rin.ru/forum/viewtopic.php?f=38&t=60668&hilit=gfwl+live&start=195 auto setting_ids = (xe::be<uint32_t>*)SHIM_MEM_ADDR(setting_ids_ptr);
// Result buffer is:
// uint32_t setting_count
// struct {
// uint32_t source;
// (4b pad)
// uint32_t user_index;
// (4b pad)
// uint32_t setting_id;
// (4b pad)
// <data>
// } settings[setting_count]
const size_t kSettingSize = 4 + 4 + 4 + 4 + 4 + 4 + 16;
// Compute required size. // Compute required base size.
uint32_t base_size_needed = sizeof(X_USER_READ_PROFILE_SETTINGS);
base_size_needed += setting_count * sizeof(X_USER_READ_PROFILE_SETTING);
// Compute required extra size.
uint32_t size_needed = base_size_needed;
bool any_missing = false; bool any_missing = false;
uint32_t size_needed = 4 + 4 + setting_count * kSettingSize;
for (uint32_t n = 0; n < setting_count; ++n) { for (uint32_t n = 0; n < setting_count; ++n) {
uint32_t setting_id = SHIM_MEM_32(setting_ids_ptr + n * 4); uint32_t setting_id = setting_ids[n];
auto setting = user_profile->GetSetting(setting_id); auto setting = user_profile->GetSetting(setting_id);
if (setting) { if (setting) {
auto extra_size = static_cast<uint32_t>(setting->extra_size()); auto extra_size = static_cast<uint32_t>(setting->extra_size());
@ -169,6 +178,7 @@ SHIM_CALL XamUserReadProfileSettings_shim(PPCContext* ppc_context,
setting_id); setting_id);
} }
} }
if (any_missing) { if (any_missing) {
// TODO(benvanik): don't fail? most games don't even check! // TODO(benvanik): don't fail? most games don't even check!
if (overlapped_ptr) { if (overlapped_ptr) {
@ -180,6 +190,7 @@ SHIM_CALL XamUserReadProfileSettings_shim(PPCContext* ppc_context,
} }
return; return;
} }
SHIM_SET_MEM_32(buffer_size_ptr, size_needed); SHIM_SET_MEM_32(buffer_size_ptr, size_needed);
if (!buffer_ptr || buffer_size < size_needed) { if (!buffer_ptr || buffer_size < size_needed) {
if (overlapped_ptr) { if (overlapped_ptr) {
@ -192,28 +203,29 @@ SHIM_CALL XamUserReadProfileSettings_shim(PPCContext* ppc_context,
return; return;
} }
SHIM_SET_MEM_32(buffer_ptr + 0, setting_count); auto out_header = (X_USER_READ_PROFILE_SETTINGS*)SHIM_MEM_ADDR(buffer_ptr);
SHIM_SET_MEM_32(buffer_ptr + 4, buffer_ptr + 8); out_header->setting_count = setting_count;
out_header->settings_ptr = buffer_ptr + 8;
size_t buffer_offset = 4 + setting_count * kSettingSize; auto out_setting = (X_USER_READ_PROFILE_SETTING*)SHIM_MEM_ADDR(out_header->settings_ptr);
size_t user_data_ptr = buffer_ptr + 8;
size_t buffer_offset = base_size_needed;
for (uint32_t n = 0; n < setting_count; ++n) { for (uint32_t n = 0; n < setting_count; ++n) {
uint32_t setting_id = SHIM_MEM_32(setting_ids_ptr + n * 4); uint32_t setting_id = setting_ids[n];
auto setting = user_profile->GetSetting(setting_id); auto setting = user_profile->GetSetting(setting_id);
std::memset(out_setting, 0, sizeof(X_USER_READ_PROFILE_SETTING));
out_setting->from = !setting ? 0 : setting->is_title_specific() ? 2 : 1;
out_setting->user_index = user_index;
out_setting->setting_id = setting_id;
if (setting) { if (setting) {
SHIM_SET_MEM_32(user_data_ptr + 0, setting->is_title_specific() ? 2 : 1); buffer_offset = setting->Append(&out_setting->setting_data[0],
} else { SHIM_MEM_ADDR(buffer_ptr), buffer_ptr, buffer_offset);
SHIM_SET_MEM_32(user_data_ptr + 0, 0); } /*else {
} std::memset(&out_setting->setting_data[0], 0, sizeof(out_setting->setting_data));
SHIM_SET_MEM_32(user_data_ptr + 8, user_index); }*/
SHIM_SET_MEM_32(user_data_ptr + 16, setting_id); ++out_setting;
if (setting) {
buffer_offset = setting->Append(SHIM_MEM_ADDR(user_data_ptr + 24),
SHIM_MEM_ADDR(buffer_ptr), buffer_offset);
} else {
std::memset(SHIM_MEM_ADDR(user_data_ptr + 24), 0, 16);
}
user_data_ptr += kSettingSize;
} }
if (overlapped_ptr) { if (overlapped_ptr) {