C++17ification.
C++17ification! - Filesystem interaction now uses std::filesystem::path. - Usage of const char*, std::string have been changed to std::string_view where appropriate. - Usage of printf-style functions changed to use fmt.
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -19,14 +19,15 @@
|
||||
namespace xe {
|
||||
namespace kernel {
|
||||
|
||||
KernelModule::KernelModule(KernelState* kernel_state, const char* path)
|
||||
KernelModule::KernelModule(KernelState* kernel_state,
|
||||
const std::string_view path)
|
||||
: XModule(kernel_state, ModuleType::kKernelModule) {
|
||||
emulator_ = kernel_state->emulator();
|
||||
memory_ = emulator_->memory();
|
||||
export_resolver_ = kernel_state->emulator()->export_resolver();
|
||||
|
||||
path_ = path;
|
||||
name_ = NameFromPath(path);
|
||||
name_ = utf8::find_base_name_from_guest_path(path);
|
||||
|
||||
// Persist this object through reloads.
|
||||
host_object_ = true;
|
||||
@@ -48,7 +49,7 @@ KernelModule::KernelModule(KernelState* kernel_state, const char* path)
|
||||
emulator_->processor()->AddModule(std::move(module));
|
||||
} else {
|
||||
XELOGW("KernelModule %s could not allocate trampoline for GetProcAddress!",
|
||||
path);
|
||||
path.c_str());
|
||||
}
|
||||
|
||||
OnLoad();
|
||||
@@ -143,7 +144,7 @@ uint32_t KernelModule::GetProcAddressByOrdinal(uint16_t ordinal) {
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t KernelModule::GetProcAddressByName(const char* name) {
|
||||
uint32_t KernelModule::GetProcAddressByName(const std::string_view name) {
|
||||
// TODO: Does this even work for kernel modules?
|
||||
XELOGE("KernelModule::GetProcAddressByName not implemented");
|
||||
return 0;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -26,17 +26,23 @@ class KernelState;
|
||||
|
||||
class KernelModule : public XModule {
|
||||
public:
|
||||
KernelModule(KernelState* kernel_state, const char* path);
|
||||
KernelModule(KernelState* kernel_state, const std::string_view path);
|
||||
~KernelModule() override;
|
||||
|
||||
const std::string& path() const override { return path_; }
|
||||
const std::string& name() const override { return name_; }
|
||||
|
||||
uint32_t GetProcAddressByOrdinal(uint16_t ordinal) override;
|
||||
uint32_t GetProcAddressByName(const char* name) override;
|
||||
uint32_t GetProcAddressByName(const std::string_view name) override;
|
||||
|
||||
protected:
|
||||
Emulator* emulator_;
|
||||
Memory* memory_;
|
||||
xe::cpu::ExportResolver* export_resolver_;
|
||||
|
||||
std::string name_;
|
||||
std::string path_;
|
||||
|
||||
// Guest trampoline for GetProcAddress
|
||||
static const uint32_t kTrampolineSize = 400 * 8;
|
||||
uint32_t guest_trampoline_ = 0;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "third_party/fmt/include/fmt/format.h"
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/byte_stream.h"
|
||||
#include "xenia/base/logging.h"
|
||||
@@ -51,7 +52,7 @@ KernelState::KernelState(Emulator* emulator)
|
||||
user_profile_ = std::make_unique<xam::UserProfile>();
|
||||
|
||||
auto content_root = emulator_->content_root();
|
||||
content_root = xe::to_absolute_path(content_root);
|
||||
content_root = std::filesystem::absolute(content_root);
|
||||
content_manager_ = std::make_unique<xam::ContentManager>(this, content_root);
|
||||
|
||||
assert_null(shared_kernel_state_);
|
||||
@@ -165,8 +166,8 @@ void KernelState::UnregisterUserModule(UserModule* module) {
|
||||
}
|
||||
}
|
||||
|
||||
bool KernelState::IsKernelModule(const char* name) {
|
||||
if (!name) {
|
||||
bool KernelState::IsKernelModule(const std::string_view name) {
|
||||
if (name.empty()) {
|
||||
// Executing module isn't a kernel module.
|
||||
return false;
|
||||
}
|
||||
@@ -179,7 +180,8 @@ bool KernelState::IsKernelModule(const char* name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
object_ref<KernelModule> KernelState::GetKernelModule(const char* name) {
|
||||
object_ref<KernelModule> KernelState::GetKernelModule(
|
||||
const std::string_view name) {
|
||||
assert_true(IsKernelModule(name));
|
||||
|
||||
for (auto kernel_module : kernel_modules_) {
|
||||
@@ -191,12 +193,13 @@ object_ref<KernelModule> KernelState::GetKernelModule(const char* name) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
object_ref<XModule> KernelState::GetModule(const char* name, bool user_only) {
|
||||
if (!name) {
|
||||
object_ref<XModule> KernelState::GetModule(const std::string_view name,
|
||||
bool user_only) {
|
||||
if (name.empty()) {
|
||||
// NULL name = self.
|
||||
// TODO(benvanik): lookup module from caller address.
|
||||
return GetExecutableModule();
|
||||
} else if (strcasecmp(name, "kernel32.dll") == 0) {
|
||||
} else if (xe::utf8::equal_case(name, "kernel32.dll")) {
|
||||
// Some games request this, for some reason. wtf.
|
||||
return nullptr;
|
||||
}
|
||||
@@ -211,7 +214,7 @@ object_ref<XModule> KernelState::GetModule(const char* name, bool user_only) {
|
||||
}
|
||||
}
|
||||
|
||||
std::string path(name);
|
||||
auto path(name);
|
||||
|
||||
// Resolve the path to an absolute path.
|
||||
auto entry = file_system_->ResolvePath(name);
|
||||
@@ -242,10 +245,7 @@ object_ref<XThread> KernelState::LaunchModule(object_ref<UserModule> module) {
|
||||
module->entry_point(), 0, X_CREATE_SUSPENDED, true, true));
|
||||
|
||||
// We know this is the 'main thread'.
|
||||
char thread_name[32];
|
||||
std::snprintf(thread_name, xe::countof(thread_name), "Main XThread%08X",
|
||||
thread->handle());
|
||||
thread->set_name(thread_name);
|
||||
thread->set_name(fmt::format("Main XThread{:08X}", thread->handle()));
|
||||
|
||||
X_STATUS result = thread->Create();
|
||||
if (XFAILED(result)) {
|
||||
@@ -350,14 +350,15 @@ void KernelState::LoadKernelModule(object_ref<KernelModule> kernel_module) {
|
||||
kernel_modules_.push_back(std::move(kernel_module));
|
||||
}
|
||||
|
||||
object_ref<UserModule> KernelState::LoadUserModule(const char* raw_name,
|
||||
bool call_entry) {
|
||||
object_ref<UserModule> KernelState::LoadUserModule(
|
||||
const std::string_view raw_name, bool call_entry) {
|
||||
// Some games try to load relative to launch module, others specify full path.
|
||||
std::string name = xe::find_name_from_path(raw_name);
|
||||
auto name = xe::utf8::find_name_from_guest_path(raw_name);
|
||||
std::string path(raw_name);
|
||||
if (name == raw_name) {
|
||||
assert_not_null(executable_module_);
|
||||
path = xe::join_paths(xe::find_base_path(executable_module_->path()), name);
|
||||
path = xe::utf8::join_guest_paths(
|
||||
xe::utf8::find_base_guest_path(executable_module_->path()), name);
|
||||
}
|
||||
|
||||
object_ref<UserModule> module;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -123,18 +123,19 @@ class KernelState {
|
||||
void UnregisterModule(XModule* module);
|
||||
bool RegisterUserModule(object_ref<UserModule> module);
|
||||
void UnregisterUserModule(UserModule* module);
|
||||
bool IsKernelModule(const char* name);
|
||||
object_ref<XModule> GetModule(const char* name, bool user_only = false);
|
||||
bool IsKernelModule(const std::string_view name);
|
||||
object_ref<XModule> GetModule(const std::string_view name,
|
||||
bool user_only = false);
|
||||
|
||||
object_ref<XThread> LaunchModule(object_ref<UserModule> module);
|
||||
object_ref<UserModule> GetExecutableModule();
|
||||
void SetExecutableModule(object_ref<UserModule> module);
|
||||
object_ref<UserModule> LoadUserModule(const char* name,
|
||||
object_ref<UserModule> LoadUserModule(const std::string_view name,
|
||||
bool call_entry = true);
|
||||
void UnloadUserModule(const object_ref<UserModule>& module,
|
||||
bool call_entry = true);
|
||||
|
||||
object_ref<KernelModule> GetKernelModule(const char* name);
|
||||
object_ref<KernelModule> GetKernelModule(const std::string_view name);
|
||||
template <typename T>
|
||||
object_ref<KernelModule> LoadKernelModule() {
|
||||
auto kernel_module = object_ref<KernelModule>(new T(emulator_, this));
|
||||
@@ -142,7 +143,7 @@ class KernelState {
|
||||
return kernel_module;
|
||||
}
|
||||
template <typename T>
|
||||
object_ref<T> GetKernelModule(const char* name) {
|
||||
object_ref<T> GetKernelModule(const std::string_view name) {
|
||||
auto module = GetKernelModule(name);
|
||||
return object_ref<T>(reinterpret_cast<T*>(module.release()));
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ project("xenia-kernel")
|
||||
language("C++")
|
||||
links({
|
||||
"aes_128",
|
||||
"fmt",
|
||||
"xenia-apu",
|
||||
"xenia-base",
|
||||
"xenia-cpu",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -48,7 +48,7 @@ uint32_t UserModule::title_id() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
X_STATUS UserModule::LoadFromFile(std::string path) {
|
||||
X_STATUS UserModule::LoadFromFile(const std::string_view path) {
|
||||
X_STATUS result = X_STATUS_UNSUCCESSFUL;
|
||||
|
||||
// Resolve the file to open.
|
||||
@@ -60,7 +60,7 @@ X_STATUS UserModule::LoadFromFile(std::string path) {
|
||||
}
|
||||
|
||||
path_ = fs_entry->absolute_path();
|
||||
name_ = NameFromPath(path_);
|
||||
name_ = utf8::find_base_name_from_guest_path(path_);
|
||||
|
||||
// If the FS supports mapping, map the file in and load from that.
|
||||
if (fs_entry->can_map()) {
|
||||
@@ -258,11 +258,12 @@ uint32_t UserModule::GetProcAddressByOrdinal(uint16_t ordinal) {
|
||||
return xex_module()->GetProcAddress(ordinal);
|
||||
}
|
||||
|
||||
uint32_t UserModule::GetProcAddressByName(const char* name) {
|
||||
uint32_t UserModule::GetProcAddressByName(std::string_view name) {
|
||||
return xex_module()->GetProcAddress(name);
|
||||
}
|
||||
|
||||
X_STATUS UserModule::GetSection(const char* name, uint32_t* out_section_data,
|
||||
X_STATUS UserModule::GetSection(const std::string_view name,
|
||||
uint32_t* out_section_data,
|
||||
uint32_t* out_section_size) {
|
||||
xex2_opt_resource_info* resource_header = nullptr;
|
||||
if (!cpu::XexModule::GetOptHeader(xex_header(), XEX_HEADER_RESOURCE_INFO,
|
||||
@@ -270,15 +271,13 @@ X_STATUS UserModule::GetSection(const char* name, uint32_t* out_section_data,
|
||||
// No resources.
|
||||
return X_STATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
uint32_t count = (resource_header->size - 4) / sizeof(xex2_resource);
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
auto& res = resource_header->resources[i];
|
||||
if (std::strncmp(name, res.name, 8) == 0) {
|
||||
if (utf8::equal_z(name, std::string_view(res.name, 8))) {
|
||||
// Found!
|
||||
*out_section_data = res.address;
|
||||
*out_section_size = res.size;
|
||||
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -366,7 +365,7 @@ bool UserModule::Save(ByteStream* stream) {
|
||||
|
||||
object_ref<UserModule> UserModule::Restore(KernelState* kernel_state,
|
||||
ByteStream* stream,
|
||||
std::string path) {
|
||||
const std::string_view path) {
|
||||
auto module = new UserModule(kernel_state);
|
||||
|
||||
// XModule::Save took care of this earlier...
|
||||
@@ -403,23 +402,23 @@ void UserModule::Dump() {
|
||||
auto header = xex_header();
|
||||
|
||||
// XEX header.
|
||||
sb.AppendFormat("Module %s:\n", path_.c_str());
|
||||
sb.AppendFormat(" Module Flags: %.8X\n", (uint32_t)header->module_flags);
|
||||
sb.AppendFormat("Module {}:\n", path_);
|
||||
sb.AppendFormat(" Module Flags: {:08X}\n", (uint32_t)header->module_flags);
|
||||
|
||||
// Security header
|
||||
auto security_info = xex_module()->xex_security_info();
|
||||
sb.AppendFormat("Security Header:\n");
|
||||
sb.AppendFormat(" Image Flags: %.8X\n",
|
||||
sb.Append("Security Header:\n");
|
||||
sb.AppendFormat(" Image Flags: {:08X}\n",
|
||||
(uint32_t)security_info->image_flags);
|
||||
sb.AppendFormat(" Load Address: %.8X\n",
|
||||
sb.AppendFormat(" Load Address: {:08X}\n",
|
||||
(uint32_t)security_info->load_address);
|
||||
sb.AppendFormat(" Image Size: %.8X\n",
|
||||
sb.AppendFormat(" Image Size: {:08X}\n",
|
||||
(uint32_t)security_info->image_size);
|
||||
sb.AppendFormat(" Export Table: %.8X\n",
|
||||
sb.AppendFormat(" Export Table: {:08X}\n",
|
||||
(uint32_t)security_info->export_table);
|
||||
|
||||
// Optional headers
|
||||
sb.AppendFormat("Optional Header Count: %d\n",
|
||||
sb.AppendFormat("Optional Header Count: {}\n",
|
||||
(uint32_t)header->header_count);
|
||||
|
||||
for (uint32_t i = 0; i < header->header_count; i++) {
|
||||
@@ -430,7 +429,7 @@ void UserModule::Dump() {
|
||||
reinterpret_cast<const uint8_t*>(header) + opt_header.offset;
|
||||
switch (opt_header.key) {
|
||||
case XEX_HEADER_RESOURCE_INFO: {
|
||||
sb.AppendFormat(" XEX_HEADER_RESOURCE_INFO:\n");
|
||||
sb.Append(" XEX_HEADER_RESOURCE_INFO:\n");
|
||||
auto opt_resource_info =
|
||||
reinterpret_cast<const xex2_opt_resource_info*>(opt_header_ptr);
|
||||
|
||||
@@ -444,36 +443,36 @@ void UserModule::Dump() {
|
||||
name[8] = 0;
|
||||
|
||||
sb.AppendFormat(
|
||||
" %-8s %.8X-%.8X, %db\n", name, (uint32_t)res.address,
|
||||
" {:<8} {:08X}-{:08X}, {}b\n", name, (uint32_t)res.address,
|
||||
(uint32_t)res.address + (uint32_t)res.size, (uint32_t)res.size);
|
||||
}
|
||||
} break;
|
||||
case XEX_HEADER_FILE_FORMAT_INFO: {
|
||||
sb.AppendFormat(" XEX_HEADER_FILE_FORMAT_INFO (TODO):\n");
|
||||
sb.Append(" XEX_HEADER_FILE_FORMAT_INFO (TODO):\n");
|
||||
} break;
|
||||
case XEX_HEADER_DELTA_PATCH_DESCRIPTOR: {
|
||||
sb.AppendFormat(" XEX_HEADER_DELTA_PATCH_DESCRIPTOR (TODO):\n");
|
||||
sb.Append(" XEX_HEADER_DELTA_PATCH_DESCRIPTOR (TODO):\n");
|
||||
} break;
|
||||
case XEX_HEADER_BOUNDING_PATH: {
|
||||
auto opt_bound_path =
|
||||
reinterpret_cast<const xex2_opt_bound_path*>(opt_header_ptr);
|
||||
sb.AppendFormat(" XEX_HEADER_BOUNDING_PATH: %s\n",
|
||||
sb.AppendFormat(" XEX_HEADER_BOUNDING_PATH: {}\n",
|
||||
opt_bound_path->path);
|
||||
} break;
|
||||
case XEX_HEADER_ORIGINAL_BASE_ADDRESS: {
|
||||
sb.AppendFormat(" XEX_HEADER_ORIGINAL_BASE_ADDRESS: %.8X\n",
|
||||
sb.AppendFormat(" XEX_HEADER_ORIGINAL_BASE_ADDRESS: {:08X}\n",
|
||||
(uint32_t)opt_header.value);
|
||||
} break;
|
||||
case XEX_HEADER_ENTRY_POINT: {
|
||||
sb.AppendFormat(" XEX_HEADER_ENTRY_POINT: %.8X\n",
|
||||
sb.AppendFormat(" XEX_HEADER_ENTRY_POINT: {:08X}\n",
|
||||
(uint32_t)opt_header.value);
|
||||
} break;
|
||||
case XEX_HEADER_IMAGE_BASE_ADDRESS: {
|
||||
sb.AppendFormat(" XEX_HEADER_IMAGE_BASE_ADDRESS: %.8X\n",
|
||||
sb.AppendFormat(" XEX_HEADER_IMAGE_BASE_ADDRESS: {:08X}\n",
|
||||
(uint32_t)opt_header.value);
|
||||
} break;
|
||||
case XEX_HEADER_IMPORT_LIBRARIES: {
|
||||
sb.AppendFormat(" XEX_HEADER_IMPORT_LIBRARIES:\n");
|
||||
sb.Append(" XEX_HEADER_IMPORT_LIBRARIES:\n");
|
||||
auto opt_import_libraries =
|
||||
reinterpret_cast<const xex2_opt_import_libraries*>(opt_header_ptr);
|
||||
|
||||
@@ -509,7 +508,7 @@ void UserModule::Dump() {
|
||||
}
|
||||
auto name = string_table[library->name_index & 0xFF];
|
||||
assert_not_null(name);
|
||||
sb.AppendFormat(" %s - %d imports\n", name,
|
||||
sb.AppendFormat(" {} - {} imports\n", name,
|
||||
(uint16_t)library->count);
|
||||
|
||||
// Manually byteswap these because of the bitfields.
|
||||
@@ -517,9 +516,9 @@ void UserModule::Dump() {
|
||||
version.value = xe::byte_swap<uint32_t>(library->version.value);
|
||||
version_min.value =
|
||||
xe::byte_swap<uint32_t>(library->version_min.value);
|
||||
sb.AppendFormat(" Version: %d.%d.%d.%d\n", version.major,
|
||||
sb.AppendFormat(" Version: {}.{}.{}.{}\n", version.major,
|
||||
version.minor, version.build, version.qfe);
|
||||
sb.AppendFormat(" Min Version: %d.%d.%d.%d\n", version_min.major,
|
||||
sb.AppendFormat(" Min Version: {}.{}.{}.{}\n", version_min.major,
|
||||
version_min.minor, version_min.build,
|
||||
version_min.qfe);
|
||||
|
||||
@@ -527,23 +526,23 @@ void UserModule::Dump() {
|
||||
}
|
||||
} break;
|
||||
case XEX_HEADER_CHECKSUM_TIMESTAMP: {
|
||||
sb.AppendFormat(" XEX_HEADER_CHECKSUM_TIMESTAMP (TODO):\n");
|
||||
sb.Append(" XEX_HEADER_CHECKSUM_TIMESTAMP (TODO):\n");
|
||||
} break;
|
||||
case XEX_HEADER_ORIGINAL_PE_NAME: {
|
||||
auto opt_pe_name =
|
||||
reinterpret_cast<const xex2_opt_original_pe_name*>(opt_header_ptr);
|
||||
sb.AppendFormat(" XEX_HEADER_ORIGINAL_PE_NAME: %s\n",
|
||||
sb.AppendFormat(" XEX_HEADER_ORIGINAL_PE_NAME: {}\n",
|
||||
opt_pe_name->name);
|
||||
} break;
|
||||
case XEX_HEADER_STATIC_LIBRARIES: {
|
||||
sb.AppendFormat(" XEX_HEADER_STATIC_LIBRARIES:\n");
|
||||
sb.Append(" XEX_HEADER_STATIC_LIBRARIES:\n");
|
||||
auto opt_static_libraries =
|
||||
reinterpret_cast<const xex2_opt_static_libraries*>(opt_header_ptr);
|
||||
|
||||
uint32_t count = (opt_static_libraries->size - 4) / 0x10;
|
||||
for (uint32_t l = 0; l < count; l++) {
|
||||
auto& library = opt_static_libraries->libraries[l];
|
||||
sb.AppendFormat(" %-8s : %d.%d.%d.%d\n", library.name,
|
||||
sb.AppendFormat(" {:<8} : {}.{}.{}.{}\n", library.name,
|
||||
static_cast<uint16_t>(library.version_major),
|
||||
static_cast<uint16_t>(library.version_minor),
|
||||
static_cast<uint16_t>(library.version_build),
|
||||
@@ -551,84 +550,84 @@ void UserModule::Dump() {
|
||||
}
|
||||
} break;
|
||||
case XEX_HEADER_TLS_INFO: {
|
||||
sb.AppendFormat(" XEX_HEADER_TLS_INFO:\n");
|
||||
sb.Append(" XEX_HEADER_TLS_INFO:\n");
|
||||
auto opt_tls_info =
|
||||
reinterpret_cast<const xex2_opt_tls_info*>(opt_header_ptr);
|
||||
|
||||
sb.AppendFormat(" Slot Count: %d\n",
|
||||
sb.AppendFormat(" Slot Count: {}\n",
|
||||
static_cast<uint32_t>(opt_tls_info->slot_count));
|
||||
sb.AppendFormat(" Raw Data Address: %.8X\n",
|
||||
sb.AppendFormat(" Raw Data Address: {:08X}\n",
|
||||
static_cast<uint32_t>(opt_tls_info->raw_data_address));
|
||||
sb.AppendFormat(" Data Size: %d\n",
|
||||
sb.AppendFormat(" Data Size: {}\n",
|
||||
static_cast<uint32_t>(opt_tls_info->data_size));
|
||||
sb.AppendFormat(" Raw Data Size: %d\n",
|
||||
sb.AppendFormat(" Raw Data Size: {}\n",
|
||||
static_cast<uint32_t>(opt_tls_info->raw_data_size));
|
||||
} break;
|
||||
case XEX_HEADER_DEFAULT_STACK_SIZE: {
|
||||
sb.AppendFormat(" XEX_HEADER_DEFAULT_STACK_SIZE: %d\n",
|
||||
sb.AppendFormat(" XEX_HEADER_DEFAULT_STACK_SIZE: {}\n",
|
||||
static_cast<uint32_t>(opt_header.value));
|
||||
} break;
|
||||
case XEX_HEADER_DEFAULT_FILESYSTEM_CACHE_SIZE: {
|
||||
sb.AppendFormat(" XEX_HEADER_DEFAULT_FILESYSTEM_CACHE_SIZE: %d\n",
|
||||
sb.AppendFormat(" XEX_HEADER_DEFAULT_FILESYSTEM_CACHE_SIZE: {}\n",
|
||||
static_cast<uint32_t>(opt_header.value));
|
||||
} break;
|
||||
case XEX_HEADER_DEFAULT_HEAP_SIZE: {
|
||||
sb.AppendFormat(" XEX_HEADER_DEFAULT_HEAP_SIZE: %d\n",
|
||||
sb.AppendFormat(" XEX_HEADER_DEFAULT_HEAP_SIZE: {}\n",
|
||||
static_cast<uint32_t>(opt_header.value));
|
||||
} break;
|
||||
case XEX_HEADER_PAGE_HEAP_SIZE_AND_FLAGS: {
|
||||
sb.AppendFormat(" XEX_HEADER_PAGE_HEAP_SIZE_AND_FLAGS (TODO):\n");
|
||||
sb.Append(" XEX_HEADER_PAGE_HEAP_SIZE_AND_FLAGS (TODO):\n");
|
||||
} break;
|
||||
case XEX_HEADER_SYSTEM_FLAGS: {
|
||||
sb.AppendFormat(" XEX_HEADER_SYSTEM_FLAGS: %.8X\n",
|
||||
sb.AppendFormat(" XEX_HEADER_SYSTEM_FLAGS: {:08X}\n",
|
||||
static_cast<uint32_t>(opt_header.value));
|
||||
} break;
|
||||
case XEX_HEADER_EXECUTION_INFO: {
|
||||
sb.AppendFormat(" XEX_HEADER_EXECUTION_INFO:\n");
|
||||
sb.Append(" XEX_HEADER_EXECUTION_INFO:\n");
|
||||
auto opt_exec_info =
|
||||
reinterpret_cast<const xex2_opt_execution_info*>(opt_header_ptr);
|
||||
|
||||
sb.AppendFormat(" Media ID: %.8X\n",
|
||||
sb.AppendFormat(" Media ID: {:08X}\n",
|
||||
static_cast<uint32_t>(opt_exec_info->media_id));
|
||||
sb.AppendFormat(" Title ID: %.8X\n",
|
||||
sb.AppendFormat(" Title ID: {:08X}\n",
|
||||
static_cast<uint32_t>(opt_exec_info->title_id));
|
||||
sb.AppendFormat(" Savegame ID: %.8X\n",
|
||||
sb.AppendFormat(" Savegame ID: {:08X}\n",
|
||||
static_cast<uint32_t>(opt_exec_info->title_id));
|
||||
sb.AppendFormat(" Disc Number / Total: %d / %d\n",
|
||||
sb.AppendFormat(" Disc Number / Total: {} / {}\n",
|
||||
opt_exec_info->disc_number, opt_exec_info->disc_count);
|
||||
} break;
|
||||
case XEX_HEADER_TITLE_WORKSPACE_SIZE: {
|
||||
sb.AppendFormat(" XEX_HEADER_TITLE_WORKSPACE_SIZE: %d\n",
|
||||
sb.AppendFormat(" XEX_HEADER_TITLE_WORKSPACE_SIZE: {}\n",
|
||||
uint32_t(opt_header.value));
|
||||
} break;
|
||||
case XEX_HEADER_GAME_RATINGS: {
|
||||
sb.AppendFormat(" XEX_HEADER_GAME_RATINGS (TODO):\n");
|
||||
sb.Append(" XEX_HEADER_GAME_RATINGS (TODO):\n");
|
||||
} break;
|
||||
case XEX_HEADER_LAN_KEY: {
|
||||
sb.AppendFormat(" XEX_HEADER_LAN_KEY:");
|
||||
sb.Append(" XEX_HEADER_LAN_KEY:");
|
||||
auto opt_lan_key =
|
||||
reinterpret_cast<const xex2_opt_lan_key*>(opt_header_ptr);
|
||||
|
||||
for (int l = 0; l < 16; l++) {
|
||||
sb.AppendFormat(" %.2X", opt_lan_key->key[l]);
|
||||
sb.AppendFormat(" {:02X}", opt_lan_key->key[l]);
|
||||
}
|
||||
sb.Append("\n");
|
||||
} break;
|
||||
case XEX_HEADER_XBOX360_LOGO: {
|
||||
sb.AppendFormat(" XEX_HEADER_XBOX360_LOGO (TODO):\n");
|
||||
sb.Append(" XEX_HEADER_XBOX360_LOGO (TODO):\n");
|
||||
} break;
|
||||
case XEX_HEADER_MULTIDISC_MEDIA_IDS: {
|
||||
sb.AppendFormat(" XEX_HEADER_MULTIDISC_MEDIA_IDS (TODO):\n");
|
||||
sb.Append(" XEX_HEADER_MULTIDISC_MEDIA_IDS (TODO):\n");
|
||||
} break;
|
||||
case XEX_HEADER_ALTERNATE_TITLE_IDS: {
|
||||
sb.AppendFormat(" XEX_HEADER_ALTERNATE_TITLE_IDS (TODO):\n");
|
||||
sb.Append(" XEX_HEADER_ALTERNATE_TITLE_IDS (TODO):\n");
|
||||
} break;
|
||||
case XEX_HEADER_ADDITIONAL_TITLE_MEMORY: {
|
||||
sb.AppendFormat(" XEX_HEADER_ADDITIONAL_TITLE_MEMORY: %d\n",
|
||||
sb.AppendFormat(" XEX_HEADER_ADDITIONAL_TITLE_MEMORY: {}\n",
|
||||
uint32_t(opt_header.value));
|
||||
} break;
|
||||
case XEX_HEADER_EXPORTS_BY_NAME: {
|
||||
sb.AppendFormat(" XEX_HEADER_EXPORTS_BY_NAME:\n");
|
||||
sb.Append(" XEX_HEADER_EXPORTS_BY_NAME:\n");
|
||||
auto dir =
|
||||
reinterpret_cast<const xex2_opt_data_directory*>(opt_header_ptr);
|
||||
|
||||
@@ -650,16 +649,17 @@ void UserModule::Dump() {
|
||||
auto name = reinterpret_cast<const char*>(e_base + name_table[n]);
|
||||
uint16_t ordinal = ordinal_table[n];
|
||||
uint32_t addr = exe_address + function_table[ordinal];
|
||||
sb.AppendFormat(" %-28s - %.3X - %.8X\n", name, ordinal, addr);
|
||||
sb.AppendFormat(" {:<28} - {:03X} - {:08X}\n", name, ordinal,
|
||||
addr);
|
||||
}
|
||||
} break;
|
||||
default: {
|
||||
sb.AppendFormat(" Unknown Header %.8X\n", (uint32_t)opt_header.key);
|
||||
sb.AppendFormat(" Unknown Header {:08X}\n", (uint32_t)opt_header.key);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
sb.AppendFormat("Sections:\n");
|
||||
sb.Append("Sections:\n");
|
||||
for (uint32_t i = 0, page = 0; i < security_info->page_descriptor_count;
|
||||
i++) {
|
||||
// Manually byteswap the bitfield data.
|
||||
@@ -686,8 +686,8 @@ void UserModule::Dump() {
|
||||
uint32_t end_address =
|
||||
start_address + (page_descriptor.page_count * page_size);
|
||||
|
||||
sb.AppendFormat(" %3u %s %3u pages %.8X - %.8X (%d bytes)\n", page,
|
||||
type, page_descriptor.page_count, start_address,
|
||||
sb.AppendFormat(" {:3} {} {:3} pages {:08X} - {:08X} ({} bytes)\n",
|
||||
page, type, page_descriptor.page_count, start_address,
|
||||
end_address, page_descriptor.page_count * page_size);
|
||||
page += page_descriptor.page_count;
|
||||
}
|
||||
@@ -696,20 +696,20 @@ void UserModule::Dump() {
|
||||
|
||||
auto import_libs = xex_module()->import_libraries();
|
||||
|
||||
sb.AppendFormat("Imports:\n");
|
||||
sb.Append("Imports:\n");
|
||||
for (std::vector<cpu::XexModule::ImportLibrary>::const_iterator library =
|
||||
import_libs->begin();
|
||||
library != import_libs->end(); ++library) {
|
||||
if (library->imports.size() > 0) {
|
||||
sb.AppendFormat(" %s - %lld imports\n", library->name.c_str(),
|
||||
sb.AppendFormat(" {} - {} imports\n", library->name,
|
||||
library->imports.size());
|
||||
sb.AppendFormat(" Version: %d.%d.%d.%d\n", library->version.major,
|
||||
sb.AppendFormat(" Version: {}.{}.{}.{}\n", library->version.major,
|
||||
library->version.minor, library->version.build,
|
||||
library->version.qfe);
|
||||
sb.AppendFormat(" Min Version: %d.%d.%d.%d\n",
|
||||
sb.AppendFormat(" Min Version: {}.{}.{}.{}\n",
|
||||
library->min_version.major, library->min_version.minor,
|
||||
library->min_version.build, library->min_version.qfe);
|
||||
sb.AppendFormat("\n");
|
||||
sb.Append("\n");
|
||||
|
||||
// Counts.
|
||||
int known_count = 0;
|
||||
@@ -720,9 +720,9 @@ void UserModule::Dump() {
|
||||
for (std::vector<cpu::XexModule::ImportLibraryFn>::const_iterator info =
|
||||
library->imports.begin();
|
||||
info != library->imports.end(); ++info) {
|
||||
if (kernel_state_->IsKernelModule(library->name.c_str())) {
|
||||
auto kernel_export = export_resolver->GetExportByOrdinal(
|
||||
library->name.c_str(), info->ordinal);
|
||||
if (kernel_state_->IsKernelModule(library->name)) {
|
||||
auto kernel_export =
|
||||
export_resolver->GetExportByOrdinal(library->name, info->ordinal);
|
||||
if (kernel_export) {
|
||||
known_count++;
|
||||
if (kernel_export->is_implemented()) {
|
||||
@@ -735,7 +735,7 @@ void UserModule::Dump() {
|
||||
unimpl_count++;
|
||||
}
|
||||
} else {
|
||||
auto module = kernel_state_->GetModule(library->name.c_str());
|
||||
auto module = kernel_state_->GetModule(library->name);
|
||||
if (module) {
|
||||
uint32_t export_addr =
|
||||
module->GetProcAddressByOrdinal(info->ordinal);
|
||||
@@ -753,12 +753,12 @@ void UserModule::Dump() {
|
||||
}
|
||||
}
|
||||
float total_count = static_cast<float>(library->imports.size()) / 100.0f;
|
||||
sb.AppendFormat(" Total: %4llu\n", library->imports.size());
|
||||
sb.AppendFormat(" Known: %3d%% (%d known, %d unknown)\n",
|
||||
sb.AppendFormat(" Total: {:4}\n", library->imports.size());
|
||||
sb.AppendFormat(" Known: {:3}% ({} known, {} unknown)\n",
|
||||
static_cast<int>(known_count / total_count), known_count,
|
||||
unknown_count);
|
||||
sb.AppendFormat(
|
||||
" Implemented: %3d%% (%d implemented, %d unimplemented)\n",
|
||||
" Implemented: {:3}% ({} implemented, {} unimplemented)\n",
|
||||
static_cast<int>(impl_count / total_count), impl_count, unimpl_count);
|
||||
sb.AppendFormat("\n");
|
||||
|
||||
@@ -770,15 +770,15 @@ void UserModule::Dump() {
|
||||
bool implemented = false;
|
||||
|
||||
cpu::Export* kernel_export = nullptr;
|
||||
if (kernel_state_->IsKernelModule(library->name.c_str())) {
|
||||
kernel_export = export_resolver->GetExportByOrdinal(
|
||||
library->name.c_str(), info->ordinal);
|
||||
if (kernel_state_->IsKernelModule(library->name)) {
|
||||
kernel_export =
|
||||
export_resolver->GetExportByOrdinal(library->name, info->ordinal);
|
||||
if (kernel_export) {
|
||||
name = kernel_export->name;
|
||||
implemented = kernel_export->is_implemented();
|
||||
}
|
||||
} else {
|
||||
auto module = kernel_state_->GetModule(library->name.c_str());
|
||||
auto module = kernel_state_->GetModule(library->name);
|
||||
if (module && module->GetProcAddressByOrdinal(info->ordinal)) {
|
||||
// TODO(benvanik): name lookup.
|
||||
implemented = true;
|
||||
@@ -786,11 +786,11 @@ void UserModule::Dump() {
|
||||
}
|
||||
if (kernel_export &&
|
||||
kernel_export->type == cpu::Export::Type::kVariable) {
|
||||
sb.AppendFormat(" V %.8X %.3X (%4d) %s %s\n",
|
||||
sb.AppendFormat(" V {:08X} {:03X} ({:4}) {} {}\n",
|
||||
info->value_address, info->ordinal, info->ordinal,
|
||||
implemented ? " " : "!!", name);
|
||||
} else if (info->thunk_address) {
|
||||
sb.AppendFormat(" F %.8X %.8X %.3X (%4d) %s %s\n",
|
||||
sb.AppendFormat(" F {:08X} {:08X} {:03X} ({:4}) {} {}\n",
|
||||
info->value_address, info->thunk_address,
|
||||
info->ordinal, info->ordinal,
|
||||
implemented ? " " : "!!", name);
|
||||
@@ -798,10 +798,10 @@ void UserModule::Dump() {
|
||||
}
|
||||
}
|
||||
|
||||
sb.AppendFormat("\n");
|
||||
sb.Append("\n");
|
||||
}
|
||||
|
||||
xe::LogLine(xe::LogLevel::Info, 'i', sb.GetString());
|
||||
xe::LogLine(xe::LogLevel::Info, 'i', sb.to_string_view());
|
||||
}
|
||||
|
||||
} // namespace kernel
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -36,6 +36,9 @@ class UserModule : public XModule {
|
||||
UserModule(KernelState* kernel_state);
|
||||
~UserModule() override;
|
||||
|
||||
const std::string& path() const override { return path_; }
|
||||
const std::string& name() const override { return name_; }
|
||||
|
||||
enum ModuleFormat {
|
||||
kModuleFormatUndefined = 0,
|
||||
kModuleFormatXex,
|
||||
@@ -61,13 +64,13 @@ class UserModule : public XModule {
|
||||
uint32_t entry_point() const { return entry_point_; }
|
||||
uint32_t stack_size() const { return stack_size_; }
|
||||
|
||||
X_STATUS LoadFromFile(std::string path);
|
||||
X_STATUS LoadFromFile(const std::string_view path);
|
||||
X_STATUS LoadFromMemory(const void* addr, const size_t length);
|
||||
X_STATUS Unload();
|
||||
|
||||
uint32_t GetProcAddressByOrdinal(uint16_t ordinal) override;
|
||||
uint32_t GetProcAddressByName(const char* name) override;
|
||||
X_STATUS GetSection(const char* name, uint32_t* out_section_data,
|
||||
uint32_t GetProcAddressByName(const std::string_view name) override;
|
||||
X_STATUS GetSection(const std::string_view name, uint32_t* out_section_data,
|
||||
uint32_t* out_section_size) override;
|
||||
|
||||
// Get optional header - FOR HOST USE ONLY!
|
||||
@@ -89,11 +92,15 @@ class UserModule : public XModule {
|
||||
|
||||
bool Save(ByteStream* stream) override;
|
||||
static object_ref<UserModule> Restore(KernelState* kernel_state,
|
||||
ByteStream* stream, std::string path);
|
||||
ByteStream* stream,
|
||||
const std::string_view path);
|
||||
|
||||
private:
|
||||
X_STATUS LoadXexContinue();
|
||||
|
||||
std::string name_;
|
||||
std::string path_;
|
||||
|
||||
uint32_t guest_xex_header_ = 0;
|
||||
ModuleFormat module_format_ = kModuleFormatUndefined;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2016 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -48,9 +48,9 @@ class GameInfoWrapper {
|
||||
static_assert_size(GameInfoBlockComm, 4);
|
||||
|
||||
struct GameInfoBlockTitl {
|
||||
xe::be<wchar_t> title[128];
|
||||
xe::be<wchar_t> description[256];
|
||||
xe::be<wchar_t> publisher[256]; // assumed field name from wxPirs
|
||||
xe::be<char16_t> title[128];
|
||||
xe::be<char16_t> description[256];
|
||||
xe::be<char16_t> publisher[256]; // assumed field name from wxPirs
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -328,42 +328,30 @@ X_HANDLE ObjectTable::TranslateHandle(X_HANDLE handle) {
|
||||
}
|
||||
}
|
||||
|
||||
X_STATUS ObjectTable::AddNameMapping(const std::string& name, X_HANDLE handle) {
|
||||
// Names are case-insensitive.
|
||||
std::string lower_name = name;
|
||||
std::transform(lower_name.begin(), lower_name.end(), lower_name.begin(),
|
||||
tolower);
|
||||
|
||||
X_STATUS ObjectTable::AddNameMapping(const std::string_view name,
|
||||
X_HANDLE handle) {
|
||||
auto global_lock = global_critical_region_.Acquire();
|
||||
if (name_table_.count(lower_name)) {
|
||||
if (name_table_.count(string_key_case(name))) {
|
||||
return X_STATUS_OBJECT_NAME_COLLISION;
|
||||
}
|
||||
name_table_.insert({lower_name, handle});
|
||||
name_table_.insert({string_key_case::create(name), handle});
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
void ObjectTable::RemoveNameMapping(const std::string& name) {
|
||||
void ObjectTable::RemoveNameMapping(const std::string_view name) {
|
||||
// Names are case-insensitive.
|
||||
std::string lower_name = name;
|
||||
std::transform(lower_name.begin(), lower_name.end(), lower_name.begin(),
|
||||
tolower);
|
||||
|
||||
auto global_lock = global_critical_region_.Acquire();
|
||||
auto it = name_table_.find(lower_name);
|
||||
auto it = name_table_.find(string_key_case(name));
|
||||
if (it != name_table_.end()) {
|
||||
name_table_.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
X_STATUS ObjectTable::GetObjectByName(const std::string& name,
|
||||
X_STATUS ObjectTable::GetObjectByName(const std::string_view name,
|
||||
X_HANDLE* out_handle) {
|
||||
// Names are case-insensitive.
|
||||
std::string lower_name = name;
|
||||
std::transform(lower_name.begin(), lower_name.end(), lower_name.begin(),
|
||||
tolower);
|
||||
|
||||
auto global_lock = global_critical_region_.Acquire();
|
||||
auto it = name_table_.find(lower_name);
|
||||
auto it = name_table_.find(string_key_case(name));
|
||||
if (it == name_table_.end()) {
|
||||
*out_handle = X_INVALID_HANDLE_VALUE;
|
||||
return X_STATUS_OBJECT_NAME_NOT_FOUND;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/base/mutex.h"
|
||||
#include "xenia/base/string_key.h"
|
||||
#include "xenia/kernel/xobject.h"
|
||||
#include "xenia/xbox.h"
|
||||
|
||||
@@ -57,9 +58,9 @@ class ObjectTable {
|
||||
return result;
|
||||
}
|
||||
|
||||
X_STATUS AddNameMapping(const std::string& name, X_HANDLE handle);
|
||||
void RemoveNameMapping(const std::string& name);
|
||||
X_STATUS GetObjectByName(const std::string& name, X_HANDLE* out_handle);
|
||||
X_STATUS AddNameMapping(const std::string_view name, X_HANDLE handle);
|
||||
void RemoveNameMapping(const std::string_view name);
|
||||
X_STATUS GetObjectByName(const std::string_view name, X_HANDLE* out_handle);
|
||||
template <typename T>
|
||||
std::vector<object_ref<T>> GetObjectsByType(XObject::Type type) {
|
||||
std::vector<object_ref<T>> results;
|
||||
@@ -99,7 +100,7 @@ class ObjectTable {
|
||||
uint32_t table_capacity_ = 0;
|
||||
ObjectTableEntry* table_ = nullptr;
|
||||
uint32_t last_free_entry_ = 0;
|
||||
std::unordered_map<std::string, X_HANDLE> name_table_;
|
||||
std::unordered_map<string_key_case, X_HANDLE> name_table_;
|
||||
};
|
||||
|
||||
// Generic lookup
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -13,6 +13,7 @@
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "third_party/fmt/include/fmt/format.h"
|
||||
#include "xenia/base/byte_order.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/memory.h"
|
||||
@@ -101,22 +102,22 @@ inline std::string TranslateAnsiStringAddress(const Memory* memory,
|
||||
memory, memory->TranslateVirtual<const X_ANSI_STRING*>(guest_address));
|
||||
}
|
||||
|
||||
inline std::wstring TranslateUnicodeString(
|
||||
inline std::u16string TranslateUnicodeString(
|
||||
const Memory* memory, const X_UNICODE_STRING* unicode_string) {
|
||||
if (!unicode_string) {
|
||||
return L"";
|
||||
return u"";
|
||||
}
|
||||
uint16_t length = unicode_string->length;
|
||||
if (!length) {
|
||||
return L"";
|
||||
return u"";
|
||||
}
|
||||
const xe::be<uint16_t>* guest_string =
|
||||
memory->TranslateVirtual<const xe::be<uint16_t>*>(
|
||||
unicode_string->pointer);
|
||||
std::wstring translated_string;
|
||||
std::u16string translated_string;
|
||||
translated_string.reserve(length);
|
||||
for (uint16_t i = 0; i < length; ++i) {
|
||||
translated_string += wchar_t(uint16_t(guest_string[i]));
|
||||
translated_string += char16_t(uint16_t(guest_string[i]));
|
||||
}
|
||||
return translated_string;
|
||||
}
|
||||
@@ -352,7 +353,7 @@ using lpqword_t = const shim::PrimitivePointerParam<uint64_t>&;
|
||||
using lpfloat_t = const shim::PrimitivePointerParam<float>&;
|
||||
using lpdouble_t = const shim::PrimitivePointerParam<double>&;
|
||||
using lpstring_t = const shim::StringPointerParam<char, std::string>&;
|
||||
using lpwstring_t = const shim::StringPointerParam<wchar_t, std::wstring>&;
|
||||
using lpu16string_t = const shim::StringPointerParam<char16_t, std::u16string>&;
|
||||
using function_t = const shim::ParamBase<uint32_t>&;
|
||||
using unknown_t = const shim::ParamBase<uint32_t>&;
|
||||
using lpunknown_t = const shim::PointerParam&;
|
||||
@@ -371,65 +372,65 @@ inline Memory* kernel_memory() { return kernel_state()->memory(); }
|
||||
namespace shim {
|
||||
|
||||
inline void AppendParam(StringBuffer* string_buffer, int_t param) {
|
||||
string_buffer->AppendFormat("%d", int32_t(param));
|
||||
string_buffer->AppendFormat("{}", int32_t(param));
|
||||
}
|
||||
inline void AppendParam(StringBuffer* string_buffer, word_t param) {
|
||||
string_buffer->AppendFormat("%.4X", uint16_t(param));
|
||||
string_buffer->AppendFormat("{:04X}", uint16_t(param));
|
||||
}
|
||||
inline void AppendParam(StringBuffer* string_buffer, dword_t param) {
|
||||
string_buffer->AppendFormat("%.8X", uint32_t(param));
|
||||
string_buffer->AppendFormat("{:08X}", uint32_t(param));
|
||||
}
|
||||
inline void AppendParam(StringBuffer* string_buffer, qword_t param) {
|
||||
string_buffer->AppendFormat("%.16llX", uint64_t(param));
|
||||
string_buffer->AppendFormat("{:016X}", uint64_t(param));
|
||||
}
|
||||
inline void AppendParam(StringBuffer* string_buffer, float_t param) {
|
||||
string_buffer->AppendFormat("%G", static_cast<float>(param));
|
||||
string_buffer->AppendFormat("{:G}", static_cast<float>(param));
|
||||
}
|
||||
inline void AppendParam(StringBuffer* string_buffer, double_t param) {
|
||||
string_buffer->AppendFormat("%G", static_cast<double>(param));
|
||||
string_buffer->AppendFormat("{:G}", static_cast<double>(param));
|
||||
}
|
||||
inline void AppendParam(StringBuffer* string_buffer, lpvoid_t param) {
|
||||
string_buffer->AppendFormat("%.8X", uint32_t(param));
|
||||
string_buffer->AppendFormat("{:08X}", uint32_t(param));
|
||||
}
|
||||
inline void AppendParam(StringBuffer* string_buffer, lpdword_t param) {
|
||||
string_buffer->AppendFormat("%.8X", param.guest_address());
|
||||
string_buffer->AppendFormat("{:08X}", param.guest_address());
|
||||
if (param) {
|
||||
string_buffer->AppendFormat("(%.8X)", param.value());
|
||||
string_buffer->AppendFormat("({:08X})", param.value());
|
||||
}
|
||||
}
|
||||
inline void AppendParam(StringBuffer* string_buffer, lpqword_t param) {
|
||||
string_buffer->AppendFormat("%.8X", param.guest_address());
|
||||
string_buffer->AppendFormat("{:08X}", param.guest_address());
|
||||
if (param) {
|
||||
string_buffer->AppendFormat("(%.16llX)", param.value());
|
||||
string_buffer->AppendFormat("({:016X})", param.value());
|
||||
}
|
||||
}
|
||||
inline void AppendParam(StringBuffer* string_buffer, lpfloat_t param) {
|
||||
string_buffer->AppendFormat("%.8X", param.guest_address());
|
||||
string_buffer->AppendFormat("{:08X}", param.guest_address());
|
||||
if (param) {
|
||||
string_buffer->AppendFormat("(%G)", param.value());
|
||||
string_buffer->AppendFormat("({:G})", param.value());
|
||||
}
|
||||
}
|
||||
inline void AppendParam(StringBuffer* string_buffer, lpdouble_t param) {
|
||||
string_buffer->AppendFormat("%.8X", param.guest_address());
|
||||
string_buffer->AppendFormat("{:08X}", param.guest_address());
|
||||
if (param) {
|
||||
string_buffer->AppendFormat("(%G)", param.value());
|
||||
string_buffer->AppendFormat("({:G})", param.value());
|
||||
}
|
||||
}
|
||||
inline void AppendParam(StringBuffer* string_buffer, lpstring_t param) {
|
||||
string_buffer->AppendFormat("%.8X", param.guest_address());
|
||||
string_buffer->AppendFormat("{:08X}", param.guest_address());
|
||||
if (param) {
|
||||
string_buffer->AppendFormat("(%s)", param.value().c_str());
|
||||
string_buffer->AppendFormat("({})", param.value());
|
||||
}
|
||||
}
|
||||
inline void AppendParam(StringBuffer* string_buffer, lpwstring_t param) {
|
||||
string_buffer->AppendFormat("%.8X", param.guest_address());
|
||||
inline void AppendParam(StringBuffer* string_buffer, lpu16string_t param) {
|
||||
string_buffer->AppendFormat("{:08X}", param.guest_address());
|
||||
if (param) {
|
||||
string_buffer->AppendFormat("(%S)", param.value().c_str());
|
||||
string_buffer->AppendFormat("({})", xe::to_utf8(param.value()));
|
||||
}
|
||||
}
|
||||
inline void AppendParam(StringBuffer* string_buffer,
|
||||
pointer_t<X_OBJECT_ATTRIBUTES> record) {
|
||||
string_buffer->AppendFormat("%.8X", record.guest_address());
|
||||
string_buffer->AppendFormat("{:08X}", record.guest_address());
|
||||
if (record) {
|
||||
auto name_string =
|
||||
kernel_memory()->TranslateVirtual<X_ANSI_STRING*>(record->name_ptr);
|
||||
@@ -437,25 +438,25 @@ inline void AppendParam(StringBuffer* string_buffer,
|
||||
name_string == nullptr
|
||||
? "(null)"
|
||||
: util::TranslateAnsiString(kernel_memory(), name_string);
|
||||
string_buffer->AppendFormat("(%.8X,%s,%.8X)",
|
||||
uint32_t(record->root_directory), name.c_str(),
|
||||
string_buffer->AppendFormat("({:08X},{},{:08X})",
|
||||
uint32_t(record->root_directory), name,
|
||||
uint32_t(record->attributes));
|
||||
}
|
||||
}
|
||||
inline void AppendParam(StringBuffer* string_buffer,
|
||||
pointer_t<X_EX_TITLE_TERMINATE_REGISTRATION> reg) {
|
||||
string_buffer->AppendFormat("%.8X(%.8X, %.8X)", reg.guest_address(),
|
||||
string_buffer->AppendFormat("{:08X}({:08X}, {:08X})", reg.guest_address(),
|
||||
static_cast<uint32_t>(reg->notification_routine),
|
||||
static_cast<uint32_t>(reg->priority));
|
||||
}
|
||||
inline void AppendParam(StringBuffer* string_buffer,
|
||||
pointer_t<X_EXCEPTION_RECORD> record) {
|
||||
string_buffer->AppendFormat("%.8X(%.8X)", record.guest_address(),
|
||||
string_buffer->AppendFormat("{:08X}({:08X})", record.guest_address(),
|
||||
uint32_t(record->exception_code));
|
||||
}
|
||||
template <typename T>
|
||||
void AppendParam(StringBuffer* string_buffer, pointer_t<T> param) {
|
||||
string_buffer->AppendFormat("%.8X", param.guest_address());
|
||||
string_buffer->AppendFormat("{:08X}", param.guest_address());
|
||||
}
|
||||
|
||||
enum class KernelModuleId {
|
||||
@@ -493,11 +494,9 @@ void PrintKernelCall(cpu::Export* export_entry, const Tuple& params) {
|
||||
AppendKernelCallParams(string_buffer, export_entry, params);
|
||||
string_buffer.Append(')');
|
||||
if (export_entry->tags & xe::cpu::ExportTag::kImportant) {
|
||||
xe::LogLine(xe::LogLevel::Info, 'i', string_buffer.GetString(),
|
||||
string_buffer.length());
|
||||
xe::LogLine(xe::LogLevel::Info, 'i', string_buffer.to_string_view());
|
||||
} else {
|
||||
xe::LogLine(xe::LogLevel::Debug, 'd', string_buffer.GetString(),
|
||||
string_buffer.length());
|
||||
xe::LogLine(xe::LogLevel::Debug, 'd', string_buffer.to_string_view());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2014 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -43,17 +43,17 @@ X_RESULT XmpApp::XMPGetStatus(uint32_t state_ptr) {
|
||||
|
||||
X_RESULT XmpApp::XMPCreateTitlePlaylist(uint32_t songs_ptr, uint32_t song_count,
|
||||
uint32_t playlist_name_ptr,
|
||||
std::wstring playlist_name,
|
||||
const std::u16string& playlist_name,
|
||||
uint32_t flags,
|
||||
uint32_t out_song_handles,
|
||||
uint32_t out_playlist_handle) {
|
||||
XELOGD("XMPCreateTitlePlaylist(%.8X, %.8X, %.8X(%s), %.8X, %.8X, %.8X)",
|
||||
songs_ptr, song_count, playlist_name_ptr,
|
||||
xe::to_string(playlist_name).c_str(), flags, out_song_handles,
|
||||
xe::to_utf8(playlist_name).c_str(), flags, out_song_handles,
|
||||
out_playlist_handle);
|
||||
auto playlist = std::make_unique<Playlist>();
|
||||
playlist->handle = ++next_playlist_handle_;
|
||||
playlist->name = std::move(playlist_name);
|
||||
playlist->name = playlist_name;
|
||||
playlist->flags = flags;
|
||||
if (songs_ptr) {
|
||||
for (uint32_t i = 0; i < song_count; ++i) {
|
||||
@@ -61,18 +61,19 @@ X_RESULT XmpApp::XMPCreateTitlePlaylist(uint32_t songs_ptr, uint32_t song_count,
|
||||
song->handle = ++next_song_handle_;
|
||||
uint8_t* song_base = memory_->TranslateVirtual(songs_ptr + (i * 36));
|
||||
song->file_path =
|
||||
xe::load_and_swap<std::wstring>(memory_->TranslateVirtual(
|
||||
xe::load_and_swap<std::u16string>(memory_->TranslateVirtual(
|
||||
xe::load_and_swap<uint32_t>(song_base + 0)));
|
||||
song->name = xe::load_and_swap<std::wstring>(memory_->TranslateVirtual(
|
||||
song->name = xe::load_and_swap<std::u16string>(memory_->TranslateVirtual(
|
||||
xe::load_and_swap<uint32_t>(song_base + 4)));
|
||||
song->artist = xe::load_and_swap<std::wstring>(memory_->TranslateVirtual(
|
||||
xe::load_and_swap<uint32_t>(song_base + 8)));
|
||||
song->album = xe::load_and_swap<std::wstring>(memory_->TranslateVirtual(
|
||||
song->artist =
|
||||
xe::load_and_swap<std::u16string>(memory_->TranslateVirtual(
|
||||
xe::load_and_swap<uint32_t>(song_base + 8)));
|
||||
song->album = xe::load_and_swap<std::u16string>(memory_->TranslateVirtual(
|
||||
xe::load_and_swap<uint32_t>(song_base + 12)));
|
||||
song->album_artist =
|
||||
xe::load_and_swap<std::wstring>(memory_->TranslateVirtual(
|
||||
xe::load_and_swap<std::u16string>(memory_->TranslateVirtual(
|
||||
xe::load_and_swap<uint32_t>(song_base + 16)));
|
||||
song->genre = xe::load_and_swap<std::wstring>(memory_->TranslateVirtual(
|
||||
song->genre = xe::load_and_swap<std::u16string>(memory_->TranslateVirtual(
|
||||
xe::load_and_swap<uint32_t>(song_base + 20)));
|
||||
song->track_number = xe::load_and_swap<uint32_t>(song_base + 24);
|
||||
song->duration_ms = xe::load_and_swap<uint32_t>(song_base + 28);
|
||||
@@ -310,11 +311,11 @@ X_RESULT XmpApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
|
||||
xe::store_and_swap<uint32_t>(
|
||||
memory_->TranslateVirtual(playlist_handle_ptr), storage_ptr);
|
||||
assert_true(xmp_client == 0x00000002);
|
||||
std::wstring playlist_name;
|
||||
std::u16string playlist_name;
|
||||
if (!playlist_name_ptr) {
|
||||
playlist_name = L"";
|
||||
playlist_name = u"";
|
||||
} else {
|
||||
playlist_name = xe::load_and_swap<std::wstring>(
|
||||
playlist_name = xe::load_and_swap<std::u16string>(
|
||||
memory_->TranslateVirtual(playlist_name_ptr));
|
||||
}
|
||||
// dummy_alloc_ptr is the result of a XamAlloc of storage_size.
|
||||
@@ -337,12 +338,12 @@ X_RESULT XmpApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
|
||||
}
|
||||
auto& song = active_playlist_->songs[active_song_index_];
|
||||
xe::store_and_swap<uint32_t>(info + 0, song->handle);
|
||||
xe::store_and_swap<std::wstring>(info + 4 + 572 + 0, song->name);
|
||||
xe::store_and_swap<std::wstring>(info + 4 + 572 + 40, song->artist);
|
||||
xe::store_and_swap<std::wstring>(info + 4 + 572 + 80, song->album);
|
||||
xe::store_and_swap<std::wstring>(info + 4 + 572 + 120,
|
||||
song->album_artist);
|
||||
xe::store_and_swap<std::wstring>(info + 4 + 572 + 160, song->genre);
|
||||
xe::store_and_swap<std::u16string>(info + 4 + 572 + 0, song->name);
|
||||
xe::store_and_swap<std::u16string>(info + 4 + 572 + 40, song->artist);
|
||||
xe::store_and_swap<std::u16string>(info + 4 + 572 + 80, song->album);
|
||||
xe::store_and_swap<std::u16string>(info + 4 + 572 + 120,
|
||||
song->album_artist);
|
||||
xe::store_and_swap<std::u16string>(info + 4 + 572 + 160, song->genre);
|
||||
xe::store_and_swap<uint32_t>(info + 4 + 572 + 200, song->track_number);
|
||||
xe::store_and_swap<uint32_t>(info + 4 + 572 + 204, song->duration_ms);
|
||||
xe::store_and_swap<uint32_t>(info + 4 + 572 + 208,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2014 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -49,19 +49,19 @@ class XmpApp : public App {
|
||||
};
|
||||
|
||||
uint32_t handle;
|
||||
std::wstring file_path;
|
||||
std::wstring name;
|
||||
std::wstring artist;
|
||||
std::wstring album;
|
||||
std::wstring album_artist;
|
||||
std::wstring genre;
|
||||
std::u16string file_path;
|
||||
std::u16string name;
|
||||
std::u16string artist;
|
||||
std::u16string album;
|
||||
std::u16string album_artist;
|
||||
std::u16string genre;
|
||||
uint32_t track_number;
|
||||
uint32_t duration_ms;
|
||||
Format format;
|
||||
};
|
||||
struct Playlist {
|
||||
uint32_t handle;
|
||||
std::wstring name;
|
||||
std::u16string name;
|
||||
uint32_t flags;
|
||||
std::vector<std::unique_ptr<Song>> songs;
|
||||
};
|
||||
@@ -72,8 +72,8 @@ class XmpApp : public App {
|
||||
|
||||
X_RESULT XMPCreateTitlePlaylist(uint32_t songs_ptr, uint32_t song_count,
|
||||
uint32_t playlist_name_ptr,
|
||||
std::wstring playlist_name, uint32_t flags,
|
||||
uint32_t out_song_handles,
|
||||
const std::u16string& playlist_name,
|
||||
uint32_t flags, uint32_t out_song_handles,
|
||||
uint32_t out_playlist_handle);
|
||||
X_RESULT XMPDeleteTitlePlaylist(uint32_t playlist_handle);
|
||||
X_RESULT XMPPlayTitlePlaylist(uint32_t playlist_handle, uint32_t song_handle);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2015 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "third_party/fmt/include/fmt/format.h"
|
||||
#include "xenia/base/filesystem.h"
|
||||
#include "xenia/base/string.h"
|
||||
#include "xenia/kernel/kernel_state.h"
|
||||
@@ -21,18 +22,18 @@ namespace xe {
|
||||
namespace kernel {
|
||||
namespace xam {
|
||||
|
||||
static const wchar_t* kThumbnailFileName = L"__thumbnail.png";
|
||||
static const char* kThumbnailFileName = "__thumbnail.png";
|
||||
|
||||
static const wchar_t* kGameUserContentDirName = L"profile";
|
||||
static const char* kGameUserContentDirName = "profile";
|
||||
|
||||
static int content_device_id_ = 0;
|
||||
|
||||
ContentPackage::ContentPackage(KernelState* kernel_state, std::string root_name,
|
||||
ContentPackage::ContentPackage(KernelState* kernel_state,
|
||||
const std::string_view root_name,
|
||||
const XCONTENT_DATA& data,
|
||||
std::wstring package_path)
|
||||
: kernel_state_(kernel_state), root_name_(std::move(root_name)) {
|
||||
device_path_ = std::string("\\Device\\Content\\") +
|
||||
std::to_string(++content_device_id_) + "\\";
|
||||
const std::filesystem::path& package_path)
|
||||
: kernel_state_(kernel_state), root_name_(root_name) {
|
||||
device_path_ = fmt::format("\\Device\\Content\\{0}\\", ++content_device_id_);
|
||||
|
||||
auto fs = kernel_state_->file_system();
|
||||
auto device =
|
||||
@@ -49,53 +50,49 @@ ContentPackage::~ContentPackage() {
|
||||
}
|
||||
|
||||
ContentManager::ContentManager(KernelState* kernel_state,
|
||||
std::wstring root_path)
|
||||
: kernel_state_(kernel_state), root_path_(std::move(root_path)) {}
|
||||
const std::filesystem::path& root_path)
|
||||
: kernel_state_(kernel_state), root_path_(root_path) {}
|
||||
|
||||
ContentManager::~ContentManager() = default;
|
||||
|
||||
std::wstring ContentManager::ResolvePackageRoot(uint32_t content_type) {
|
||||
wchar_t title_id[9] = L"00000000";
|
||||
std::swprintf(title_id, 9, L"%.8X", kernel_state_->title_id());
|
||||
std::filesystem::path ContentManager::ResolvePackageRoot(
|
||||
uint32_t content_type) {
|
||||
auto title_id = fmt::format("{:8X}", kernel_state_->title_id());
|
||||
|
||||
std::wstring type_name;
|
||||
std::string type_name;
|
||||
switch (content_type) {
|
||||
case 1:
|
||||
// Save games.
|
||||
type_name = L"00000001";
|
||||
type_name = "00000001";
|
||||
break;
|
||||
case 2:
|
||||
// DLC from the marketplace.
|
||||
type_name = L"00000002";
|
||||
type_name = "00000002";
|
||||
break;
|
||||
case 3:
|
||||
// Publisher content?
|
||||
type_name = L"00000003";
|
||||
type_name = "00000003";
|
||||
break;
|
||||
case 0x000D0000:
|
||||
// ???
|
||||
type_name = L"000D0000";
|
||||
type_name = "000D0000";
|
||||
break;
|
||||
default:
|
||||
assert_unhandled_case(data.content_type);
|
||||
return nullptr;
|
||||
return std::filesystem::path();
|
||||
}
|
||||
|
||||
// Package root path:
|
||||
// content_root/title_id/type_name/
|
||||
auto package_root =
|
||||
xe::join_paths(root_path_, xe::join_paths(title_id, type_name));
|
||||
return package_root + xe::kWPathSeparator;
|
||||
return root_path_ / title_id / type_name;
|
||||
}
|
||||
|
||||
std::wstring ContentManager::ResolvePackagePath(const XCONTENT_DATA& data) {
|
||||
std::filesystem::path ContentManager::ResolvePackagePath(
|
||||
const XCONTENT_DATA& data) {
|
||||
// Content path:
|
||||
// content_root/title_id/type_name/data_file_name/
|
||||
auto package_root = ResolvePackageRoot(data.content_type);
|
||||
auto package_path =
|
||||
xe::join_paths(package_root, xe::to_wstring(data.file_name));
|
||||
package_path += xe::kPathSeparator;
|
||||
return package_path;
|
||||
return package_root / xe::to_path(data.file_name);
|
||||
}
|
||||
|
||||
std::vector<XCONTENT_DATA> ContentManager::ListContent(uint32_t device_id,
|
||||
@@ -114,8 +111,8 @@ std::vector<XCONTENT_DATA> ContentManager::ListContent(uint32_t device_id,
|
||||
XCONTENT_DATA content_data;
|
||||
content_data.device_id = device_id;
|
||||
content_data.content_type = content_type;
|
||||
content_data.display_name = file_info.name;
|
||||
content_data.file_name = xe::to_string(file_info.name);
|
||||
content_data.display_name = xe::path_to_utf16(file_info.name);
|
||||
content_data.file_name = xe::path_to_utf8(file_info.name);
|
||||
result.emplace_back(std::move(content_data));
|
||||
}
|
||||
|
||||
@@ -123,7 +120,7 @@ std::vector<XCONTENT_DATA> ContentManager::ListContent(uint32_t device_id,
|
||||
}
|
||||
|
||||
std::unique_ptr<ContentPackage> ContentManager::ResolvePackage(
|
||||
std::string root_name, const XCONTENT_DATA& data) {
|
||||
const std::string_view root_name, const XCONTENT_DATA& data) {
|
||||
auto package_path = ResolvePackagePath(data);
|
||||
if (!xe::filesystem::PathExists(package_path)) {
|
||||
return nullptr;
|
||||
@@ -141,11 +138,11 @@ bool ContentManager::ContentExists(const XCONTENT_DATA& data) {
|
||||
return xe::filesystem::PathExists(path);
|
||||
}
|
||||
|
||||
X_RESULT ContentManager::CreateContent(std::string root_name,
|
||||
X_RESULT ContentManager::CreateContent(const std::string_view root_name,
|
||||
const XCONTENT_DATA& data) {
|
||||
auto global_lock = global_critical_region_.Acquire();
|
||||
|
||||
if (open_packages_.count(root_name)) {
|
||||
if (open_packages_.count(string_key(root_name))) {
|
||||
// Already content open with this root name.
|
||||
return X_ERROR_ALREADY_EXISTS;
|
||||
}
|
||||
@@ -163,16 +160,16 @@ X_RESULT ContentManager::CreateContent(std::string root_name,
|
||||
auto package = ResolvePackage(root_name, data);
|
||||
assert_not_null(package);
|
||||
|
||||
open_packages_.insert({root_name, package.release()});
|
||||
open_packages_.insert({string_key::create(root_name), package.release()});
|
||||
|
||||
return X_ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
X_RESULT ContentManager::OpenContent(std::string root_name,
|
||||
X_RESULT ContentManager::OpenContent(const std::string_view root_name,
|
||||
const XCONTENT_DATA& data) {
|
||||
auto global_lock = global_critical_region_.Acquire();
|
||||
|
||||
if (open_packages_.count(root_name)) {
|
||||
if (open_packages_.count(string_key(root_name))) {
|
||||
// Already content open with this root name.
|
||||
return X_ERROR_ALREADY_EXISTS;
|
||||
}
|
||||
@@ -187,15 +184,15 @@ X_RESULT ContentManager::OpenContent(std::string root_name,
|
||||
auto package = ResolvePackage(root_name, data);
|
||||
assert_not_null(package);
|
||||
|
||||
open_packages_.insert({root_name, package.release()});
|
||||
open_packages_.insert({string_key::create(root_name), package.release()});
|
||||
|
||||
return X_ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
X_RESULT ContentManager::CloseContent(std::string root_name) {
|
||||
X_RESULT ContentManager::CloseContent(const std::string_view root_name) {
|
||||
auto global_lock = global_critical_region_.Acquire();
|
||||
|
||||
auto it = open_packages_.find(root_name);
|
||||
auto it = open_packages_.find(string_key(root_name));
|
||||
if (it == open_packages_.end()) {
|
||||
return X_ERROR_FILE_NOT_FOUND;
|
||||
}
|
||||
@@ -211,7 +208,7 @@ X_RESULT ContentManager::GetContentThumbnail(const XCONTENT_DATA& data,
|
||||
std::vector<uint8_t>* buffer) {
|
||||
auto global_lock = global_critical_region_.Acquire();
|
||||
auto package_path = ResolvePackagePath(data);
|
||||
auto thumb_path = xe::join_paths(package_path, kThumbnailFileName);
|
||||
auto thumb_path = package_path / kThumbnailFileName;
|
||||
if (xe::filesystem::PathExists(thumb_path)) {
|
||||
auto file = xe::filesystem::OpenFile(thumb_path, "rb");
|
||||
fseek(file, 0, SEEK_END);
|
||||
@@ -232,7 +229,7 @@ X_RESULT ContentManager::SetContentThumbnail(const XCONTENT_DATA& data,
|
||||
auto package_path = ResolvePackagePath(data);
|
||||
xe::filesystem::CreateFolder(package_path);
|
||||
if (xe::filesystem::PathExists(package_path)) {
|
||||
auto thumb_path = xe::join_paths(package_path, kThumbnailFileName);
|
||||
auto thumb_path = package_path / kThumbnailFileName;
|
||||
auto file = xe::filesystem::OpenFile(thumb_path, "wb");
|
||||
fwrite(buffer.data(), 1, buffer.size(), file);
|
||||
fclose(file);
|
||||
@@ -254,18 +251,13 @@ X_RESULT ContentManager::DeleteContent(const XCONTENT_DATA& data) {
|
||||
}
|
||||
}
|
||||
|
||||
std::wstring ContentManager::ResolveGameUserContentPath() {
|
||||
wchar_t title_id[9] = L"00000000";
|
||||
std::swprintf(title_id, 9, L"%.8X", kernel_state_->title_id());
|
||||
auto user_name = xe::to_wstring(kernel_state_->user_profile()->name());
|
||||
std::filesystem::path ContentManager::ResolveGameUserContentPath() {
|
||||
auto title_id = fmt::format("{:8X}", kernel_state_->title_id());
|
||||
auto user_name = xe::to_path(kernel_state_->user_profile()->name());
|
||||
|
||||
// Per-game per-profile data location:
|
||||
// content_root/title_id/profile/user_name
|
||||
auto package_root = xe::join_paths(
|
||||
root_path_,
|
||||
xe::join_paths(title_id,
|
||||
xe::join_paths(kGameUserContentDirName, user_name)));
|
||||
return package_root + xe::kWPathSeparator;
|
||||
return root_path_ / title_id / kGameUserContentDirName / user_name;
|
||||
}
|
||||
|
||||
} // namespace xam
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2015 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include "xenia/base/memory.h"
|
||||
#include "xenia/base/mutex.h"
|
||||
#include "xenia/base/string_key.h"
|
||||
#include "xenia/xbox.h"
|
||||
|
||||
namespace xe {
|
||||
@@ -33,29 +34,30 @@ struct XCONTENT_DATA {
|
||||
static const size_t kSize = 4 + 4 + 128 * 2 + 42 + 2; // = 306 + 2b padding
|
||||
uint32_t device_id;
|
||||
uint32_t content_type;
|
||||
std::wstring display_name; // 128 chars
|
||||
std::u16string display_name; // 128 chars
|
||||
std::string file_name;
|
||||
|
||||
XCONTENT_DATA() = default;
|
||||
explicit XCONTENT_DATA(const uint8_t* ptr) {
|
||||
device_id = xe::load_and_swap<uint32_t>(ptr + 0);
|
||||
content_type = xe::load_and_swap<uint32_t>(ptr + 4);
|
||||
display_name = xe::load_and_swap<std::wstring>(ptr + 8);
|
||||
display_name = xe::load_and_swap<std::u16string>(ptr + 8);
|
||||
file_name = xe::load_and_swap<std::string>(ptr + 8 + 128 * 2);
|
||||
}
|
||||
|
||||
void Write(uint8_t* ptr) {
|
||||
xe::store_and_swap<uint32_t>(ptr + 0, device_id);
|
||||
xe::store_and_swap<uint32_t>(ptr + 4, content_type);
|
||||
xe::store_and_swap<std::wstring>(ptr + 8, display_name);
|
||||
xe::store_and_swap<std::u16string>(ptr + 8, display_name);
|
||||
xe::store_and_swap<std::string>(ptr + 8 + 128 * 2, file_name);
|
||||
}
|
||||
};
|
||||
|
||||
class ContentPackage {
|
||||
public:
|
||||
ContentPackage(KernelState* kernel_state, std::string root_name,
|
||||
const XCONTENT_DATA& data, std::wstring package_path);
|
||||
ContentPackage(KernelState* kernel_state, const std::string_view root_name,
|
||||
const XCONTENT_DATA& data,
|
||||
const std::filesystem::path& package_path);
|
||||
~ContentPackage();
|
||||
|
||||
private:
|
||||
@@ -66,36 +68,39 @@ class ContentPackage {
|
||||
|
||||
class ContentManager {
|
||||
public:
|
||||
ContentManager(KernelState* kernel_state, std::wstring root_path);
|
||||
ContentManager(KernelState* kernel_state,
|
||||
const std::filesystem::path& root_path);
|
||||
~ContentManager();
|
||||
|
||||
std::vector<XCONTENT_DATA> ListContent(uint32_t device_id,
|
||||
uint32_t content_type);
|
||||
|
||||
std::unique_ptr<ContentPackage> ResolvePackage(std::string root_name,
|
||||
const XCONTENT_DATA& data);
|
||||
std::unique_ptr<ContentPackage> ResolvePackage(
|
||||
const std::string_view root_name, const XCONTENT_DATA& data);
|
||||
|
||||
bool ContentExists(const XCONTENT_DATA& data);
|
||||
X_RESULT CreateContent(std::string root_name, const XCONTENT_DATA& data);
|
||||
X_RESULT OpenContent(std::string root_name, const XCONTENT_DATA& data);
|
||||
X_RESULT CloseContent(std::string root_name);
|
||||
X_RESULT CreateContent(const std::string_view root_name,
|
||||
const XCONTENT_DATA& data);
|
||||
X_RESULT OpenContent(const std::string_view root_name,
|
||||
const XCONTENT_DATA& data);
|
||||
X_RESULT CloseContent(const std::string_view root_name);
|
||||
X_RESULT GetContentThumbnail(const XCONTENT_DATA& data,
|
||||
std::vector<uint8_t>* buffer);
|
||||
X_RESULT SetContentThumbnail(const XCONTENT_DATA& data,
|
||||
std::vector<uint8_t> buffer);
|
||||
X_RESULT DeleteContent(const XCONTENT_DATA& data);
|
||||
std::wstring ResolveGameUserContentPath();
|
||||
std::filesystem::path ResolveGameUserContentPath();
|
||||
|
||||
private:
|
||||
std::wstring ResolvePackageRoot(uint32_t content_type);
|
||||
std::wstring ResolvePackagePath(const XCONTENT_DATA& data);
|
||||
std::filesystem::path ResolvePackageRoot(uint32_t content_type);
|
||||
std::filesystem::path ResolvePackagePath(const XCONTENT_DATA& data);
|
||||
|
||||
KernelState* kernel_state_;
|
||||
std::wstring root_path_;
|
||||
std::filesystem::path root_path_;
|
||||
|
||||
// TODO(benvanik): remove use of global lock, it's bad here!
|
||||
xe::global_critical_region global_critical_region_;
|
||||
std::unordered_map<std::string, ContentPackage*> open_packages_;
|
||||
std::unordered_map<string_key, ContentPackage*> open_packages_;
|
||||
};
|
||||
|
||||
} // namespace xam
|
||||
|
||||
@@ -2,16 +2,18 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2014 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/kernel/xam/user_profile.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "third_party/fmt/include/fmt/format.h"
|
||||
#include "xenia/kernel/kernel_state.h"
|
||||
#include "xenia/kernel/util/shim_utils.h"
|
||||
#include "xenia/kernel/xam/user_profile.h"
|
||||
|
||||
namespace xe {
|
||||
namespace kernel {
|
||||
@@ -42,7 +44,7 @@ UserProfile::UserProfile() {
|
||||
// XPROFILE_OPTION_VOICE_VOLUME
|
||||
AddSetting(std::make_unique<Int32Setting>(0x1004000E, 0x64));
|
||||
// XPROFILE_GAMERCARD_MOTTO
|
||||
AddSetting(std::make_unique<UnicodeSetting>(0x402C0011, L""));
|
||||
AddSetting(std::make_unique<UnicodeSetting>(0x402C0011, u""));
|
||||
// XPROFILE_GAMERCARD_TITLES_PLAYED
|
||||
AddSetting(std::make_unique<Int32Setting>(0x10040012, 1));
|
||||
// XPROFILE_GAMERCARD_ACHIEVEMENTS_EARNED
|
||||
@@ -77,7 +79,7 @@ UserProfile::UserProfile() {
|
||||
// If we set this, games will try to get it.
|
||||
// XPROFILE_GAMERCARD_PICTURE_KEY
|
||||
AddSetting(
|
||||
std::make_unique<UnicodeSetting>(0x4064000F, L"gamercard_picture_key"));
|
||||
std::make_unique<UnicodeSetting>(0x4064000F, u"gamercard_picture_key"));
|
||||
|
||||
// XPROFILE_TITLE_SPECIFIC1
|
||||
AddSetting(std::make_unique<BinarySetting>(0x63E83FFF));
|
||||
@@ -130,8 +132,8 @@ void UserProfile::LoadSetting(UserProfile::Setting* setting) {
|
||||
if (setting->is_title_specific()) {
|
||||
auto content_dir =
|
||||
kernel_state()->content_manager()->ResolveGameUserContentPath();
|
||||
auto setting_id = xe::format_string(L"%.8X", setting->setting_id);
|
||||
auto file_path = xe::join_paths(content_dir, setting_id);
|
||||
auto setting_id = fmt::format("{:08X}", setting->setting_id);
|
||||
auto file_path = content_dir / setting_id;
|
||||
auto file = xe::filesystem::OpenFile(file_path, "rb");
|
||||
if (file) {
|
||||
fseek(file, 0, SEEK_END);
|
||||
@@ -157,8 +159,8 @@ void UserProfile::SaveSetting(UserProfile::Setting* setting) {
|
||||
auto content_dir =
|
||||
kernel_state()->content_manager()->ResolveGameUserContentPath();
|
||||
xe::filesystem::CreateFolder(content_dir);
|
||||
auto setting_id = xe::format_string(L"%.8X", setting->setting_id);
|
||||
auto file_path = xe::join_paths(content_dir, setting_id);
|
||||
auto setting_id = fmt::format("{:08X}", setting->setting_id);
|
||||
auto file_path = content_dir / setting_id;
|
||||
auto file = xe::filesystem::OpenFile(file_path, "wb");
|
||||
fwrite(serialized_setting.data(), 1, serialized_setting.size(), file);
|
||||
fclose(file);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2014 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -110,9 +110,9 @@ class UserProfile {
|
||||
}
|
||||
};
|
||||
struct UnicodeSetting : public Setting {
|
||||
UnicodeSetting(uint32_t setting_id, const std::wstring& value)
|
||||
UnicodeSetting(uint32_t setting_id, const std::u16string& value)
|
||||
: Setting(setting_id, Type::WSTRING, 8, true), value(value) {}
|
||||
std::wstring value;
|
||||
std::u16string value;
|
||||
size_t extra_size() const override {
|
||||
return value.empty() ? 0 : 2 * (static_cast<int32_t>(value.size()) + 1);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -31,7 +31,7 @@ struct DeviceInfo {
|
||||
uint32_t device_type;
|
||||
uint64_t total_bytes;
|
||||
uint64_t free_bytes;
|
||||
wchar_t name[28];
|
||||
char16_t name[28];
|
||||
};
|
||||
|
||||
// TODO(gibbed): real information.
|
||||
@@ -48,7 +48,7 @@ static const DeviceInfo dummy_device_info_ = {
|
||||
0xF00D0000, 1,
|
||||
4ull * ONE_GB, // 4GB
|
||||
3ull * ONE_GB, // 3GB, so it looks a little used.
|
||||
L"Dummy HDD",
|
||||
u"Dummy HDD",
|
||||
};
|
||||
#undef ONE_GB
|
||||
|
||||
@@ -70,19 +70,19 @@ dword_result_t XamContentGetLicenseMask(lpdword_t mask_ptr,
|
||||
DECLARE_XAM_EXPORT2(XamContentGetLicenseMask, kContent, kStub, kHighFrequency);
|
||||
|
||||
dword_result_t XamContentGetDeviceName(dword_t device_id,
|
||||
lpwstring_t name_buffer,
|
||||
lpu16string_t name_buffer,
|
||||
dword_t name_capacity) {
|
||||
if ((device_id & 0xFFFF0000) != dummy_device_info_.device_id) {
|
||||
return X_ERROR_DEVICE_NOT_CONNECTED;
|
||||
}
|
||||
|
||||
auto name = std::wstring(dummy_device_info_.name);
|
||||
auto name = std::u16string(dummy_device_info_.name);
|
||||
if (name_capacity < name.size() + 1) {
|
||||
return X_ERROR_INSUFFICIENT_BUFFER;
|
||||
}
|
||||
|
||||
xe::store_and_swap<std::wstring>(name_buffer, name);
|
||||
((wchar_t*)name_buffer)[name.size()] = 0;
|
||||
xe::store_and_swap<std::u16string>(name_buffer, name);
|
||||
((char16_t*)name_buffer)[name.size()] = 0;
|
||||
return X_ERROR_SUCCESS;
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamContentGetDeviceName, kContent, kImplemented);
|
||||
@@ -132,7 +132,7 @@ dword_result_t XamContentGetDeviceData(
|
||||
device_data->unknown = device_id & 0xFFFF; // Fake it.
|
||||
device_data->total_bytes = device_info.total_bytes;
|
||||
device_data->free_bytes = device_info.free_bytes;
|
||||
xe::store_and_swap<std::wstring>(&device_data->name[0], device_info.name);
|
||||
xe::store_and_swap<std::u16string>(&device_data->name[0], device_info.name);
|
||||
return X_ERROR_SUCCESS;
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamContentGetDeviceData, kContent, kImplemented);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2019 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -21,6 +21,8 @@
|
||||
#include "xenia/base/platform_win.h"
|
||||
#endif
|
||||
|
||||
#include "third_party/fmt/include/fmt/format.h"
|
||||
|
||||
namespace xe {
|
||||
namespace kernel {
|
||||
namespace xam {
|
||||
@@ -56,54 +58,48 @@ dword_result_t XamGetOnlineSchema() {
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamGetOnlineSchema, kNone, kImplemented);
|
||||
|
||||
void XamFormatDateString(dword_t unk, qword_t filetime, lpvoid_t buffer,
|
||||
dword_t buffer_length) {
|
||||
std::memset(buffer, 0, buffer_length * 2);
|
||||
|
||||
// TODO: implement this for other platforms
|
||||
#if XE_PLATFORM_WIN32
|
||||
static SYSTEMTIME xeGetLocalSystemTime(uint64_t filetime) {
|
||||
FILETIME t;
|
||||
t.dwHighDateTime = filetime >> 32;
|
||||
t.dwLowDateTime = (uint32_t)filetime;
|
||||
|
||||
SYSTEMTIME st;
|
||||
SYSTEMTIME stLocal;
|
||||
|
||||
SYSTEMTIME local_st;
|
||||
FileTimeToSystemTime(&t, &st);
|
||||
SystemTimeToTzSpecificLocalTime(NULL, &st, &stLocal);
|
||||
SystemTimeToTzSpecificLocalTime(NULL, &st, &local_st);
|
||||
return local_st;
|
||||
}
|
||||
#endif
|
||||
|
||||
wchar_t buf[256];
|
||||
void XamFormatDateString(dword_t unk, qword_t filetime, lpvoid_t output_buffer,
|
||||
dword_t output_count) {
|
||||
std::memset(output_buffer, 0, output_count * 2);
|
||||
|
||||
// TODO: implement this for other platforms
|
||||
#if XE_PLATFORM_WIN32
|
||||
auto st = xeGetLocalSystemTime(filetime);
|
||||
// TODO: format this depending on users locale?
|
||||
swprintf(buf, 256, L"%02d/%02d/%d", stLocal.wMonth, stLocal.wDay,
|
||||
stLocal.wYear);
|
||||
|
||||
xe::copy_and_swap((wchar_t*)buffer.host_address(), buf, buffer_length);
|
||||
auto str = fmt::format(u"{:02d}/{:02d}/{}", st.wMonth, st.wDay, st.wYear);
|
||||
auto copy_length = std::min(size_t(output_count), str.size()) * 2;
|
||||
xe::copy_and_swap(output_buffer.as<char16_t*>(), str.c_str(), copy_length);
|
||||
#else
|
||||
assert_always();
|
||||
#endif
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamFormatDateString, kNone, kImplemented);
|
||||
|
||||
void XamFormatTimeString(dword_t unk, qword_t filetime, lpvoid_t buffer,
|
||||
dword_t buffer_length) {
|
||||
std::memset(buffer, 0, buffer_length * 2);
|
||||
void XamFormatTimeString(dword_t unk, qword_t filetime, lpvoid_t output_buffer,
|
||||
dword_t output_count) {
|
||||
std::memset(output_buffer, 0, output_count * 2);
|
||||
|
||||
// TODO: implement this for other platforms
|
||||
#if XE_PLATFORM_WIN32
|
||||
FILETIME t;
|
||||
t.dwHighDateTime = filetime >> 32;
|
||||
t.dwLowDateTime = (uint32_t)filetime;
|
||||
|
||||
SYSTEMTIME st;
|
||||
SYSTEMTIME stLocal;
|
||||
|
||||
FileTimeToSystemTime(&t, &st);
|
||||
SystemTimeToTzSpecificLocalTime(NULL, &st, &stLocal);
|
||||
|
||||
wchar_t buf[256];
|
||||
swprintf(buf, 256, L"%02d:%02d", stLocal.wHour, stLocal.wMinute);
|
||||
|
||||
xe::copy_and_swap((wchar_t*)buffer.host_address(), buf, buffer_length);
|
||||
auto st = xeGetLocalSystemTime(filetime);
|
||||
// TODO: format this depending on users locale?
|
||||
auto str = fmt::format(u"{:02d}:{:02d}", st.wHour, st.wMinute);
|
||||
auto copy_count = std::min(size_t(output_count), str.size());
|
||||
xe::copy_and_swap(output_buffer.as<char16_t*>(), str.c_str(), copy_count);
|
||||
#else
|
||||
assert_always();
|
||||
#endif
|
||||
@@ -111,38 +107,36 @@ void XamFormatTimeString(dword_t unk, qword_t filetime, lpvoid_t buffer,
|
||||
DECLARE_XAM_EXPORT1(XamFormatTimeString, kNone, kImplemented);
|
||||
|
||||
dword_result_t keXamBuildResourceLocator(uint64_t module,
|
||||
const wchar_t* container,
|
||||
const wchar_t* resource,
|
||||
lpvoid_t buffer,
|
||||
uint32_t buffer_length) {
|
||||
wchar_t buf[256];
|
||||
|
||||
const std::u16string& container,
|
||||
const std::u16string& resource,
|
||||
lpvoid_t buffer_ptr,
|
||||
uint32_t buffer_count) {
|
||||
std::u16string path;
|
||||
if (!module) {
|
||||
swprintf(buf, 256, L"file://media:/%s.xzp#%s", container, resource);
|
||||
XELOGD(
|
||||
"XamBuildResourceLocator(%ws) returning locator to local file %ws.xzp",
|
||||
container, container);
|
||||
path = fmt::format(u"file://media:/{0}.xzp#{0}", container, resource);
|
||||
XELOGD("XamBuildResourceLocator(%s) returning locator to local file %s.xzp",
|
||||
xe::to_utf8(container).c_str(), xe::to_utf8(container).c_str());
|
||||
} else {
|
||||
swprintf(buf, 256, L"section://%X,%s#%s", (uint32_t)module, container,
|
||||
resource);
|
||||
path = fmt::format(u"section://{:X},{}#{}", (uint32_t)module, container,
|
||||
resource);
|
||||
}
|
||||
|
||||
xe::copy_and_swap((wchar_t*)buffer.host_address(), buf, buffer_length);
|
||||
auto copy_count = std::min(size_t(buffer_count), path.size());
|
||||
xe::copy_and_swap(buffer_ptr.as<char16_t*>(), path.c_str(), copy_count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dword_result_t XamBuildResourceLocator(qword_t module, lpwstring_t container,
|
||||
lpwstring_t resource, lpvoid_t buffer,
|
||||
dword_t buffer_length) {
|
||||
return keXamBuildResourceLocator(module, container.value().c_str(),
|
||||
resource.value().c_str(), buffer,
|
||||
buffer_length);
|
||||
dword_result_t XamBuildResourceLocator(qword_t module, lpu16string_t container,
|
||||
lpu16string_t resource,
|
||||
lpvoid_t buffer_ptr,
|
||||
dword_t buffer_count) {
|
||||
return keXamBuildResourceLocator(module, container.value(), resource.value(),
|
||||
buffer_ptr, buffer_count);
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamBuildResourceLocator, kNone, kImplemented);
|
||||
|
||||
dword_result_t XamBuildGamercardResourceLocator(lpwstring_t filename,
|
||||
lpvoid_t buffer,
|
||||
dword_t buffer_length) {
|
||||
dword_result_t XamBuildGamercardResourceLocator(lpu16string_t filename,
|
||||
lpvoid_t buffer_ptr,
|
||||
dword_t buffer_count) {
|
||||
// On an actual xbox these funcs would return a locator to xam.xex resources,
|
||||
// but for Xenia we can return a locator to the resources as local files. (big
|
||||
// thanks to MS for letting XamBuildResourceLocator return local file
|
||||
@@ -151,31 +145,33 @@ dword_result_t XamBuildGamercardResourceLocator(lpwstring_t filename,
|
||||
// If you're running an app that'll need them, make sure to extract xam.xex
|
||||
// resources with xextool ("xextool -d . xam.xex") and add a .xzp extension.
|
||||
|
||||
return keXamBuildResourceLocator(0, L"gamercrd", filename.value().c_str(),
|
||||
buffer, buffer_length);
|
||||
return keXamBuildResourceLocator(0, u"gamercrd", filename.value(), buffer_ptr,
|
||||
buffer_count);
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamBuildGamercardResourceLocator, kNone, kImplemented);
|
||||
|
||||
dword_result_t XamBuildSharedSystemResourceLocator(lpwstring_t filename,
|
||||
lpvoid_t buffer,
|
||||
dword_t buffer_length) {
|
||||
dword_result_t XamBuildSharedSystemResourceLocator(lpu16string_t filename,
|
||||
lpvoid_t buffer_ptr,
|
||||
dword_t buffer_count) {
|
||||
// see notes inside XamBuildGamercardResourceLocator above
|
||||
return keXamBuildResourceLocator(0, L"shrdres", filename.value().c_str(),
|
||||
buffer, buffer_length);
|
||||
return keXamBuildResourceLocator(0, u"shrdres", filename.value(), buffer_ptr,
|
||||
buffer_count);
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamBuildSharedSystemResourceLocator, kNone, kImplemented);
|
||||
|
||||
dword_result_t XamBuildLegacySystemResourceLocator(lpwstring_t filename,
|
||||
lpvoid_t buffer,
|
||||
dword_t buffer_length) {
|
||||
return XamBuildSharedSystemResourceLocator(filename, buffer, buffer_length);
|
||||
dword_result_t XamBuildLegacySystemResourceLocator(lpu16string_t filename,
|
||||
lpvoid_t buffer_ptr,
|
||||
dword_t buffer_count) {
|
||||
return XamBuildSharedSystemResourceLocator(filename, buffer_ptr,
|
||||
buffer_count);
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamBuildLegacySystemResourceLocator, kNone, kImplemented);
|
||||
|
||||
dword_result_t XamBuildXamResourceLocator(lpwstring_t filename, lpvoid_t buffer,
|
||||
dword_t buffer_length) {
|
||||
return keXamBuildResourceLocator(0, L"xam", filename.value().c_str(), buffer,
|
||||
buffer_length);
|
||||
dword_result_t XamBuildXamResourceLocator(lpu16string_t filename,
|
||||
lpvoid_t buffer_ptr,
|
||||
dword_t buffer_count) {
|
||||
return keXamBuildResourceLocator(0, u"xam", filename.value(), buffer_ptr,
|
||||
buffer_count);
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamBuildXamResourceLocator, kNone, kImplemented);
|
||||
|
||||
@@ -285,25 +281,26 @@ dword_result_t XamLoaderGetLaunchData(lpvoid_t buffer_ptr,
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamLoaderGetLaunchData, kNone, kSketchy);
|
||||
|
||||
void XamLoaderLaunchTitle(lpstring_t raw_name, dword_t flags) {
|
||||
void XamLoaderLaunchTitle(lpstring_t raw_name_ptr, dword_t flags) {
|
||||
auto xam = kernel_state()->GetKernelModule<XamModule>("xam.xex");
|
||||
|
||||
auto& loader_data = xam->loader_data();
|
||||
loader_data.launch_flags = flags;
|
||||
|
||||
// Translate the launch path to a full path.
|
||||
if (raw_name && raw_name.value() == "") {
|
||||
loader_data.launch_path = "game:\\default.xex";
|
||||
} else if (raw_name) {
|
||||
std::string name = xe::find_name_from_path(std::string(raw_name));
|
||||
std::string path(raw_name);
|
||||
if (name == std::string(raw_name)) {
|
||||
path = xe::join_paths(
|
||||
xe::find_base_path(kernel_state()->GetExecutableModule()->path()),
|
||||
name);
|
||||
if (raw_name_ptr) {
|
||||
auto path = raw_name_ptr.value();
|
||||
if (path.empty()) {
|
||||
loader_data.launch_path = "game:\\default.xex";
|
||||
} else {
|
||||
if (xe::utf8::find_name_from_guest_path(path) == path) {
|
||||
path = xe::utf8::join_guest_paths(
|
||||
xe::utf8::find_base_guest_path(
|
||||
kernel_state()->GetExecutableModule()->path()),
|
||||
path);
|
||||
}
|
||||
loader_data.launch_path = path;
|
||||
}
|
||||
|
||||
loader_data.launch_path = path;
|
||||
} else {
|
||||
assert_always("Game requested exit to dashboard via XamLoaderLaunchTitle");
|
||||
}
|
||||
@@ -422,7 +419,7 @@ dword_result_t XamGetPrivateEnumStructureFromHandle(unknown_t unk1,
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamGetPrivateEnumStructureFromHandle, kNone, kStub);
|
||||
|
||||
dword_result_t XamQueryLiveHiveW(lpwstring_t name, lpvoid_t out_buf,
|
||||
dword_result_t XamQueryLiveHiveW(lpu16string_t name, lpvoid_t out_buf,
|
||||
dword_t out_size, dword_t type /* guess */) {
|
||||
return X_STATUS_INVALID_PARAMETER_1;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2019 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -45,75 +45,75 @@ uint8_t xeXamGetOnlineCountryFromLocale(uint8_t id) {
|
||||
return id < xe::countof(table) ? table[id] : 0;
|
||||
}
|
||||
|
||||
const wchar_t* xeXamGetOnlineCountryString(uint8_t id) {
|
||||
static const wchar_t* const table[] = {
|
||||
L"ZZ", L"AE", L"AL", L"AM", L"AR", L"AT", L"AU", L"AZ", L"BE",
|
||||
L"BG", L"BH", L"BN", L"BO", L"BR", L"BY", L"BZ", L"CA", nullptr,
|
||||
L"CH", L"CL", L"CN", L"CO", L"CR", L"CZ", L"DE", L"DK", L"DO",
|
||||
L"DZ", L"EC", L"EE", L"EG", L"ES", L"FI", L"FO", L"FR", L"GB",
|
||||
L"GE", L"GR", L"GT", L"HK", L"HN", L"HR", L"HU", L"ID", L"IE",
|
||||
L"IL", L"IN", L"IQ", L"IR", L"IS", L"IT", L"JM", L"JO", L"JP",
|
||||
L"KE", L"KG", L"KR", L"KW", L"KZ", L"LB", L"LI", L"LT", L"LU",
|
||||
L"LV", L"LY", L"MA", L"MC", L"MK", L"MN", L"MO", L"MV", L"MX",
|
||||
L"MY", L"NI", L"NL", L"NO", L"NZ", L"OM", L"PA", L"PE", L"PH",
|
||||
L"PK", L"PL", L"PR", L"PT", L"PY", L"QA", L"RO", L"RU", L"SA",
|
||||
L"SE", L"SG", L"SI", L"SK", nullptr, L"SV", L"SY", L"TH", L"TN",
|
||||
L"TR", L"TT", L"TW", L"UA", L"US", L"UY", L"UZ", L"VE", L"VN",
|
||||
L"YE", L"ZA", L"ZW", L"AF", nullptr, L"AD", L"AO", L"AI", nullptr,
|
||||
L"AG", L"AW", L"BS", L"BD", L"BB", L"BJ", L"BM", L"BT", L"BA",
|
||||
L"BW", L"BF", L"BI", L"KH", L"CM", L"CV", L"KY", L"CF", L"TD",
|
||||
L"CX", L"CC", L"KM", L"CG", L"CD", L"CK", L"CI", L"CY", L"DJ",
|
||||
L"DM", nullptr, L"GQ", L"ER", L"ET", L"FK", L"FJ", L"GF", L"PF",
|
||||
L"GA", L"GM", L"GH", L"GI", L"GL", L"GD", L"GP", nullptr, L"GG",
|
||||
L"GN", L"GW", L"GY", L"HT", L"JE", L"KI", L"LA", L"LS", L"LR",
|
||||
L"MG", L"MW", L"ML", L"MT", L"MH", L"MQ", L"MR", L"MU", L"YT",
|
||||
L"FM", L"MD", L"ME", L"MS", L"MZ", L"MM", L"NA", L"NR", L"NP",
|
||||
L"AN", L"NC", L"NE", L"NG", L"NU", L"NF", nullptr, L"PW", L"PS",
|
||||
L"PG", L"PN", L"RE", L"RW", L"WS", L"SM", L"ST", L"SN", L"RS",
|
||||
L"SC", L"SL", L"SB", L"SO", L"LK", L"SH", L"KN", L"LC", L"PM",
|
||||
L"VC", L"SR", L"SZ", L"TJ", L"TZ", L"TL", L"TG", L"TK", L"TO",
|
||||
L"TM", L"TC", L"TV", L"UG", L"VU", L"VA", nullptr, L"VG", L"WF",
|
||||
L"EH", L"ZM", L"ZZ",
|
||||
const char16_t* xeXamGetOnlineCountryString(uint8_t id) {
|
||||
static const char16_t* const table[] = {
|
||||
u"ZZ", u"AE", u"AL", u"AM", u"AR", u"AT", u"AU", u"AZ", u"BE",
|
||||
u"BG", u"BH", u"BN", u"BO", u"BR", u"BY", u"BZ", u"CA", nullptr,
|
||||
u"CH", u"CL", u"CN", u"CO", u"CR", u"CZ", u"DE", u"DK", u"DO",
|
||||
u"DZ", u"EC", u"EE", u"EG", u"ES", u"FI", u"FO", u"FR", u"GB",
|
||||
u"GE", u"GR", u"GT", u"HK", u"HN", u"HR", u"HU", u"ID", u"IE",
|
||||
u"IL", u"IN", u"IQ", u"IR", u"IS", u"IT", u"JM", u"JO", u"JP",
|
||||
u"KE", u"KG", u"KR", u"KW", u"KZ", u"LB", u"LI", u"LT", u"LU",
|
||||
u"LV", u"LY", u"MA", u"MC", u"MK", u"MN", u"MO", u"MV", u"MX",
|
||||
u"MY", u"NI", u"NL", u"NO", u"NZ", u"OM", u"PA", u"PE", u"PH",
|
||||
u"PK", u"PL", u"PR", u"PT", u"PY", u"QA", u"RO", u"RU", u"SA",
|
||||
u"SE", u"SG", u"SI", u"SK", nullptr, u"SV", u"SY", u"TH", u"TN",
|
||||
u"TR", u"TT", u"TW", u"UA", u"US", u"UY", u"UZ", u"VE", u"VN",
|
||||
u"YE", u"ZA", u"ZW", u"AF", nullptr, u"AD", u"AO", u"AI", nullptr,
|
||||
u"AG", u"AW", u"BS", u"BD", u"BB", u"BJ", u"BM", u"BT", u"BA",
|
||||
u"BW", u"BF", u"BI", u"KH", u"CM", u"CV", u"KY", u"CF", u"TD",
|
||||
u"CX", u"CC", u"KM", u"CG", u"CD", u"CK", u"CI", u"CY", u"DJ",
|
||||
u"DM", nullptr, u"GQ", u"ER", u"ET", u"FK", u"FJ", u"GF", u"PF",
|
||||
u"GA", u"GM", u"GH", u"GI", u"GL", u"GD", u"GP", nullptr, u"GG",
|
||||
u"GN", u"GW", u"GY", u"HT", u"JE", u"KI", u"LA", u"LS", u"LR",
|
||||
u"MG", u"MW", u"ML", u"MT", u"MH", u"MQ", u"MR", u"MU", u"YT",
|
||||
u"FM", u"MD", u"ME", u"MS", u"MZ", u"MM", u"NA", u"NR", u"NP",
|
||||
u"AN", u"NC", u"NE", u"NG", u"NU", u"NF", nullptr, u"PW", u"PS",
|
||||
u"PG", u"PN", u"RE", u"RW", u"WS", u"SM", u"ST", u"SN", u"RS",
|
||||
u"SC", u"SL", u"SB", u"SO", u"LK", u"SH", u"KN", u"LC", u"PM",
|
||||
u"VC", u"SR", u"SZ", u"TJ", u"TZ", u"TL", u"TG", u"TK", u"TO",
|
||||
u"TM", u"TC", u"TV", u"UG", u"VU", u"VA", nullptr, u"VG", u"WF",
|
||||
u"EH", u"ZM", u"ZZ",
|
||||
};
|
||||
#pragma warning(suppress : 6385)
|
||||
return id < xe::countof(table) ? table[id] : nullptr;
|
||||
}
|
||||
|
||||
const wchar_t* xeXamGetCountryString(uint8_t id) {
|
||||
static const wchar_t* const table[] = {
|
||||
L"ZZ", L"AE", L"AL", L"AM", L"AR", L"AT", L"AU", L"AZ", L"BE", L"BG",
|
||||
L"BH", L"BN", L"BO", L"BR", L"BY", L"BZ", L"CA", nullptr, L"CH", L"CL",
|
||||
L"CN", L"CO", L"CR", L"CZ", L"DE", L"DK", L"DO", L"DZ", L"EC", L"EE",
|
||||
L"EG", L"ES", L"FI", L"FO", L"FR", L"GB", L"GE", L"GR", L"GT", L"HK",
|
||||
L"HN", L"HR", L"HU", L"ID", L"IE", L"IL", L"IN", L"IQ", L"IR", L"IS",
|
||||
L"IT", L"JM", L"JO", L"JP", L"KE", L"KG", L"KR", L"KW", L"KZ", L"LB",
|
||||
L"LI", L"LT", L"LU", L"LV", L"LY", L"MA", L"MC", L"MK", L"MN", L"MO",
|
||||
L"MV", L"MX", L"MY", L"NI", L"NL", L"NO", L"NZ", L"OM", L"PA", L"PE",
|
||||
L"PH", L"PK", L"PL", L"PR", L"PT", L"PY", L"QA", L"RO", L"RU", L"SA",
|
||||
L"SE", L"SG", L"SI", L"SK", nullptr, L"SV", L"SY", L"TH", L"TN", L"TR",
|
||||
L"TT", L"TW", L"UA", L"US", L"UY", L"UZ", L"VE", L"VN", L"YE", L"ZA",
|
||||
L"ZW", L"ZZ",
|
||||
const char16_t* xeXamGetCountryString(uint8_t id) {
|
||||
static const char16_t* const table[] = {
|
||||
u"ZZ", u"AE", u"AL", u"AM", u"AR", u"AT", u"AU", u"AZ", u"BE", u"BG",
|
||||
u"BH", u"BN", u"BO", u"BR", u"BY", u"BZ", u"CA", nullptr, u"CH", u"CL",
|
||||
u"CN", u"CO", u"CR", u"CZ", u"DE", u"DK", u"DO", u"DZ", u"EC", u"EE",
|
||||
u"EG", u"ES", u"FI", u"FO", u"FR", u"GB", u"GE", u"GR", u"GT", u"HK",
|
||||
u"HN", u"HR", u"HU", u"ID", u"IE", u"IL", u"IN", u"IQ", u"IR", u"IS",
|
||||
u"IT", u"JM", u"JO", u"JP", u"KE", u"KG", u"KR", u"KW", u"KZ", u"LB",
|
||||
u"LI", u"LT", u"LU", u"LV", u"LY", u"MA", u"MC", u"MK", u"MN", u"MO",
|
||||
u"MV", u"MX", u"MY", u"NI", u"NL", u"NO", u"NZ", u"OM", u"PA", u"PE",
|
||||
u"PH", u"PK", u"PL", u"PR", u"PT", u"PY", u"QA", u"RO", u"RU", u"SA",
|
||||
u"SE", u"SG", u"SI", u"SK", nullptr, u"SV", u"SY", u"TH", u"TN", u"TR",
|
||||
u"TT", u"TW", u"UA", u"US", u"UY", u"UZ", u"VE", u"VN", u"YE", u"ZA",
|
||||
u"ZW", u"ZZ",
|
||||
};
|
||||
#pragma warning(suppress : 6385)
|
||||
return id < xe::countof(table) ? table[id] : nullptr;
|
||||
}
|
||||
|
||||
const wchar_t* xeXamGetLanguageString(uint8_t id) {
|
||||
static const wchar_t* const table[] = {
|
||||
L"zz", L"en", L"ja", L"de", L"fr", L"es", L"it", L"ko", L"zh",
|
||||
L"pt", nullptr, L"pl", L"ru", L"sv", L"tr", L"nb", L"nl", L"zh",
|
||||
const char16_t* xeXamGetLanguageString(uint8_t id) {
|
||||
static const char16_t* const table[] = {
|
||||
u"zz", u"en", u"ja", u"de", u"fr", u"es", u"it", u"ko", u"zh",
|
||||
u"pt", nullptr, u"pl", u"ru", u"sv", u"tr", u"nb", u"nl", u"zh",
|
||||
};
|
||||
#pragma warning(suppress : 6385)
|
||||
return id < xe::countof(table) ? table[id] : nullptr;
|
||||
}
|
||||
|
||||
const wchar_t* xeXamGetLocaleString(uint8_t id) {
|
||||
static const wchar_t* const table[] = {
|
||||
L"ZZ", L"AU", L"AT", L"BE", L"BR", L"CA", L"CL", L"CN", L"CO",
|
||||
L"CZ", L"DK", L"FI", L"FR", L"DE", L"GR", L"HK", L"HU", L"IN",
|
||||
L"IE", L"IT", L"JP", L"KR", L"MX", L"NL", L"NZ", L"NO", L"PL",
|
||||
L"PT", L"SG", L"SK", L"ZA", L"ES", L"SE", L"CH", L"TW", L"GB",
|
||||
L"US", L"RU", L"ZZ", L"TR", L"AR", L"SA", L"IL", L"AE",
|
||||
const char16_t* xeXamGetLocaleString(uint8_t id) {
|
||||
static const char16_t* const table[] = {
|
||||
u"ZZ", u"AU", u"AT", u"BE", u"BR", u"CA", u"CL", u"CN", u"CO",
|
||||
u"CZ", u"DK", u"FI", u"FR", u"DE", u"GR", u"HK", u"HU", u"IN",
|
||||
u"IE", u"IT", u"JP", u"KR", u"MX", u"NL", u"NZ", u"NO", u"PL",
|
||||
u"PT", u"SG", u"SK", u"ZA", u"ES", u"SE", u"CH", u"TW", u"GB",
|
||||
u"US", u"RU", u"ZZ", u"TR", u"AR", u"SA", u"IL", u"AE",
|
||||
};
|
||||
#pragma warning(suppress : 6385)
|
||||
return id < xe::countof(table) ? table[id] : nullptr;
|
||||
@@ -149,15 +149,15 @@ uint8_t xeXamGetLanguageFromOnlineLanguage(uint8_t id) {
|
||||
return id < xe::countof(table) ? table[id] : 0;
|
||||
}
|
||||
|
||||
const wchar_t* xeXamGetOnlineLanguageString(uint8_t id) {
|
||||
static const wchar_t* const table[] = {
|
||||
L"zz", L"en", L"ja", L"de", L"fr", L"es", L"it", L"ko", L"zh",
|
||||
L"pt", L"zh", L"pl", L"ru", L"da", L"fi", L"nb", L"nl", L"sv",
|
||||
L"cs", L"el", L"hu", L"sk", L"id", L"ms", L"ar", L"bg", L"et",
|
||||
L"hr", L"he", L"is", L"kk", L"lt", L"lv", L"ro", L"sl", L"th",
|
||||
L"tr", L"uk", L"vi", L"ps", L"sq", L"hy", L"bn", L"be", L"km",
|
||||
L"am", L"fo", L"ka", L"kl", L"sw", L"ky", L"lb", L"mk", L"mt",
|
||||
L"mn", L"ne", L"ur", L"rw", L"wo", L"si", L"tk",
|
||||
const char16_t* xeXamGetOnlineLanguageString(uint8_t id) {
|
||||
static const char16_t* const table[] = {
|
||||
u"zz", u"en", u"ja", u"de", u"fr", u"es", u"it", u"ko", u"zh",
|
||||
u"pt", u"zh", u"pl", u"ru", u"da", u"fi", u"nb", u"nl", u"sv",
|
||||
u"cs", u"el", u"hu", u"sk", u"id", u"ms", u"ar", u"bg", u"et",
|
||||
u"hr", u"he", u"is", u"kk", u"lt", u"lv", u"ro", u"sl", u"th",
|
||||
u"tr", u"uk", u"vi", u"ps", u"sq", u"hy", u"bn", u"be", u"km",
|
||||
u"am", u"fo", u"ka", u"kl", u"sw", u"ky", u"lb", u"mk", u"mt",
|
||||
u"mn", u"ne", u"ur", u"rw", u"wo", u"si", u"tk",
|
||||
};
|
||||
#pragma warning(suppress : 6385)
|
||||
return id < xe::countof(table) ? table[id] : nullptr;
|
||||
@@ -235,7 +235,7 @@ dword_result_t XamGetOnlineCountryFromLocale(dword_t id) {
|
||||
DECLARE_XAM_EXPORT1(XamGetOnlineCountryFromLocale, kLocale, kImplemented);
|
||||
|
||||
dword_result_t XamGetOnlineCountryString(dword_t id, dword_t buffer_length,
|
||||
lpwstring_t buffer) {
|
||||
lpu16string_t buffer) {
|
||||
if (buffer_length >= 0x80000000u) {
|
||||
return X_E_INVALIDARG;
|
||||
}
|
||||
@@ -245,19 +245,19 @@ dword_result_t XamGetOnlineCountryString(dword_t id, dword_t buffer_length,
|
||||
return X_E_NOTFOUND;
|
||||
}
|
||||
|
||||
const auto value = std::wstring(str);
|
||||
const auto value = std::u16string(str);
|
||||
if (value.size() + 1 > buffer_length) {
|
||||
return X_HRESULT_FROM_WIN32(X_ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
xe::store_and_swap<std::wstring>(buffer, value);
|
||||
static_cast<wchar_t*>(buffer)[value.size()] = 0;
|
||||
xe::store_and_swap<std::u16string>(buffer, value);
|
||||
static_cast<char16_t*>(buffer)[value.size()] = 0;
|
||||
return X_E_SUCCESS;
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamGetOnlineCountryString, kLocale, kImplemented);
|
||||
|
||||
dword_result_t XamGetCountryString(dword_t id, dword_t buffer_length,
|
||||
lpwstring_t buffer) {
|
||||
lpu16string_t buffer) {
|
||||
if (buffer_length >= 0x80000000u) {
|
||||
return X_E_INVALIDARG;
|
||||
}
|
||||
@@ -267,19 +267,19 @@ dword_result_t XamGetCountryString(dword_t id, dword_t buffer_length,
|
||||
return X_E_NOTFOUND;
|
||||
}
|
||||
|
||||
const auto value = std::wstring(str);
|
||||
const auto value = std::u16string(str);
|
||||
if (value.size() + 1 > buffer_length) {
|
||||
return X_HRESULT_FROM_WIN32(X_ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
xe::store_and_swap<std::wstring>(buffer, value);
|
||||
static_cast<wchar_t*>(buffer)[value.size()] = 0;
|
||||
xe::store_and_swap<std::u16string>(buffer, value);
|
||||
static_cast<char16_t*>(buffer)[value.size()] = 0;
|
||||
return X_E_SUCCESS;
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamGetCountryString, kLocale, kImplemented);
|
||||
|
||||
dword_result_t XamGetLanguageString(dword_t id, dword_t buffer_length,
|
||||
lpwstring_t buffer) {
|
||||
lpu16string_t buffer) {
|
||||
if (buffer_length >= 0x80000000u) {
|
||||
return X_E_INVALIDARG;
|
||||
}
|
||||
@@ -289,13 +289,13 @@ dword_result_t XamGetLanguageString(dword_t id, dword_t buffer_length,
|
||||
return X_E_NOTFOUND;
|
||||
}
|
||||
|
||||
const auto value = std::wstring(str);
|
||||
const auto value = std::u16string(str);
|
||||
if (value.size() + 1 > buffer_length) {
|
||||
return X_HRESULT_FROM_WIN32(X_ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
xe::store_and_swap<std::wstring>(buffer, value);
|
||||
static_cast<wchar_t*>(buffer)[value.size()] = 0;
|
||||
xe::store_and_swap<std::u16string>(buffer, value);
|
||||
static_cast<char16_t*>(buffer)[value.size()] = 0;
|
||||
return X_E_SUCCESS;
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamGetLanguageString, kLocale, kImplemented);
|
||||
@@ -303,7 +303,7 @@ DECLARE_XAM_EXPORT1(XamGetLanguageString, kLocale, kImplemented);
|
||||
dword_result_t XamGetLanguageLocaleString(dword_t language_id,
|
||||
dword_t locale_id,
|
||||
dword_t buffer_length,
|
||||
lpwstring_t buffer) {
|
||||
lpu16string_t buffer) {
|
||||
if (buffer_length >= 0x80000000u) {
|
||||
return X_E_INVALIDARG;
|
||||
}
|
||||
@@ -319,13 +319,13 @@ dword_result_t XamGetLanguageLocaleString(dword_t language_id,
|
||||
}
|
||||
|
||||
const auto value =
|
||||
std::wstring(language_str) + L"-" + std::wstring(locale_str);
|
||||
std::u16string(language_str) + u"-" + std::u16string(locale_str);
|
||||
if (value.size() + 1 > buffer_length) {
|
||||
return X_HRESULT_FROM_WIN32(X_ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
xe::store_and_swap<std::wstring>(buffer, value);
|
||||
static_cast<wchar_t*>(buffer)[value.size()] = 0;
|
||||
xe::store_and_swap<std::u16string>(buffer, value);
|
||||
static_cast<char16_t*>(buffer)[value.size()] = 0;
|
||||
return X_E_SUCCESS;
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamGetLanguageLocaleString, kLocale, kImplemented);
|
||||
@@ -333,7 +333,7 @@ DECLARE_XAM_EXPORT1(XamGetLanguageLocaleString, kLocale, kImplemented);
|
||||
dword_result_t XamGetOnlineLanguageAndCountryString(dword_t language_id,
|
||||
dword_t country_id,
|
||||
dword_t buffer_length,
|
||||
lpwstring_t buffer) {
|
||||
lpu16string_t buffer) {
|
||||
if (buffer_length >= 0x80000000u) {
|
||||
return X_E_INVALIDARG;
|
||||
}
|
||||
@@ -351,20 +351,20 @@ dword_result_t XamGetOnlineLanguageAndCountryString(dword_t language_id,
|
||||
}
|
||||
|
||||
const auto value =
|
||||
std::wstring(language_str) + L"-" + std::wstring(country_str);
|
||||
std::u16string(language_str) + u"-" + std::u16string(country_str);
|
||||
if (value.size() + 1 > buffer_length) {
|
||||
return X_HRESULT_FROM_WIN32(X_ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
xe::store_and_swap<std::wstring>(buffer, value);
|
||||
static_cast<wchar_t*>(buffer)[value.size()] = 0;
|
||||
xe::store_and_swap<std::u16string>(buffer, value);
|
||||
static_cast<char16_t*>(buffer)[value.size()] = 0;
|
||||
return X_E_SUCCESS;
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamGetOnlineLanguageAndCountryString, kLocale,
|
||||
kImplemented);
|
||||
|
||||
dword_result_t XamGetLocaleString(dword_t id, dword_t buffer_length,
|
||||
lpwstring_t buffer) {
|
||||
lpu16string_t buffer) {
|
||||
if (buffer_length >= 0x80000000u) {
|
||||
return X_E_INVALIDARG;
|
||||
}
|
||||
@@ -374,13 +374,13 @@ dword_result_t XamGetLocaleString(dword_t id, dword_t buffer_length,
|
||||
return X_E_NOTFOUND;
|
||||
}
|
||||
|
||||
const auto value = std::wstring(str);
|
||||
const auto value = std::u16string(str);
|
||||
if (value.size() + 1 > buffer_length) {
|
||||
return X_HRESULT_FROM_WIN32(X_ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
xe::store_and_swap<std::wstring>(buffer, value);
|
||||
static_cast<wchar_t*>(buffer)[value.size()] = 0;
|
||||
xe::store_and_swap<std::u16string>(buffer, value);
|
||||
static_cast<char16_t*>(buffer)[value.size()] = 0;
|
||||
return X_E_SUCCESS;
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamGetLocaleString, kLocale, kImplemented);
|
||||
@@ -396,7 +396,7 @@ dword_result_t XamGetLanguageFromOnlineLanguage(dword_t id) {
|
||||
DECLARE_XAM_EXPORT1(XamGetLanguageFromOnlineLanguage, kLocale, kImplemented);
|
||||
|
||||
dword_result_t XamGetOnlineLanguageString(dword_t id, dword_t buffer_length,
|
||||
lpwstring_t buffer) {
|
||||
lpu16string_t buffer) {
|
||||
if (buffer_length >= 0x80000000u) {
|
||||
return X_E_INVALIDARG;
|
||||
}
|
||||
@@ -406,13 +406,13 @@ dword_result_t XamGetOnlineLanguageString(dword_t id, dword_t buffer_length,
|
||||
return X_E_NOTFOUND;
|
||||
}
|
||||
|
||||
const auto value = std::wstring(str);
|
||||
const auto value = std::u16string(str);
|
||||
if (value.size() + 1 > buffer_length) {
|
||||
return X_HRESULT_FROM_WIN32(X_ERROR_INSUFFICIENT_BUFFER);
|
||||
}
|
||||
|
||||
xe::store_and_swap<std::wstring>(buffer, value);
|
||||
static_cast<wchar_t*>(buffer)[value.size()] = 0;
|
||||
xe::store_and_swap<std::u16string>(buffer, value);
|
||||
static_cast<char16_t*>(buffer)[value.size()] = 0;
|
||||
return X_E_SUCCESS;
|
||||
}
|
||||
DECLARE_XAM_EXPORT1(XamGetOnlineLanguageString, kLocale, kImplemented);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2014 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -29,12 +29,13 @@ DECLARE_XAM_EXPORT2(XamIsUIActive, kUI, kImplemented, kHighFrequency);
|
||||
|
||||
class MessageBoxDialog : public xe::ui::ImGuiDialog {
|
||||
public:
|
||||
MessageBoxDialog(xe::ui::Window* window, std::wstring title,
|
||||
std::wstring description, std::vector<std::wstring> buttons,
|
||||
uint32_t default_button, uint32_t* out_chosen_button)
|
||||
MessageBoxDialog(xe::ui::Window* window, std::u16string title,
|
||||
std::u16string description,
|
||||
std::vector<std::u16string> buttons, uint32_t default_button,
|
||||
uint32_t* out_chosen_button)
|
||||
: ImGuiDialog(window),
|
||||
title_(xe::to_string(title)),
|
||||
description_(xe::to_string(description)),
|
||||
title_(xe::to_utf8(title)),
|
||||
description_(xe::to_utf8(description)),
|
||||
buttons_(std::move(buttons)),
|
||||
default_button_(default_button),
|
||||
out_chosen_button_(out_chosen_button) {
|
||||
@@ -62,7 +63,7 @@ class MessageBoxDialog : public xe::ui::ImGuiDialog {
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
}
|
||||
for (size_t i = 0; i < buttons_.size(); ++i) {
|
||||
auto button_name = xe::to_string(buttons_[i]);
|
||||
auto button_name = xe::to_utf8(buttons_[i]);
|
||||
if (ImGui::Button(button_name.c_str())) {
|
||||
if (out_chosen_button_) {
|
||||
*out_chosen_button_ = static_cast<uint32_t>(i);
|
||||
@@ -84,34 +85,34 @@ class MessageBoxDialog : public xe::ui::ImGuiDialog {
|
||||
bool has_opened_ = false;
|
||||
std::string title_;
|
||||
std::string description_;
|
||||
std::vector<std::wstring> buttons_;
|
||||
std::vector<std::u16string> buttons_;
|
||||
uint32_t default_button_ = 0;
|
||||
uint32_t* out_chosen_button_ = nullptr;
|
||||
};
|
||||
|
||||
// https://www.se7ensins.com/forums/threads/working-xshowmessageboxui.844116/
|
||||
dword_result_t XamShowMessageBoxUI(dword_t user_index, lpwstring_t title_ptr,
|
||||
lpwstring_t text_ptr, dword_t button_count,
|
||||
dword_result_t XamShowMessageBoxUI(dword_t user_index, lpu16string_t title_ptr,
|
||||
lpu16string_t text_ptr, dword_t button_count,
|
||||
lpdword_t button_ptrs, dword_t active_button,
|
||||
dword_t flags, lpdword_t result_ptr,
|
||||
pointer_t<XAM_OVERLAPPED> overlapped) {
|
||||
std::wstring title;
|
||||
std::u16string title;
|
||||
if (title_ptr) {
|
||||
title = title_ptr.value();
|
||||
} else {
|
||||
title = L""; // TODO(gibbed): default title based on flags?
|
||||
title = u""; // TODO(gibbed): default title based on flags?
|
||||
}
|
||||
auto text = text_ptr.value();
|
||||
|
||||
std::vector<std::wstring> buttons;
|
||||
std::wstring all_buttons;
|
||||
std::vector<std::u16string> buttons;
|
||||
std::u16string all_buttons;
|
||||
for (uint32_t j = 0; j < button_count; ++j) {
|
||||
uint32_t button_ptr = button_ptrs[j];
|
||||
auto button = xe::load_and_swap<std::wstring>(
|
||||
auto button = xe::load_and_swap<std::u16string>(
|
||||
kernel_state()->memory()->TranslateVirtual(button_ptr));
|
||||
all_buttons.append(button);
|
||||
if (j + 1 < button_count) {
|
||||
all_buttons.append(L" | ");
|
||||
all_buttons.append(u" | ");
|
||||
}
|
||||
buttons.push_back(button);
|
||||
}
|
||||
@@ -166,13 +167,13 @@ DECLARE_XAM_EXPORT1(XamShowMessageBoxUI, kUI, kImplemented);
|
||||
|
||||
class KeyboardInputDialog : public xe::ui::ImGuiDialog {
|
||||
public:
|
||||
KeyboardInputDialog(xe::ui::Window* window, std::wstring title,
|
||||
std::wstring description, std::wstring default_text,
|
||||
std::wstring* out_text, size_t max_length)
|
||||
KeyboardInputDialog(xe::ui::Window* window, std::u16string title,
|
||||
std::u16string description, std::u16string default_text,
|
||||
std::u16string* out_text, size_t max_length)
|
||||
: ImGuiDialog(window),
|
||||
title_(xe::to_string(title)),
|
||||
description_(xe::to_string(description)),
|
||||
default_text_(xe::to_string(default_text)),
|
||||
title_(xe::to_utf8(title)),
|
||||
description_(xe::to_utf8(description)),
|
||||
default_text_(xe::to_utf8(default_text)),
|
||||
out_text_(out_text),
|
||||
max_length_(max_length) {
|
||||
if (!title_.size()) {
|
||||
@@ -209,14 +210,16 @@ class KeyboardInputDialog : public xe::ui::ImGuiDialog {
|
||||
if (ImGui::InputText("##body", text_buffer_.data(), text_buffer_.size(),
|
||||
ImGuiInputTextFlags_EnterReturnsTrue)) {
|
||||
if (out_text_) {
|
||||
*out_text_ = xe::to_wstring(text_buffer_.data());
|
||||
*out_text_ = xe::to_utf16(
|
||||
std::string_view(text_buffer_.data(), text_buffer_.size()));
|
||||
}
|
||||
ImGui::CloseCurrentPopup();
|
||||
Close();
|
||||
}
|
||||
if (ImGui::Button("OK")) {
|
||||
if (out_text_) {
|
||||
*out_text_ = xe::to_wstring(text_buffer_.data());
|
||||
*out_text_ = xe::to_utf16(
|
||||
std::string_view(text_buffer_.data(), text_buffer_.size()));
|
||||
}
|
||||
ImGui::CloseCurrentPopup();
|
||||
Close();
|
||||
@@ -238,16 +241,16 @@ class KeyboardInputDialog : public xe::ui::ImGuiDialog {
|
||||
std::string title_;
|
||||
std::string description_;
|
||||
std::string default_text_;
|
||||
std::wstring* out_text_ = nullptr;
|
||||
std::u16string* out_text_ = nullptr;
|
||||
std::vector<char> text_buffer_;
|
||||
size_t max_length_ = 0;
|
||||
};
|
||||
|
||||
// https://www.se7ensins.com/forums/threads/release-how-to-use-xshowkeyboardui-release.906568/
|
||||
dword_result_t XamShowKeyboardUI(dword_t user_index, dword_t flags,
|
||||
lpwstring_t default_text, lpwstring_t title,
|
||||
lpwstring_t description, lpwstring_t buffer,
|
||||
dword_t buffer_length,
|
||||
lpu16string_t default_text,
|
||||
lpu16string_t title, lpu16string_t description,
|
||||
lpu16string_t buffer, dword_t buffer_length,
|
||||
pointer_t<XAM_OVERLAPPED> overlapped) {
|
||||
if (!buffer) {
|
||||
return X_ERROR_INVALID_PARAMETER;
|
||||
@@ -260,7 +263,7 @@ dword_result_t XamShowKeyboardUI(dword_t user_index, dword_t flags,
|
||||
// Redirect default_text back into the buffer.
|
||||
std::memset(buffer, 0, buffer_length * 2);
|
||||
if (default_text) {
|
||||
xe::store_and_swap<std::wstring>(buffer, default_text.value());
|
||||
xe::store_and_swap<std::u16string>(buffer, default_text.value());
|
||||
}
|
||||
|
||||
// Broadcast XN_SYS_UI = false
|
||||
@@ -274,14 +277,14 @@ dword_result_t XamShowKeyboardUI(dword_t user_index, dword_t flags,
|
||||
}
|
||||
}
|
||||
|
||||
std::wstring out_text;
|
||||
std::u16string out_text;
|
||||
|
||||
auto display_window = kernel_state()->emulator()->display_window();
|
||||
xe::threading::Fence fence;
|
||||
display_window->loop()->PostSynchronous([&]() {
|
||||
(new KeyboardInputDialog(display_window, title ? title.value() : L"",
|
||||
description ? description.value() : L"",
|
||||
default_text ? default_text.value() : L"",
|
||||
(new KeyboardInputDialog(display_window, title ? title.value() : u"",
|
||||
description ? description.value() : u"",
|
||||
default_text ? default_text.value() : u"",
|
||||
&out_text, buffer_length))
|
||||
->Then(&fence);
|
||||
});
|
||||
@@ -294,7 +297,7 @@ dword_result_t XamShowKeyboardUI(dword_t user_index, dword_t flags,
|
||||
|
||||
// Truncate the string.
|
||||
out_text = out_text.substr(0, buffer_length - 1);
|
||||
xe::store_and_swap<std::wstring>(buffer, out_text);
|
||||
xe::store_and_swap<std::u16string>(buffer, out_text);
|
||||
|
||||
// Broadcast XN_SYS_UI = false
|
||||
kernel_state()->BroadcastNotification(0x9, false);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -91,8 +91,7 @@ dword_result_t NtCreateFile(lpdword_t handle_out, dword_t desired_access,
|
||||
kernel_memory()->TranslateVirtual<X_ANSI_STRING*>(object_attrs->name_ptr);
|
||||
|
||||
// Compute path, possibly attrs relative.
|
||||
std::string target_path =
|
||||
util::TranslateAnsiString(kernel_memory(), object_name);
|
||||
auto target_path = util::TranslateAnsiString(kernel_memory(), object_name);
|
||||
if (object_attrs->root_directory != 0xFFFFFFFD && // ObDosDevices
|
||||
object_attrs->root_directory != 0) {
|
||||
auto root_file = kernel_state()->object_table()->LookupObject<XFile>(
|
||||
@@ -102,8 +101,8 @@ dword_result_t NtCreateFile(lpdword_t handle_out, dword_t desired_access,
|
||||
|
||||
// Resolve the file using the device the root directory is part of.
|
||||
auto device = root_file->device();
|
||||
target_path = xe::join_paths(
|
||||
device->mount_path(), xe::join_paths(root_file->path(), target_path));
|
||||
target_path = xe::utf8::join_guest_paths(
|
||||
{device->mount_path(), root_file->path(), target_path});
|
||||
}
|
||||
|
||||
// Attempt open (or create).
|
||||
@@ -686,9 +685,8 @@ dword_result_t NtQueryDirectoryFile(
|
||||
auto name = util::TranslateAnsiString(kernel_memory(), file_name);
|
||||
if (file) {
|
||||
X_FILE_DIRECTORY_INFORMATION dir_info = {0};
|
||||
result = file->QueryDirectory(file_info_ptr, length,
|
||||
!name.empty() ? name.c_str() : nullptr,
|
||||
restart_scan != 0);
|
||||
result =
|
||||
file->QueryDirectory(file_info_ptr, length, name, restart_scan != 0);
|
||||
if (XSUCCEEDED(result)) {
|
||||
info = length;
|
||||
}
|
||||
@@ -741,8 +739,7 @@ dword_result_t NtOpenSymbolicLinkObject(
|
||||
assert_always();
|
||||
}
|
||||
|
||||
auto pos = target_path.find("\\??\\");
|
||||
if (pos != target_path.npos && pos == 0) {
|
||||
if (utf8::starts_with(target_path, "\\??\\")) {
|
||||
target_path = target_path.substr(4); // Strip the full qualifier
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2019 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "third_party/fmt/include/fmt/format.h"
|
||||
#include "xenia/base/clock.h"
|
||||
#include "xenia/base/debugging.h"
|
||||
#include "xenia/base/logging.h"
|
||||
@@ -47,15 +48,18 @@ bool XboxkrnlModule::SendPIXCommand(const char* cmd) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto scratch = memory_->SystemHeapAlloc(260 + 260);
|
||||
uint32_t scratch_size = 260 + 260;
|
||||
auto scratch_ptr = memory_->SystemHeapAlloc(scratch_size);
|
||||
auto scratch = memory_->TranslateVirtual(scratch_ptr);
|
||||
std::memset(scratch, 0, scratch_size);
|
||||
|
||||
auto response = memory_->TranslateVirtual<const char*>(scratch + 0);
|
||||
auto command = memory_->TranslateVirtual<char*>(scratch + 260);
|
||||
auto response = reinterpret_cast<char*>(scratch + 0);
|
||||
auto command = reinterpret_cast<char*>(scratch + 260);
|
||||
|
||||
std::snprintf(command, 260, "PIX!m!%s", cmd);
|
||||
fmt::format_to_n(command, 259, "PIX!m!{}", cmd);
|
||||
|
||||
global_lock.unlock();
|
||||
uint64_t args[] = {scratch + 260, scratch, 260};
|
||||
uint64_t args[] = {scratch_ptr + 260, scratch_ptr, 260};
|
||||
auto result = kernel_state_->processor()->Execute(
|
||||
XThread::GetCurrentThread()->thread_state(), pix_function_, args,
|
||||
xe::countof(args));
|
||||
@@ -64,7 +68,7 @@ bool XboxkrnlModule::SendPIXCommand(const char* cmd) {
|
||||
XELOGD("PIX(command): %s", cmd);
|
||||
XELOGD("PIX(response): %s", response);
|
||||
|
||||
memory_->SystemHeapFree(scratch);
|
||||
memory_->SystemHeapFree(scratch_ptr);
|
||||
|
||||
if (XSUCCEEDED(result)) {
|
||||
return true;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2019 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -46,7 +46,7 @@ dword_result_t XexGetModuleHandle(lpstring_t module_name,
|
||||
if (!module_name) {
|
||||
module = kernel_state()->GetExecutableModule();
|
||||
} else {
|
||||
module = kernel_state()->GetModule(module_name);
|
||||
module = kernel_state()->GetModule(module_name.value());
|
||||
}
|
||||
|
||||
if (!module) {
|
||||
@@ -69,7 +69,7 @@ dword_result_t XexGetModuleSection(lpvoid_t hmodule, lpstring_t name,
|
||||
if (module) {
|
||||
uint32_t section_data = 0;
|
||||
uint32_t section_size = 0;
|
||||
result = module->GetSection(name, §ion_data, §ion_size);
|
||||
result = module->GetSection(name.value(), §ion_data, §ion_size);
|
||||
if (XSUCCEEDED(result)) {
|
||||
*data_ptr = section_data;
|
||||
*size_ptr = section_size;
|
||||
@@ -87,14 +87,14 @@ dword_result_t XexLoadImage(lpstring_t module_name, dword_t module_flags,
|
||||
X_STATUS result = X_STATUS_NO_SUCH_FILE;
|
||||
|
||||
uint32_t hmodule = 0;
|
||||
auto module = kernel_state()->GetModule(module_name);
|
||||
auto module = kernel_state()->GetModule(module_name.value());
|
||||
if (module) {
|
||||
// Existing module found.
|
||||
hmodule = module->hmodule_ptr();
|
||||
result = X_STATUS_SUCCESS;
|
||||
} else {
|
||||
// Not found; attempt to load as a user module.
|
||||
auto user_module = kernel_state()->LoadUserModule(module_name);
|
||||
auto user_module = kernel_state()->LoadUserModule(module_name.value());
|
||||
if (user_module) {
|
||||
// Give up object ownership, this reference will be released by the last
|
||||
// XexUnloadImage call
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -141,20 +141,18 @@ dword_result_t ObDereferenceObject(dword_t native_ptr) {
|
||||
}
|
||||
DECLARE_XBOXKRNL_EXPORT1(ObDereferenceObject, kNone, kImplemented);
|
||||
|
||||
dword_result_t ObCreateSymbolicLink(pointer_t<X_ANSI_STRING> path,
|
||||
pointer_t<X_ANSI_STRING> target) {
|
||||
auto path_str = util::TranslateAnsiString(kernel_memory(), path);
|
||||
auto target_str = util::TranslateAnsiString(kernel_memory(), target);
|
||||
path_str = filesystem::CanonicalizePath(path_str);
|
||||
target_str = filesystem::CanonicalizePath(target_str);
|
||||
dword_result_t ObCreateSymbolicLink(pointer_t<X_ANSI_STRING> path_ptr,
|
||||
pointer_t<X_ANSI_STRING> target_ptr) {
|
||||
auto path = util::TranslateAnsiString(kernel_memory(), path_ptr);
|
||||
auto target = util::TranslateAnsiString(kernel_memory(), target_ptr);
|
||||
path = xe::utf8::canonicalize_guest_path(path);
|
||||
target = xe::utf8::canonicalize_guest_path(target);
|
||||
|
||||
auto pos = path_str.find("\\??\\");
|
||||
if (pos != path_str.npos && pos == 0) {
|
||||
path_str = path_str.substr(4); // Strip the full qualifier
|
||||
if (xe::utf8::starts_with(path, u8"\\??\\")) {
|
||||
path = path.substr(4); // Strip the full qualifier
|
||||
}
|
||||
|
||||
if (!kernel_state()->file_system()->RegisterSymbolicLink(path_str,
|
||||
target_str)) {
|
||||
if (!kernel_state()->file_system()->RegisterSymbolicLink(path, target)) {
|
||||
return X_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
@@ -162,9 +160,9 @@ dword_result_t ObCreateSymbolicLink(pointer_t<X_ANSI_STRING> path,
|
||||
}
|
||||
DECLARE_XBOXKRNL_EXPORT1(ObCreateSymbolicLink, kNone, kImplemented);
|
||||
|
||||
dword_result_t ObDeleteSymbolicLink(pointer_t<X_ANSI_STRING> path) {
|
||||
auto path_str = util::TranslateAnsiString(kernel_memory(), path);
|
||||
if (!kernel_state()->file_system()->UnregisterSymbolicLink(path_str)) {
|
||||
dword_result_t ObDeleteSymbolicLink(pointer_t<X_ANSI_STRING> path_ptr) {
|
||||
auto path = util::TranslateAnsiString(kernel_memory(), path_ptr);
|
||||
if (!kernel_state()->file_system()->UnregisterSymbolicLink(path)) {
|
||||
return X_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -165,7 +165,7 @@ DECLARE_XBOXKRNL_EXPORT1(RtlFreeAnsiString, kNone, kImplemented);
|
||||
|
||||
// https://msdn.microsoft.com/en-us/library/ff561934
|
||||
void RtlInitUnicodeString(pointer_t<X_UNICODE_STRING> destination,
|
||||
lpwstring_t source) {
|
||||
lpu16string_t source) {
|
||||
if (source) {
|
||||
destination->length = (uint16_t)source.value().size() * 2;
|
||||
destination->maximum_length = (uint16_t)(source.value().size() + 1) * 2;
|
||||
@@ -229,9 +229,9 @@ dword_result_t RtlUnicodeStringToAnsiString(
|
||||
// _In_ PCUNICODE_STRING SourceString,
|
||||
// _In_ BOOLEAN AllocateDestinationString
|
||||
|
||||
std::wstring unicode_str =
|
||||
std::u16string unicode_str =
|
||||
util::TranslateUnicodeString(kernel_memory(), source_ptr);
|
||||
std::string ansi_str = xe::to_string(unicode_str);
|
||||
std::string ansi_str = xe::to_utf8(unicode_str);
|
||||
if (ansi_str.size() > 0xFFFF - 1) {
|
||||
return X_STATUS_INVALID_PARAMETER_2;
|
||||
}
|
||||
|
||||
@@ -117,8 +117,8 @@ int32_t format_core(PPCContext* ppc_context, FormatData& data, ArgList& args,
|
||||
const bool wide) {
|
||||
int32_t count = 0;
|
||||
|
||||
char work[512];
|
||||
wchar_t wwork[4];
|
||||
char work8[512];
|
||||
char16_t work16[4];
|
||||
|
||||
struct {
|
||||
const void* buffer;
|
||||
@@ -339,13 +339,13 @@ int32_t format_core(PPCContext* ppc_context, FormatData& data, ArgList& args,
|
||||
auto value = args.get32();
|
||||
|
||||
if (!is_wide) {
|
||||
work[0] = (uint8_t)value;
|
||||
text.buffer = &work[0];
|
||||
work8[0] = (uint8_t)value;
|
||||
text.buffer = &work8[0];
|
||||
text.length = 1;
|
||||
text.is_wide = false;
|
||||
} else {
|
||||
wwork[0] = (uint16_t)value;
|
||||
text.buffer = &wwork[0];
|
||||
work16[0] = (uint16_t)value;
|
||||
text.buffer = &work16[0];
|
||||
text.length = 1;
|
||||
text.is_wide = true;
|
||||
text.swap_wide = false;
|
||||
@@ -378,7 +378,7 @@ int32_t format_core(PPCContext* ppc_context, FormatData& data, ArgList& args,
|
||||
}
|
||||
|
||||
if (precision >= 0) {
|
||||
precision = std::min(precision, (int32_t)xe::countof(work));
|
||||
precision = std::min(precision, (int32_t)xe::countof(work8));
|
||||
} else {
|
||||
precision = 1;
|
||||
}
|
||||
@@ -396,7 +396,7 @@ int32_t format_core(PPCContext* ppc_context, FormatData& data, ArgList& args,
|
||||
prefix.length = 0;
|
||||
}
|
||||
|
||||
char* end = &work[xe::countof(work) - 1];
|
||||
char* end = &work8[xe::countof(work8) - 1];
|
||||
char* start = end;
|
||||
start[0] = '\0';
|
||||
|
||||
@@ -471,9 +471,9 @@ int32_t format_core(PPCContext* ppc_context, FormatData& data, ArgList& args,
|
||||
|
||||
auto s = format_double(value, precision, c, flags);
|
||||
auto length = (int32_t)s.size();
|
||||
assert_true(length < xe::countof(work));
|
||||
assert_true(length < xe::countof(work8));
|
||||
|
||||
auto start = &work[0];
|
||||
auto start = &work8[0];
|
||||
auto end = &start[length];
|
||||
|
||||
std::memcpy(start, s.c_str(), length);
|
||||
@@ -637,7 +637,7 @@ int32_t format_core(PPCContext* ppc_context, FormatData& data, ArgList& args,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// it's a const wchar_t*
|
||||
// it's a const char16_t*
|
||||
auto b = (const uint16_t*)text.buffer;
|
||||
if (text.swap_wide) {
|
||||
while (remaining-- > 0) {
|
||||
@@ -768,15 +768,15 @@ class WideStringFormatData : public FormatData {
|
||||
}
|
||||
|
||||
bool put(uint16_t c) {
|
||||
output_ << (wchar_t)c;
|
||||
output_ << (char16_t)c;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::wstring wstr() const { return output_.str(); }
|
||||
std::u16string wstr() const { return output_.str(); }
|
||||
|
||||
private:
|
||||
const uint16_t* input_;
|
||||
std::wostringstream output_;
|
||||
std::basic_stringstream<char16_t> output_;
|
||||
};
|
||||
|
||||
class WideCountFormatData : public FormatData {
|
||||
|
||||
@@ -294,7 +294,7 @@ struct BufferScaling {
|
||||
};
|
||||
void AppendParam(StringBuffer* string_buffer, pointer_t<BufferScaling> param) {
|
||||
string_buffer->AppendFormat(
|
||||
"%.8X(scale %dx%d -> %dx%d))", param.guest_address(),
|
||||
"{:08X}(scale {}x{} -> {}x{}))", param.guest_address(),
|
||||
uint16_t(param->bb_width), uint16_t(param->bb_height),
|
||||
uint16_t(param->fb_width), uint16_t(param->fb_height));
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -37,15 +37,15 @@ XFile::~XFile() {
|
||||
}
|
||||
|
||||
X_STATUS XFile::QueryDirectory(X_FILE_DIRECTORY_INFORMATION* out_info,
|
||||
size_t length, const char* file_name,
|
||||
size_t length, const std::string_view file_name,
|
||||
bool restart) {
|
||||
assert_not_null(out_info);
|
||||
|
||||
vfs::Entry* entry = nullptr;
|
||||
|
||||
if (file_name != nullptr) {
|
||||
if (!file_name.empty()) {
|
||||
// Only queries in the current directory are supported for now.
|
||||
assert_true(std::strchr(file_name, '\\') == nullptr);
|
||||
assert_true(utf8::find_any_of(file_name, "\\") == std::string_view::npos);
|
||||
|
||||
find_engine_.SetRule(file_name);
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -96,7 +96,7 @@ class XFile : public XObject {
|
||||
void set_position(uint64_t value) { position_ = value; }
|
||||
|
||||
X_STATUS QueryDirectory(X_FILE_DIRECTORY_INFORMATION* out_info, size_t length,
|
||||
const char* file_name, bool restart);
|
||||
const std::string_view file_name, bool restart);
|
||||
|
||||
// Don't do within the global critical region because invalidation callbacks
|
||||
// may be triggered (as per the usual rule of not doing I/O within the global
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -39,24 +39,19 @@ XModule::~XModule() {
|
||||
memory()->SystemHeapFree(hmodule_ptr_);
|
||||
}
|
||||
|
||||
bool XModule::Matches(const std::string& name) const {
|
||||
if (strcasecmp(xe::find_name_from_path(path_).c_str(), name.c_str()) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (strcasecmp(name_.c_str(), name.c_str()) == 0) {
|
||||
return true;
|
||||
}
|
||||
if (strcasecmp(path_.c_str(), name.c_str()) == 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool XModule::Matches(const std::string_view name) const {
|
||||
return xe::utf8::equal_case(xe::utf8::find_name_from_guest_path(path()),
|
||||
name) ||
|
||||
xe::utf8::equal_case(this->name(), name) ||
|
||||
xe::utf8::equal_case(path(), name);
|
||||
} // namespace kernel
|
||||
|
||||
void XModule::OnLoad() { kernel_state_->RegisterModule(this); }
|
||||
|
||||
void XModule::OnUnload() { kernel_state_->UnregisterModule(this); }
|
||||
|
||||
X_STATUS XModule::GetSection(const char* name, uint32_t* out_section_data,
|
||||
X_STATUS XModule::GetSection(const std::string_view name,
|
||||
uint32_t* out_section_data,
|
||||
uint32_t* out_section_size) {
|
||||
return X_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
@@ -73,31 +68,12 @@ uint32_t XModule::GetHandleFromHModule(void* hmodule) {
|
||||
return ldr_data->checksum;
|
||||
}
|
||||
|
||||
std::string XModule::NameFromPath(std::string path) {
|
||||
std::string name;
|
||||
auto last_slash = path.find_last_of('/');
|
||||
if (last_slash == path.npos) {
|
||||
last_slash = path.find_last_of('\\');
|
||||
}
|
||||
if (last_slash == path.npos) {
|
||||
name = path;
|
||||
} else {
|
||||
name = path.substr(last_slash + 1);
|
||||
}
|
||||
auto dot = name.find_last_of('.');
|
||||
if (dot != name.npos) {
|
||||
name = name.substr(0, dot);
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
bool XModule::Save(ByteStream* stream) {
|
||||
XELOGD("XModule %.8X (%s)", handle(), path_.c_str());
|
||||
XELOGD("XModule %.8X (%s)", handle(), path().c_str());
|
||||
|
||||
stream->Write('XMOD');
|
||||
|
||||
stream->Write(path_);
|
||||
stream->Write(path());
|
||||
stream->Write(hmodule_ptr_);
|
||||
|
||||
if (!SaveObject(stream)) {
|
||||
@@ -123,7 +99,7 @@ object_ref<XModule> XModule::Restore(KernelState* kernel_state,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
XELOGD("XModule %.8X (%s)", module->handle(), module->path_.c_str());
|
||||
XELOGD("XModule %.8X (%s)", module->handle(), module->path().c_str());
|
||||
|
||||
module->hmodule_ptr_ = hmodule_ptr;
|
||||
return module;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -63,16 +63,17 @@ class XModule : public XObject {
|
||||
virtual ~XModule();
|
||||
|
||||
ModuleType module_type() const { return module_type_; }
|
||||
const std::string& path() const { return path_; }
|
||||
const std::string& name() const { return name_; }
|
||||
bool Matches(const std::string& name) const;
|
||||
virtual const std::string& path() const = 0;
|
||||
virtual const std::string& name() const = 0;
|
||||
bool Matches(const std::string_view name) const;
|
||||
|
||||
xe::cpu::Module* processor_module() const { return processor_module_; }
|
||||
uint32_t hmodule_ptr() const { return hmodule_ptr_; }
|
||||
|
||||
virtual uint32_t GetProcAddressByOrdinal(uint16_t ordinal) = 0;
|
||||
virtual uint32_t GetProcAddressByName(const char* name) = 0;
|
||||
virtual X_STATUS GetSection(const char* name, uint32_t* out_section_data,
|
||||
virtual uint32_t GetProcAddressByName(const std::string_view name) = 0;
|
||||
virtual X_STATUS GetSection(const std::string_view name,
|
||||
uint32_t* out_section_data,
|
||||
uint32_t* out_section_size);
|
||||
|
||||
static object_ref<XModule> GetFromHModule(KernelState* kernel_state,
|
||||
@@ -86,11 +87,8 @@ class XModule : public XObject {
|
||||
protected:
|
||||
void OnLoad();
|
||||
void OnUnload();
|
||||
static std::string NameFromPath(std::string path);
|
||||
|
||||
ModuleType module_type_;
|
||||
std::string name_;
|
||||
std::string path_;
|
||||
|
||||
xe::cpu::Module* processor_module_;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -22,8 +22,8 @@ XSymbolicLink::XSymbolicLink() : XObject(kType), path_(), target_() {}
|
||||
|
||||
XSymbolicLink::~XSymbolicLink() {}
|
||||
|
||||
void XSymbolicLink::Initialize(const std::string& path,
|
||||
const std::string& target) {
|
||||
void XSymbolicLink::Initialize(const std::string_view path,
|
||||
const std::string_view target) {
|
||||
path_ = path;
|
||||
target_ = target;
|
||||
// TODO(gibbed): kernel_state_->RegisterSymbolicLink(this);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -28,14 +28,14 @@ class XSymbolicLink : public XObject {
|
||||
explicit XSymbolicLink(KernelState* kernel_state);
|
||||
~XSymbolicLink() override;
|
||||
|
||||
void Initialize(const std::string& path, const std::string& target);
|
||||
void Initialize(const std::string_view path, const std::string_view target);
|
||||
|
||||
bool Save(ByteStream* stream) override;
|
||||
static object_ref<XSymbolicLink> Restore(KernelState* kernel_state,
|
||||
ByteStream* stream);
|
||||
|
||||
const std ::string& path() const { return path_; }
|
||||
const std ::string& target() const { return target_; }
|
||||
const std::string& path() const { return path_; }
|
||||
const std::string& target() const { return target_; }
|
||||
|
||||
private:
|
||||
XSymbolicLink();
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2019 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <objbase.h>
|
||||
#endif
|
||||
|
||||
#include "third_party/fmt/include/fmt/format.h"
|
||||
#include "xenia/base/byte_stream.h"
|
||||
#include "xenia/base/clock.h"
|
||||
#include "xenia/base/logging.h"
|
||||
@@ -145,8 +146,8 @@ void XThread::set_last_error(uint32_t error_code) {
|
||||
guest_object<X_KTHREAD>()->last_error = error_code;
|
||||
}
|
||||
|
||||
void XThread::set_name(const std::string& name) {
|
||||
thread_name_ = xe::format_string("%s (%.8X)", name.c_str(), handle());
|
||||
void XThread::set_name(const std::string_view name) {
|
||||
thread_name_ = fmt::format("{} ({:08X})", name, handle());
|
||||
|
||||
if (thread_) {
|
||||
// May be getting set before the thread is created.
|
||||
@@ -420,10 +421,7 @@ X_STATUS XThread::Create() {
|
||||
|
||||
// Set the thread name based on host ID (for easier debugging).
|
||||
if (thread_name_.empty()) {
|
||||
char thread_name[32];
|
||||
snprintf(thread_name, xe::countof(thread_name), "XThread%.04X",
|
||||
thread_->system_id());
|
||||
set_name(thread_name);
|
||||
set_name(fmt::format("XThread{:04X}", thread_->system_id()));
|
||||
}
|
||||
|
||||
if (creation_params_.creation_flags & 0x60) {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Copyright 2020 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
@@ -142,7 +142,7 @@ class XThread : public XObject, public cpu::Thread {
|
||||
uint32_t thread_id() const { return thread_id_; }
|
||||
uint32_t last_error();
|
||||
void set_last_error(uint32_t error_code);
|
||||
void set_name(const std::string& name);
|
||||
void set_name(const std::string_view name);
|
||||
|
||||
X_STATUS Create();
|
||||
X_STATUS Exit(int exit_code);
|
||||
|
||||
Reference in New Issue
Block a user