diff --git a/src/xenia/kernel/kernel_state.cc b/src/xenia/kernel/kernel_state.cc index f2d3b676d..b806253b3 100644 --- a/src/xenia/kernel/kernel_state.cc +++ b/src/xenia/kernel/kernel_state.cc @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,7 @@ KernelState::KernelState(Emulator* emulator) : dispatcher_ = new Dispatcher(this); app_manager_ = std::make_unique(); + user_profile_ = std::make_unique(); object_table_ = new ObjectTable(); object_mutex_ = xe_mutex_alloc(10000); @@ -171,3 +173,36 @@ void KernelState::BroadcastNotification(XNotificationID id, uint32_t data) { } xe_mutex_unlock(object_mutex_); } + +void KernelState::CompleteOverlapped(uint32_t overlapped_ptr, X_RESULT result, uint32_t length) { + auto ptr = memory()->membase() + overlapped_ptr; + XOverlappedSetResult(ptr, result); + XOverlappedSetLength(ptr, length); + XOverlappedSetExtendedError(ptr, result); + X_HANDLE event_handle = XOverlappedGetEvent(ptr); + if (event_handle) { + XEvent* ev = nullptr; + if (XSUCCEEDED(object_table()->GetObject( + event_handle, reinterpret_cast(&ev)))) { + ev->Set(0, false); + ev->Release(); + } + } + if (XOverlappedGetCompletionRoutine(ptr)) { + assert_always(); + X_HANDLE thread_handle = XOverlappedGetContext(ptr); + XThread* thread = nullptr; + if (XSUCCEEDED(object_table()->GetObject( + thread_handle, reinterpret_cast(&thread)))) { + // TODO(benvanik): queue APC on the thread that requested the overlapped operation. + thread->Release(); + } + } +} + +void KernelState::CompleteOverlappedImmediate(uint32_t overlapped_ptr, X_RESULT result, uint32_t length) { + auto ptr = memory()->membase() + overlapped_ptr; + XOverlappedSetContext(ptr, + XThread::GetCurrentThreadHandle()); + CompleteOverlapped(overlapped_ptr, result, length); +} diff --git a/src/xenia/kernel/kernel_state.h b/src/xenia/kernel/kernel_state.h index 4a7884724..77173a8f2 100644 --- a/src/xenia/kernel/kernel_state.h +++ b/src/xenia/kernel/kernel_state.h @@ -19,6 +19,7 @@ #include #include #include +#include #include @@ -51,6 +52,7 @@ public: Dispatcher* dispatcher() const { return dispatcher_; } XAppManager* app_manager() const { return app_manager_.get(); } + UserProfile* user_profile() const { return user_profile_.get(); } ObjectTable* object_table() const { return object_table_; } @@ -66,6 +68,9 @@ public: void UnregisterNotifyListener(XNotifyListener* listener); void BroadcastNotification(XNotificationID id, uint32_t data); + void CompleteOverlapped(uint32_t overlapped_ptr, X_RESULT result, uint32_t length = 0); + void CompleteOverlappedImmediate(uint32_t overlapped_ptr, X_RESULT result, uint32_t length = 0); + private: Emulator* emulator_; Memory* memory_; @@ -75,6 +80,7 @@ private: Dispatcher* dispatcher_; std::unique_ptr app_manager_; + std::unique_ptr user_profile_; ObjectTable* object_table_; xe_mutex_t* object_mutex_; diff --git a/src/xenia/kernel/objects/xnotify_listener.cc b/src/xenia/kernel/objects/xnotify_listener.cc index 2f874c988..e205f6b6d 100644 --- a/src/xenia/kernel/objects/xnotify_listener.cc +++ b/src/xenia/kernel/objects/xnotify_listener.cc @@ -44,13 +44,13 @@ void XNotifyListener::EnqueueNotification(XNotificationID id, uint32_t data) { } xe_mutex_lock(lock_); - auto existing = notifications_.find(id); - if (existing != notifications_.end()) { + if (notifications_.count(id)) { // Already exists. Overwrite. notifications_[id] = data; } else { // New. notification_count_++; + notifications_.insert({ id, data }); } SetEvent(wait_handle_); xe_mutex_unlock(lock_); diff --git a/src/xenia/kernel/sources.gypi b/src/xenia/kernel/sources.gypi index 2c393a0ab..e96dbee36 100644 --- a/src/xenia/kernel/sources.gypi +++ b/src/xenia/kernel/sources.gypi @@ -15,6 +15,8 @@ 'native_list.h', 'object_table.cc', 'object_table.h', + 'user_profile.cc', + 'user_profile.h', 'xam_content.cc', 'xam_content.h', 'xam_info.cc', diff --git a/src/xenia/kernel/user_profile.cc b/src/xenia/kernel/user_profile.cc new file mode 100644 index 000000000..a401b8185 --- /dev/null +++ b/src/xenia/kernel/user_profile.cc @@ -0,0 +1,98 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2014 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include + + +namespace xe { +namespace kernel { + + +UserProfile::UserProfile() { + xuid_ = 0xBABEBABEBABEBABE; + name_ = "User"; + + // http://cs.rin.ru/forum/viewtopic.php?f=38&t=60668&hilit=gfwl+live&start=195 + // XPROFILE_GAMER_YAXIS_INVERSION + AddSetting(std::make_unique(0x10040002, 0)); + // XPROFILE_OPTION_CONTROLLER_VIBRATION + AddSetting(std::make_unique(0x10040003, 3)); + // XPROFILE_GAMERCARD_ZONE + AddSetting(std::make_unique(0x10040004, 0)); + // XPROFILE_GAMERCARD_REGION + AddSetting(std::make_unique(0x10040005, 0)); + // XPROFILE_GAMERCARD_CRED + AddSetting(std::make_unique(0x10040006, 0xFA)); + // XPROFILE_GAMERCARD_REP + AddSetting(std::make_unique(0x5004000B, 0.0f)); + // XPROFILE_OPTION_VOICE_MUTED + AddSetting(std::make_unique(0x1004000C, 0)); + // XPROFILE_OPTION_VOICE_THRU_SPEAKERS + AddSetting(std::make_unique(0x1004000D, 0)); + // XPROFILE_OPTION_VOICE_VOLUME + AddSetting(std::make_unique(0x1004000E, 0x64)); + // XPROFILE_GAMERCARD_MOTTO + AddSetting(std::make_unique(0x402C0011, L"")); + // XPROFILE_GAMERCARD_TITLES_PLAYED + AddSetting(std::make_unique(0x10040012, 1)); + // XPROFILE_GAMERCARD_ACHIEVEMENTS_EARNED + AddSetting(std::make_unique(0x10040013, 0)); + // XPROFILE_GAMER_DIFFICULTY + AddSetting(std::make_unique(0x10040015, 0)); + // XPROFILE_GAMER_ACTION_AUTO_AIM + AddSetting(std::make_unique(0x10040022, 1)); + // XPROFILE_GAMER_ACTION_AUTO_CENTER + AddSetting(std::make_unique(0x10040023, 0)); + // XPROFILE_GAMER_ACTION_MOVEMENT_CONTROL + AddSetting(std::make_unique(0x10040024, 0)); + // XPROFILE_GAMER_RACE_TRANSMISSION + AddSetting(std::make_unique(0x10040026, 0)); + // XPROFILE_GAMER_RACE_CAMERA_LOCATION + AddSetting(std::make_unique(0x10040027, 0)); + // XPROFILE_GAMER_RACE_BRAKE_CONTROL + AddSetting(std::make_unique(0x10040028, 0)); + // XPROFILE_GAMER_RACE_ACCELERATOR_CONTROL + AddSetting(std::make_unique(0x10040029, 0)); + // XPROFILE_GAMERCARD_TITLE_CRED_EARNED + AddSetting(std::make_unique(0x10040038, 0)); + // XPROFILE_GAMERCARD_TITLE_ACHIEVEMENTS_EARNED + AddSetting(std::make_unique(0x10040039, 0)); + + // If we set this, games will try to get it. + // XPROFILE_GAMERCARD_PICTURE_KEY + // AddSetting(WStringSetting(0x4064000F, L"")); + + std::vector zeros; + zeros.resize(1000); + // XPROFILE_TITLE_SPECIFIC1 + AddSetting(std::make_unique(0x63E83FFF, zeros)); + // XPROFILE_TITLE_SPECIFIC2 + AddSetting(std::make_unique(0x63E83FFE, zeros)); + // XPROFILE_TITLE_SPECIFIC3 + AddSetting(std::make_unique(0x63E83FFD, zeros)); +} + + +void UserProfile::AddSetting(std::unique_ptr setting) { + settings_.insert({ setting->setting_id, setting.get() }); + setting_list_.push_back(std::move(setting)); +} + + +UserProfile::Setting* UserProfile::GetSetting(uint32_t setting_id) { + const auto& it = settings_.find(setting_id); + if (it == settings_.end()) { + return nullptr; + } + return it->second; +} + + +} // namespace kernel +} // namespace xe diff --git a/src/xenia/kernel/user_profile.h b/src/xenia/kernel/user_profile.h new file mode 100644 index 000000000..183bfd84d --- /dev/null +++ b/src/xenia/kernel/user_profile.h @@ -0,0 +1,189 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2014 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_KERNEL_XBOXKRNL_USER_PROFILE_H_ +#define XENIA_KERNEL_XBOXKRNL_USER_PROFILE_H_ + +#include +#include +#include +#include + +#include +#include + +#include + + +namespace xe { +namespace kernel { + + +class UserProfile { +public: + struct Setting { + enum class Type { + UNKNOWN = 0, + INT32 = 1, + INT64 = 2, + DOUBLE = 3, + WSTRING = 4, + FLOAT = 5, + BINARY = 6, + DATETIME = 7, + INVALID = 0xFF, + }; + union Key { + struct { + uint32_t id : 14; + uint32_t unk : 2; + uint32_t size : 12; + uint32_t type : 4; + }; + uint32_t value; + }; + uint32_t setting_id; + Type type; + size_t size; + Setting(uint32_t setting_id, Type type, size_t size) + : setting_id(setting_id), type(type), size(size) {} + virtual size_t extra_size() const { return 0; } + virtual size_t Append(uint8_t* user_data, uint8_t* buffer, size_t buffer_offset) { + poly::store_and_swap(user_data + kTypeOffset, static_cast(type)); + return buffer_offset; + } + 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), value(value) {} + int32_t value; + size_t Append(uint8_t* user_data, uint8_t* buffer, size_t buffer_offset) override { + buffer_offset = Setting::Append(user_data, buffer, buffer_offset); + poly::store_and_swap(user_data + kValueOffset, value); + return buffer_offset; + } + }; + struct Int64Setting : public Setting { + Int64Setting(uint32_t setting_id, int64_t value) + : Setting(setting_id, Type::INT64, 8), value(value) {} + int64_t value; + size_t Append(uint8_t* user_data, uint8_t* buffer, size_t buffer_offset) override { + buffer_offset = Setting::Append(user_data, buffer, buffer_offset); + poly::store_and_swap(user_data + kValueOffset, value); + return buffer_offset; + } + }; + struct DoubleSetting : public Setting { + DoubleSetting(uint32_t setting_id, double value) + : Setting(setting_id, Type::DOUBLE, 8), value(value) {} + double value; + size_t Append(uint8_t* user_data, uint8_t* buffer, size_t buffer_offset) override { + buffer_offset = Setting::Append(user_data, buffer, buffer_offset); + poly::store_and_swap(user_data + kValueOffset, value); + return buffer_offset; + } + }; + struct UnicodeSetting : public Setting { + UnicodeSetting(uint32_t setting_id, const std::wstring& value) + : Setting(setting_id, Type::WSTRING, 8), value(value) {} + std::wstring value; + size_t extra_size() const override { + return value.empty() ? 0 : 2 * (static_cast(value.size()) + 1); + } + size_t Append(uint8_t* user_data, uint8_t* buffer, size_t buffer_offset) override { + buffer_offset = Setting::Append(user_data, buffer, buffer_offset); + int32_t length; + if (value.empty()) { + length = 0; + poly::store_and_swap(user_data + kValueOffset, 0); + poly::store_and_swap(user_data + kPointerOffset, 0); + } else { + length = 2 * (static_cast(value.size()) + 1); + poly::store_and_swap(user_data + kValueOffset, length); + poly::store_and_swap(user_data + kPointerOffset, + static_cast(buffer_offset)); + memcpy(buffer + buffer_offset, value.data(), length); + } + return buffer_offset + length; + } + }; + struct FloatSetting : public Setting { + FloatSetting(uint32_t setting_id, float value) + : Setting(setting_id, Type::FLOAT, 4), value(value) {} + float value; + size_t Append(uint8_t* user_data, uint8_t* buffer, size_t buffer_offset) override { + buffer_offset = Setting::Append(user_data, buffer, buffer_offset); + poly::store_and_swap(user_data + kValueOffset, value); + return buffer_offset; + } + }; + struct BinarySetting : public Setting { + BinarySetting(uint32_t setting_id, const std::vector& value) + : Setting(setting_id, Type::BINARY, 8), value(value) {} + std::vector value; + size_t extra_size() const override { + return static_cast(value.size()); + } + size_t Append(uint8_t* user_data, uint8_t* buffer, size_t buffer_offset) override { + buffer_offset = Setting::Append(user_data, buffer, buffer_offset); + int32_t length; + if (value.empty()) { + length = 0; + poly::store_and_swap(user_data + kValueOffset, 0); + poly::store_and_swap(user_data + kPointerOffset, 0); + } else { + length = static_cast(value.size()); + poly::store_and_swap(user_data + kValueOffset, length); + poly::store_and_swap(user_data + kPointerOffset, + static_cast(buffer_offset)); + memcpy(buffer + buffer_offset, value.data(), length); + } + return buffer_offset + length; + } + }; + struct DateTimeSetting : public Setting { + DateTimeSetting(uint32_t setting_id, int64_t value) + : Setting(setting_id, Type::DATETIME, 8), value(value) {} + int64_t value; + size_t Append(uint8_t* user_data, uint8_t* buffer, size_t buffer_offset) override { + buffer_offset = Setting::Append(user_data, buffer, buffer_offset); + poly::store_and_swap(user_data + kValueOffset, value); + return buffer_offset; + } + }; + + UserProfile(); + + uint64_t xuid() const { return xuid_; } + std::string name() const { return name_; } + uint32_t signin_state() const { return 1; } + + void AddSetting(std::unique_ptr setting); + Setting* GetSetting(uint32_t setting_id); + +private: + uint64_t xuid_; + std::string name_; + std::vector> setting_list_; + std::unordered_map settings_; +}; + + +} // namespace kernel +} // namespace xe + + +#endif // XENIA_KERNEL_XBOXKRNL_USER_PROFILE_H_ diff --git a/src/xenia/kernel/xam_user.cc b/src/xenia/kernel/xam_user.cc index 560c41623..bf22088b8 100644 --- a/src/xenia/kernel/xam_user.cc +++ b/src/xenia/kernel/xam_user.cc @@ -12,6 +12,7 @@ #include #include #include +#include #include @@ -37,8 +38,9 @@ SHIM_CALL XamUserGetXUID_shim( xuid_ptr); if (user_index == 0) { + const auto& user_profile = state->user_profile(); if (xuid_ptr) { - SHIM_SET_MEM_32(xuid_ptr, 0xBABEBABE); + SHIM_SET_MEM_64(xuid_ptr, user_profile->xuid()); } SHIM_SET_RETURN_32(0); } else { @@ -59,7 +61,8 @@ SHIM_CALL XamUserGetSigninState_shim( // This should keep games from asking us to sign in and also keep them // from initializing the network. if (user_index == 0) { - SHIM_SET_RETURN_64(1); + const auto& user_profile = state->user_profile(); + SHIM_SET_RETURN_64(user_profile->signin_state()); } else { SHIM_SET_RETURN_64(0); } @@ -77,13 +80,14 @@ SHIM_CALL XamUserGetSigninInfo_shim( user_index, flags, info_ptr); if (user_index == 0) { - SHIM_SET_MEM_32(info_ptr + 0, 0xBABEBABE); // XUID - SHIM_SET_MEM_32(info_ptr + 4, 1); // maybe zero? - SHIM_SET_MEM_32(info_ptr + 8, 1); // signin state, same as above - SHIM_SET_MEM_32(info_ptr + 12, 0); // ? + const auto& user_profile = state->user_profile(); + SHIM_SET_MEM_64(info_ptr + 0, user_profile->xuid()); + SHIM_SET_MEM_32(info_ptr + 8, 0); // maybe zero? + SHIM_SET_MEM_32(info_ptr + 12, user_profile->signin_state()); SHIM_SET_MEM_32(info_ptr + 16, 0); // ? - char* buffer = (char*)SHIM_MEM_ADDR(info_ptr + 20); - xestrncpya(buffer, 0x10, "User0", 5); + SHIM_SET_MEM_32(info_ptr + 20, 0); // ? + char* buffer = (char*)SHIM_MEM_ADDR(info_ptr + 24); + strcpy(buffer, user_profile->name().data()); SHIM_SET_RETURN_32(0); } else { SHIM_SET_RETURN_32(X_ERROR_NO_SUCH_USER); @@ -102,8 +106,9 @@ SHIM_CALL XamUserGetName_shim( user_index, buffer_ptr, buffer_len); if (user_index == 0) { + const auto& user_profile = state->user_profile(); char* buffer = (char*)SHIM_MEM_ADDR(buffer_ptr); - xestrncpya(buffer, buffer_len, "User0", 5); + strcpy(buffer, user_profile->name().data()); SHIM_SET_RETURN_32(0); } else { SHIM_SET_RETURN_32(X_ERROR_NO_SUCH_USER); @@ -136,42 +141,80 @@ SHIM_CALL XamUserReadProfileSettings_shim( // Title ID = 0 means us. // 0xfffe07d1 = profile? - // TODO(benvanik): implement overlapped support - assert_zero(overlapped_ptr); + if (user_index) { + // Only support user 0. + SHIM_SET_RETURN_32(X_ERROR_NOT_FOUND); + return; + } + const auto& user_profile = state->user_profile(); // First call asks for size (fill buffer_size_ptr). // 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 // Result buffer is: // uint32_t setting_count - // [repeated X_USER_PROFILE_SETTING structs] + // struct { + // uint32_t source; + // (4b pad) + // uint32_t user_index; + // (4b pad) + // uint32_t setting_id; + // (4b pad) + // + // } settings[setting_count] + const size_t kSettingSize = 4 + 4 + 4 + 4 + 4 + 4 + 16; - typedef union { - struct { - uint32_t id : 14; - uint32_t unk : 2; - uint32_t size : 12; - uint32_t type : 4; - }; - uint32_t value; - } x_profile_setting_t; - - // Compute sizes. - uint32_t size_needed = 4; - for (uint32_t n = 0; n < setting_count; n++) { - x_profile_setting_t setting; - setting.value = SHIM_MEM_32(setting_ids_ptr + n * 4); - size_needed += setting.size; + // Compute required size. + uint32_t size_needed = 4 + 4 + setting_count * kSettingSize; + for (uint32_t n = 0; n < setting_count; ++n) { + uint32_t setting_id = SHIM_MEM_32(setting_ids_ptr + n * 4); + auto setting = user_profile->GetSetting(setting_id); + if (setting) { + auto extra_size = static_cast(setting->extra_size());; + size_needed += extra_size; + } } SHIM_SET_MEM_32(buffer_size_ptr, size_needed); if (buffer_size < size_needed) { + if (overlapped_ptr) { + state->CompleteOverlappedImmediate(overlapped_ptr, X_ERROR_INSUFFICIENT_BUFFER); + } SHIM_SET_RETURN_32(X_ERROR_INSUFFICIENT_BUFFER); return; } - // TODO(benvanik): read profile data. - // For now, just return signed out. - SHIM_SET_RETURN_32(X_ERROR_NOT_FOUND); + SHIM_SET_MEM_32(buffer_ptr + 0, setting_count); + SHIM_SET_MEM_32(buffer_ptr + 4, buffer_ptr + 8); + + size_t buffer_offset = 4 + setting_count * kSettingSize; + size_t user_data_ptr = buffer_ptr + 8; + for (uint32_t n = 0; n < setting_count; ++n) { + uint32_t setting_id = SHIM_MEM_32(setting_ids_ptr + n * 4); + auto setting = user_profile->GetSetting(setting_id); + if (setting) { + SHIM_SET_MEM_32(user_data_ptr + 0, setting->is_title_specific() ? 2 : 1); + } else { + SHIM_SET_MEM_32(user_data_ptr + 0, 0); + } + SHIM_SET_MEM_32(user_data_ptr + 8, user_index); + SHIM_SET_MEM_32(user_data_ptr + 16, setting_id); + if (setting) { + buffer_offset = setting->Append(SHIM_MEM_ADDR(user_data_ptr + 24), + SHIM_MEM_ADDR(buffer_ptr), + buffer_offset); + } else { + memset(SHIM_MEM_ADDR(user_data_ptr + 24), 0, 16); + } + user_data_ptr += kSettingSize; + } + + if (overlapped_ptr) { + state->CompleteOverlappedImmediate(overlapped_ptr, X_ERROR_SUCCESS); + SHIM_SET_RETURN_32(X_ERROR_IO_PENDING); + } else { + SHIM_SET_RETURN_32(X_ERROR_SUCCESS); + } } diff --git a/src/xenia/xbox.h b/src/xenia/xbox.h index 9c47ced22..e597e63bb 100644 --- a/src/xenia/xbox.h +++ b/src/xenia/xbox.h @@ -60,11 +60,12 @@ typedef uint32_t X_STATUS; // Adding as needed. typedef uint32_t X_RESULT; #define X_FACILITY_WIN32 7 -#define X_HRESULT_FROM_WIN32(x) ((X_RESULT)(x) <= 0 ? ((X_RESULT)(x)) : ((X_RESULT) (((x) & 0x0000FFFF) | (X_FACILITY_WIN32 << 16) | 0x80000000))) +#define X_HRESULT_FROM_WIN32(x) x //((X_RESULT)(x) <= 0 ? ((X_RESULT)(x)) : ((X_RESULT) (((x) & 0x0000FFFF) | (X_FACILITY_WIN32 << 16) | 0x80000000))) #define X_ERROR_SUCCESS X_HRESULT_FROM_WIN32(0x00000000L) #define X_ERROR_ACCESS_DENIED X_HRESULT_FROM_WIN32(0x00000005L) #define X_ERROR_INVALID_HANDLE X_HRESULT_FROM_WIN32(0x00000006L) #define X_ERROR_NO_MORE_FILES X_HRESULT_FROM_WIN32(0x00000018L) +#define X_ERROR_IO_PENDING X_HRESULT_FROM_WIN32(0x000003E5L) #define X_ERROR_INSUFFICIENT_BUFFER X_HRESULT_FROM_WIN32(0x0000007AL) #define X_ERROR_BAD_ARGUMENTS X_HRESULT_FROM_WIN32(0x000000A0L) #define X_ERROR_BUSY X_HRESULT_FROM_WIN32(0x000000AAL) @@ -194,31 +195,35 @@ typedef enum _X_FILE_INFORMATION_CLASS { inline void XOverlappedSetResult(void* ptr, uint32_t value) { auto p = reinterpret_cast(ptr); - p[0] = value; + poly::store_and_swap(&p[0], value); } inline void XOverlappedSetLength(void* ptr, uint32_t value) { auto p = reinterpret_cast(ptr); - p[1] = value; + poly::store_and_swap(&p[1], value); +} +inline uint32_t XOverlappedGetContext(void* ptr) { + auto p = reinterpret_cast(ptr); + return poly::load_and_swap(&p[2]); } inline void XOverlappedSetContext(void* ptr, uint32_t value) { auto p = reinterpret_cast(ptr); - p[2] = value; + poly::store_and_swap(&p[2], value); } inline void XOverlappedSetExtendedError(void* ptr, uint32_t value) { auto p = reinterpret_cast(ptr); - p[7] = value; + poly::store_and_swap(&p[7], value); } inline X_HANDLE XOverlappedGetEvent(void* ptr) { auto p = reinterpret_cast(ptr); - return p[4]; + return poly::load_and_swap(&p[4]); } inline uint32_t XOverlappedGetCompletionRoutine(void* ptr) { auto p = reinterpret_cast(ptr); - return p[5]; + return poly::load_and_swap(&p[5]); } inline uint32_t XOverlappedGetCompletionContext(void* ptr) { auto p = reinterpret_cast(ptr); - return p[6]; + return poly::load_and_swap(&p[6]); } class X_ANSI_STRING {