[XAM] Improvements to profile r/w setting exports

[XAM] Improvements to XamUserReadProfileSettingsEx/
XamUserWriteProfileSettings.

- Unify X_USER_READ_PROFILE_SETTING and X_USER_WRITE_PROFILE_SETTING
  into X_USER_PROFILE_SETTING.
- Clean up Setting serialization to use X_USER_PROFILE_SETTING_DATA
  instead of manual buffer copying.
- Fix XamUserReadProfileSettingsEx case where user_index is non-zero
  and xuids are being used.
- Skip unset settings in XamUserWriteProfileSettings_entry.
This commit is contained in:
Rick Gibbed 2022-01-23 12:55:11 -06:00
parent b3bb6687db
commit e49916ea0a
2 changed files with 188 additions and 194 deletions

View File

@ -2,7 +2,7 @@
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2020 Ben Vanik. All rights reserved. *
* Copyright 2022 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
@ -15,14 +15,69 @@
#include <unordered_map>
#include <vector>
#include "xenia/base/byte_stream.h"
#include "xenia/xbox.h"
namespace xe {
namespace kernel {
namespace xam {
struct X_USER_PROFILE_SETTING_DATA {
// UserProfile::Setting::Type. Appears to be 8-in-32 field, and the upper 24
// are not always zeroed by the game.
uint8_t type;
uint8_t unk_1[3];
xe::be<uint32_t> unk_4;
// TODO(sabretooth): not sure if this is a union, but it seems likely.
// Haven't run into cases other than "binary data" yet.
union {
xe::be<int32_t> s32;
xe::be<int64_t> s64;
xe::be<uint32_t> u32;
xe::be<double> f64;
struct {
xe::be<uint32_t> size;
xe::be<uint32_t> ptr;
} unicode;
xe::be<float> f32;
struct {
xe::be<uint32_t> size;
xe::be<uint32_t> ptr;
} binary;
xe::be<uint64_t> filetime;
};
};
static_assert_size(X_USER_PROFILE_SETTING_DATA, 16);
struct X_USER_PROFILE_SETTING {
xe::be<uint32_t> from;
xe::be<uint32_t> unk04;
union {
xe::be<uint32_t> user_index;
xe::be<uint64_t> xuid;
};
xe::be<uint32_t> setting_id;
xe::be<uint32_t> unk14;
union {
uint8_t data_bytes[sizeof(X_USER_PROFILE_SETTING_DATA)];
X_USER_PROFILE_SETTING_DATA data;
};
};
static_assert_size(X_USER_PROFILE_SETTING, 40);
class UserProfile {
public:
class SettingByteStream : public ByteStream {
public:
SettingByteStream(uint32_t ptr, uint8_t* data, size_t data_length,
size_t offset = 0)
: ByteStream(data, data_length, offset), ptr_(ptr) {}
uint32_t ptr() const { return static_cast<uint32_t>(ptr_ + offset()); }
private:
uint32_t ptr_;
};
struct Setting {
enum class Type {
CONTENT = 0,
@ -33,7 +88,7 @@ class UserProfile {
FLOAT = 5,
BINARY = 6,
DATETIME = 7,
INVALID = 0xFF,
UNSET = 0xFF,
};
union Key {
uint32_t value;
@ -55,97 +110,77 @@ class UserProfile {
size(size),
is_set(is_set),
loaded_title_id(0) {}
virtual size_t extra_size() const { return 0; }
virtual size_t Append(uint8_t* user_data, uint8_t* buffer,
uint32_t buffer_ptr, size_t buffer_offset) {
xe::store_and_swap<uint8_t>(user_data + kTypeOffset,
static_cast<uint8_t>(type));
return buffer_offset;
virtual void Append(X_USER_PROFILE_SETTING_DATA* data,
SettingByteStream* stream) {
data->type = static_cast<uint8_t>(type);
}
virtual std::vector<uint8_t> Serialize() const {
return std::vector<uint8_t>();
}
virtual void Deserialize(std::vector<uint8_t>) {}
bool is_title_specific() const { return (setting_id & 0x3F00) == 0x3F00; }
protected:
const size_t kTypeOffset = 0;
const size_t kValueOffset = 8;
const size_t kPointerOffset = 12;
};
struct Int32Setting : public Setting {
Int32Setting(uint32_t setting_id, int32_t value)
: Setting(setting_id, Type::INT32, 4, true), value(value) {}
int32_t value;
size_t Append(uint8_t* user_data, uint8_t* buffer, uint32_t buffer_ptr,
size_t buffer_offset) override {
buffer_offset =
Setting::Append(user_data, buffer, buffer_ptr, buffer_offset);
xe::store_and_swap<int32_t>(user_data + kValueOffset, value);
return buffer_offset;
void Append(X_USER_PROFILE_SETTING_DATA* data,
SettingByteStream* stream) override {
Setting::Append(data, stream);
data->s32 = value;
}
};
struct Int64Setting : public Setting {
Int64Setting(uint32_t setting_id, int64_t value)
: Setting(setting_id, Type::INT64, 8, true), value(value) {}
int64_t value;
size_t Append(uint8_t* user_data, uint8_t* buffer, uint32_t buffer_ptr,
size_t buffer_offset) override {
buffer_offset =
Setting::Append(user_data, buffer, buffer_ptr, buffer_offset);
xe::store_and_swap<int64_t>(user_data + kValueOffset, value);
return buffer_offset;
void Append(X_USER_PROFILE_SETTING_DATA* data,
SettingByteStream* stream) override {
Setting::Append(data, stream);
data->s64 = value;
}
};
struct DoubleSetting : public Setting {
DoubleSetting(uint32_t setting_id, double value)
: Setting(setting_id, Type::DOUBLE, 8, true), value(value) {}
double value;
size_t Append(uint8_t* user_data, uint8_t* buffer, uint32_t buffer_ptr,
size_t buffer_offset) override {
buffer_offset =
Setting::Append(user_data, buffer, buffer_ptr, buffer_offset);
xe::store_and_swap<double>(user_data + kValueOffset, value);
return buffer_offset;
void Append(X_USER_PROFILE_SETTING_DATA* data,
SettingByteStream* stream) override {
Setting::Append(data, stream);
data->f64 = value;
}
};
struct UnicodeSetting : public Setting {
UnicodeSetting(uint32_t setting_id, const std::u16string& value)
: Setting(setting_id, Type::WSTRING, 8, true), value(value) {}
std::u16string value;
size_t extra_size() const override {
return value.empty() ? 0 : 2 * (static_cast<int32_t>(value.size()) + 1);
}
size_t Append(uint8_t* user_data, uint8_t* buffer, uint32_t buffer_ptr,
size_t buffer_offset) override {
buffer_offset =
Setting::Append(user_data, buffer, buffer_ptr, buffer_offset);
int32_t length;
void Append(X_USER_PROFILE_SETTING_DATA* data,
SettingByteStream* stream) override {
Setting::Append(data, stream);
if (value.empty()) {
length = 0;
xe::store_and_swap<int32_t>(user_data + kValueOffset, 0);
xe::store_and_swap<uint32_t>(user_data + kPointerOffset, 0);
data->unicode.size = 0;
data->unicode.ptr = 0;
} else {
length = 2 * (static_cast<int32_t>(value.size()) + 1);
xe::store_and_swap<int32_t>(user_data + kValueOffset, length);
xe::store_and_swap<uint32_t>(
user_data + kPointerOffset,
buffer_ptr + static_cast<uint32_t>(buffer_offset));
memcpy(buffer + buffer_offset, value.data(), length);
size_t count = value.size() + 1;
size_t size = 2 * count;
assert_true(size <= std::numeric_limits<uint32_t>::max());
data->unicode.size = static_cast<uint32_t>(size);
data->unicode.ptr = stream->ptr();
auto buffer =
reinterpret_cast<uint16_t*>(&stream->data()[stream->offset()]);
stream->Advance(size);
xe::copy_and_swap(buffer, (uint16_t*)value.data(), count);
}
return buffer_offset + length;
}
};
struct FloatSetting : public Setting {
FloatSetting(uint32_t setting_id, float value)
: Setting(setting_id, Type::FLOAT, 4, true), value(value) {}
float value;
size_t Append(uint8_t* user_data, uint8_t* buffer, uint32_t buffer_ptr,
size_t buffer_offset) override {
buffer_offset =
Setting::Append(user_data, buffer, buffer_ptr, buffer_offset);
xe::store_and_swap<float>(user_data + kValueOffset, value);
return buffer_offset;
void Append(X_USER_PROFILE_SETTING_DATA* data,
SettingByteStream* stream) override {
Setting::Append(data, stream);
data->f32 = value;
}
};
struct BinarySetting : public Setting {
@ -154,27 +189,19 @@ class UserProfile {
BinarySetting(uint32_t setting_id, const std::vector<uint8_t>& value)
: Setting(setting_id, Type::BINARY, 8, true), value(value) {}
std::vector<uint8_t> value;
size_t extra_size() const override {
return static_cast<int32_t>(value.size());
}
size_t Append(uint8_t* user_data, uint8_t* buffer, uint32_t buffer_ptr,
size_t buffer_offset) override {
buffer_offset =
Setting::Append(user_data, buffer, buffer_ptr, buffer_offset);
int32_t length;
void Append(X_USER_PROFILE_SETTING_DATA* data,
SettingByteStream* stream) override {
Setting::Append(data, stream);
if (value.empty()) {
length = 0;
xe::store_and_swap<int32_t>(user_data + kValueOffset, 0);
xe::store_and_swap<int32_t>(user_data + kPointerOffset, 0);
data->binary.size = 0;
data->binary.ptr = 0;
} else {
length = static_cast<int32_t>(value.size());
xe::store_and_swap<int32_t>(user_data + kValueOffset, length);
xe::store_and_swap<uint32_t>(
user_data + kPointerOffset,
buffer_ptr + static_cast<uint32_t>(buffer_offset));
memcpy(buffer + buffer_offset, value.data(), length);
size_t size = value.size();
assert_true(size <= std::numeric_limits<uint32_t>::max());
data->binary.size = static_cast<uint32_t>(size);
data->binary.ptr = stream->ptr();
stream->Write(value.data(), size);
}
return buffer_offset + length;
}
std::vector<uint8_t> Serialize() const override {
return std::vector<uint8_t>(value.data(), value.data() + value.size());
@ -188,12 +215,10 @@ class UserProfile {
DateTimeSetting(uint32_t setting_id, int64_t value)
: Setting(setting_id, Type::DATETIME, 8, true), value(value) {}
int64_t value;
size_t Append(uint8_t* user_data, uint8_t* buffer, uint32_t buffer_ptr,
size_t buffer_offset) override {
buffer_offset =
Setting::Append(user_data, buffer, buffer_ptr, buffer_offset);
xe::store_and_swap<int64_t>(user_data + kValueOffset, value);
return buffer_offset;
void Append(X_USER_PROFILE_SETTING_DATA* data,
SettingByteStream* stream) override {
Setting::Append(data, stream);
data->filetime = value;
}
};

View File

@ -14,6 +14,7 @@
#include "xenia/base/string_util.h"
#include "xenia/kernel/kernel_state.h"
#include "xenia/kernel/util/shim_utils.h"
#include "xenia/kernel/xam/user_profile.h"
#include "xenia/kernel/xam/xam_private.h"
#include "xenia/kernel/xenumerator.h"
#include "xenia/kernel/xthread.h"
@ -146,25 +147,14 @@ typedef struct {
} 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);
// https://github.com/oukiar/freestyledash/blob/master/Freestyle/Tools/Generic/xboxtools.cpp
uint32_t xeXamUserReadProfileSettingsEx(uint32_t title_id, uint32_t user_index,
uint32_t xuid_count, lpqword_t xuids,
uint32_t setting_count,
lpdword_t setting_ids, uint32_t unk,
lpdword_t buffer_size_ptr,
lpvoid_t buffer_ptr,
uint32_t overlapped_ptr) {
uint32_t XamUserReadProfileSettingsEx(uint32_t title_id, uint32_t user_index,
uint32_t xuid_count, be<uint64_t>* xuids,
uint32_t setting_count,
be<uint32_t>* setting_ids, uint32_t unk,
be<uint32_t>* buffer_size_ptr,
uint8_t* buffer,
XAM_OVERLAPPED* overlapped) {
if (!xuid_count) {
assert_null(xuids);
} else {
@ -173,8 +163,11 @@ uint32_t xeXamUserReadProfileSettingsEx(uint32_t title_id, uint32_t user_index,
// TODO(gibbed): allow proper lookup of arbitrary XUIDs
const auto& user_profile = kernel_state()->user_profile();
assert_true(static_cast<uint64_t>(xuids[0]) == user_profile->xuid());
// TODO(gibbed): we assert here, but in case a title passes xuid_count > 1
// until it's implemented for release builds...
xuid_count = 1;
}
assert_zero(unk);
assert_zero(unk); // probably flags
// must have at least 1 to 32 settings
if (setting_count < 1 || setting_count > 32) {
@ -188,35 +181,33 @@ uint32_t xeXamUserReadProfileSettingsEx(uint32_t title_id, uint32_t user_index,
// if buffer size is non-zero, buffer pointer must be valid
auto buffer_size = static_cast<uint32_t>(*buffer_size_ptr);
if (buffer_size && !buffer_ptr) {
if (buffer_size && !buffer) {
return X_ERROR_INVALID_PARAMETER;
}
uint32_t needed_header_size = 0;
uint32_t needed_extra_size = 0;
uint32_t needed_data_size = 0;
for (uint32_t i = 0; i < setting_count; ++i) {
needed_header_size += sizeof(X_USER_READ_PROFILE_SETTING);
needed_header_size += sizeof(X_USER_PROFILE_SETTING);
UserProfile::Setting::Key setting_key;
setting_key.value = static_cast<uint32_t>(setting_ids[i]);
switch (static_cast<UserProfile::Setting::Type>(setting_key.type)) {
case UserProfile::Setting::Type::WSTRING:
case UserProfile::Setting::Type::BINARY: {
needed_extra_size += setting_key.size;
case UserProfile::Setting::Type::BINARY:
needed_data_size += setting_key.size;
break;
}
default: {
default:
break;
}
}
}
if (xuids) {
// needed_header_size *= xuid_count;
// needed_extra_size *= !xuid_count;
needed_header_size *= xuid_count;
needed_data_size *= xuid_count;
}
needed_header_size += sizeof(X_USER_READ_PROFILE_SETTINGS);
uint32_t needed_size = needed_header_size + needed_extra_size;
if (!buffer_ptr || buffer_size < needed_size) {
uint32_t needed_size = needed_header_size + needed_data_size;
if (!buffer || buffer_size < needed_size) {
if (!buffer_size) {
*buffer_size_ptr = needed_size;
}
@ -226,11 +217,12 @@ uint32_t xeXamUserReadProfileSettingsEx(uint32_t title_id, uint32_t user_index,
// Title ID = 0 means us.
// 0xfffe07d1 = profile?
if (user_index) {
if (!xuids && user_index) {
// Only support user 0.
if (overlapped_ptr) {
kernel_state()->CompleteOverlappedImmediate(overlapped_ptr,
X_ERROR_NO_SUCH_USER);
if (overlapped) {
kernel_state()->CompleteOverlappedImmediate(
kernel_state()->memory()->HostToGuestVirtual(overlapped),
X_ERROR_NO_SUCH_USER);
return X_ERROR_IO_PENDING;
}
return X_ERROR_NO_SUCH_USER;
@ -257,49 +249,49 @@ uint32_t xeXamUserReadProfileSettingsEx(uint32_t title_id, uint32_t user_index,
}
if (any_missing) {
// TODO(benvanik): don't fail? most games don't even check!
if (overlapped_ptr) {
kernel_state()->CompleteOverlappedImmediate(overlapped_ptr,
X_ERROR_INVALID_PARAMETER);
if (overlapped) {
kernel_state()->CompleteOverlappedImmediate(
kernel_state()->memory()->HostToGuestVirtual(overlapped),
X_ERROR_INVALID_PARAMETER);
return X_ERROR_IO_PENDING;
}
return X_ERROR_INVALID_PARAMETER;
}
auto out_header = buffer_ptr.as<X_USER_READ_PROFILE_SETTINGS*>();
auto out_header = reinterpret_cast<X_USER_READ_PROFILE_SETTINGS*>(buffer);
auto out_setting = reinterpret_cast<X_USER_PROFILE_SETTING*>(&out_header[1]);
out_header->setting_count = static_cast<uint32_t>(setting_count);
out_header->settings_ptr = buffer_ptr.guest_address() + 8;
out_header->settings_ptr =
kernel_state()->memory()->HostToGuestVirtual(out_setting);
auto out_setting =
reinterpret_cast<X_USER_READ_PROFILE_SETTING*>(&out_header[1]);
size_t buffer_offset = needed_header_size;
UserProfile::SettingByteStream out_stream(
kernel_state()->memory()->HostToGuestVirtual(buffer), buffer, buffer_size,
needed_header_size);
for (uint32_t n = 0; n < setting_count; ++n) {
uint32_t setting_id = setting_ids[n];
auto setting = user_profile->GetSetting(setting_id);
std::memset(out_setting, 0, sizeof(X_USER_READ_PROFILE_SETTING));
std::memset(out_setting, 0, sizeof(X_USER_PROFILE_SETTING));
out_setting->from = !setting || !setting->is_set ? 0
: setting->is_title_specific() ? 2
: 1;
out_setting->user_index = static_cast<uint32_t>(user_index);
if (xuids) {
out_setting->xuid = user_profile->xuid();
} else {
out_setting->user_index = static_cast<uint32_t>(user_index);
}
out_setting->setting_id = setting_id;
if (setting && setting->is_set) {
buffer_offset =
setting->Append(&out_setting->setting_data[0], buffer_ptr,
buffer_ptr.guest_address(), buffer_offset);
setting->Append(&out_setting->data, &out_stream);
}
// TODO(benvanik): why did I do this?
/*else {
std::memset(&out_setting->setting_data[0], 0,
sizeof(out_setting->setting_data));
}*/
++out_setting;
}
if (overlapped_ptr) {
kernel_state()->CompleteOverlappedImmediate(overlapped_ptr,
X_ERROR_SUCCESS);
if (overlapped) {
kernel_state()->CompleteOverlappedImmediate(
kernel_state()->memory()->HostToGuestVirtual(overlapped),
X_ERROR_SUCCESS);
return X_ERROR_IO_PENDING;
}
return X_ERROR_SUCCESS;
@ -308,57 +300,35 @@ uint32_t xeXamUserReadProfileSettingsEx(uint32_t title_id, uint32_t user_index,
dword_result_t XamUserReadProfileSettings_entry(
dword_t title_id, dword_t user_index, dword_t xuid_count, lpqword_t xuids,
dword_t setting_count, lpdword_t setting_ids, lpdword_t buffer_size_ptr,
lpvoid_t buffer_ptr, dword_t overlapped_ptr) {
return xeXamUserReadProfileSettingsEx(
title_id, user_index, xuid_count, xuids, setting_count, setting_ids, 0,
buffer_size_ptr, buffer_ptr, overlapped_ptr);
lpvoid_t buffer_ptr, pointer_t<XAM_OVERLAPPED> overlapped) {
return XamUserReadProfileSettingsEx(title_id, user_index, xuid_count, xuids,
setting_count, setting_ids, 0,
buffer_size_ptr, buffer_ptr, overlapped);
}
DECLARE_XAM_EXPORT1(XamUserReadProfileSettings, kUserProfiles, kImplemented);
dword_result_t XamUserReadProfileSettingsEx_entry(
dword_t title_id, dword_t user_index, dword_t xuid_count, lpqword_t xuids,
dword_t setting_count, lpdword_t setting_ids, lpdword_t buffer_size_ptr,
dword_t unk_2, lpvoid_t buffer_ptr, dword_t overlapped_ptr) {
return xeXamUserReadProfileSettingsEx(
title_id, user_index, xuid_count, xuids, setting_count, setting_ids,
unk_2, buffer_size_ptr, buffer_ptr, overlapped_ptr);
dword_t unk_2, lpvoid_t buffer_ptr, pointer_t<XAM_OVERLAPPED> overlapped) {
return XamUserReadProfileSettingsEx(title_id, user_index, xuid_count, xuids,
setting_count, setting_ids, unk_2,
buffer_size_ptr, buffer_ptr, overlapped);
}
DECLARE_XAM_EXPORT1(XamUserReadProfileSettingsEx, kUserProfiles, kImplemented);
typedef struct {
xe::be<uint32_t> from;
xe::be<uint32_t> unk_04;
xe::be<uint32_t> unk_08;
xe::be<uint32_t> unk_0c;
xe::be<uint32_t> setting_id;
xe::be<uint32_t> unk_14;
// UserProfile::Setting::Type. Appears to be 8-in-32 field, and the upper 24
// are not always zeroed by the game.
uint8_t type;
xe::be<uint32_t> unk_1c;
// TODO(sabretooth): not sure if this is a union, but it seems likely.
// Haven't run into cases other than "binary data" yet.
union {
struct {
xe::be<uint32_t> length;
xe::be<uint32_t> ptr;
} binary;
};
} X_USER_WRITE_PROFILE_SETTING;
dword_result_t XamUserWriteProfileSettings_entry(
dword_t title_id, dword_t user_index, dword_t setting_count,
pointer_t<X_USER_WRITE_PROFILE_SETTING> settings, dword_t overlapped_ptr) {
pointer_t<X_USER_PROFILE_SETTING> settings,
pointer_t<XAM_OVERLAPPED> overlapped) {
if (!setting_count || !settings) {
return X_ERROR_INVALID_PARAMETER;
}
if (user_index) {
// Only support user 0.
if (overlapped_ptr) {
kernel_state()->CompleteOverlappedImmediate(overlapped_ptr,
if (overlapped) {
kernel_state()->CompleteOverlappedImmediate(overlapped,
X_ERROR_NO_SUCH_USER);
return X_ERROR_IO_PENDING;
}
@ -369,39 +339,39 @@ dword_result_t XamUserWriteProfileSettings_entry(
const auto& user_profile = kernel_state()->user_profile();
for (uint32_t n = 0; n < setting_count; ++n) {
const X_USER_WRITE_PROFILE_SETTING& settings_data = settings[n];
const X_USER_PROFILE_SETTING& setting = settings[n];
auto setting_type =
static_cast<UserProfile::Setting::Type>(setting.data.type);
if (setting_type == UserProfile::Setting::Type::UNSET) {
continue;
}
XELOGD(
"XamUserWriteProfileSettings: setting index [{}]:"
" from={} setting_id={:08X} data.type={}",
n, (uint32_t)settings_data.from, (uint32_t)settings_data.setting_id,
settings_data.type);
n, (uint32_t)setting.from, (uint32_t)setting.setting_id,
setting.data.type);
xam::UserProfile::Setting::Type settingType =
static_cast<xam::UserProfile::Setting::Type>(settings_data.type);
switch (settingType) {
switch (setting_type) {
case UserProfile::Setting::Type::CONTENT:
case UserProfile::Setting::Type::BINARY: {
uint8_t* settings_data_ptr = kernel_state()->memory()->TranslateVirtual(
settings_data.binary.ptr);
size_t settings_data_len = settings_data.binary.length;
std::vector<uint8_t> data_vec;
if (settings_data.binary.ptr) {
uint8_t* binary_ptr =
kernel_state()->memory()->TranslateVirtual(setting.data.binary.ptr);
size_t binary_size = setting.data.binary.size;
std::vector<uint8_t> bytes;
if (setting.data.binary.ptr) {
// Copy provided data
data_vec.resize(settings_data_len);
std::memcpy(data_vec.data(), settings_data_ptr, settings_data_len);
bytes.resize(binary_size);
std::memcpy(bytes.data(), binary_ptr, binary_size);
} else {
// Data pointer was NULL, so just fill with zeroes
data_vec.resize(settings_data_len, 0);
bytes.resize(binary_size, 0);
}
user_profile->AddSetting(
std::make_unique<xam::UserProfile::BinarySetting>(
settings_data.setting_id, data_vec));
setting.setting_id, bytes));
} break;
case UserProfile::Setting::Type::WSTRING:
case UserProfile::Setting::Type::DOUBLE:
case UserProfile::Setting::Type::FLOAT:
@ -410,14 +380,13 @@ dword_result_t XamUserWriteProfileSettings_entry(
case UserProfile::Setting::Type::DATETIME:
default: {
XELOGE("XamUserWriteProfileSettings: Unimplemented data type {}",
settingType);
setting_type);
} break;
};
}
if (overlapped_ptr) {
kernel_state()->CompleteOverlappedImmediate(overlapped_ptr,
X_ERROR_SUCCESS);
if (overlapped) {
kernel_state()->CompleteOverlappedImmediate(overlapped, X_ERROR_SUCCESS);
return X_ERROR_IO_PENDING;
}
return X_ERROR_SUCCESS;