From 97325d286f0a6c774c546712f5a5e88fd916dc44 Mon Sep 17 00:00:00 2001 From: 0x8081 Date: Tue, 20 Aug 2019 14:12:59 -0600 Subject: [PATCH] Fix Emoose Dashboard merge --- src/xenia/kernel/user_module.cc.orig | 856 ++++++++++++++++++ src/xenia/kernel/xam/content_manager.cc.orig | 354 ++++++++ src/xenia/kernel/xam/content_manager.h.orig | 108 +++ .../kernel/xboxkrnl/xboxkrnl_modules.cc.orig | 318 +++++++ src/xenia/vfs/virtual_file_system.cc | 9 + src/xenia/vfs/virtual_file_system.h | 1 + 6 files changed, 1646 insertions(+) create mode 100644 src/xenia/kernel/user_module.cc.orig create mode 100644 src/xenia/kernel/xam/content_manager.cc.orig create mode 100644 src/xenia/kernel/xam/content_manager.h.orig create mode 100644 src/xenia/kernel/xboxkrnl/xboxkrnl_modules.cc.orig diff --git a/src/xenia/kernel/user_module.cc.orig b/src/xenia/kernel/user_module.cc.orig new file mode 100644 index 000000000..3e4cbbcfd --- /dev/null +++ b/src/xenia/kernel/user_module.cc.orig @@ -0,0 +1,856 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2013 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/kernel/user_module.h" + +#include + +#include "xenia/base/byte_stream.h" +#include "xenia/base/logging.h" +#include "xenia/cpu/elf_module.h" +#include "xenia/cpu/processor.h" +#include "xenia/cpu/xex_module.h" +#include "xenia/emulator.h" +#include "xenia/kernel/xfile.h" +#include "xenia/kernel/xthread.h" +#include "xenia/vfs/devices/stfs_container_device.h" + +DEFINE_bool(xex_apply_patches, true, "Apply XEX patches.", "Kernel"); + +namespace xe { +namespace kernel { + +UserModule::UserModule(KernelState* kernel_state) + : XModule(kernel_state, ModuleType::kUserModule) {} + +UserModule::~UserModule() { Unload(); } + +uint32_t UserModule::title_id() const { + if (module_format_ != kModuleFormatXex) { + return 0; + } + auto header = xex_header(); + for (uint32_t i = 0; i < header->header_count; i++) { + auto& opt_header = header->headers[i]; + if (opt_header.key == XEX_HEADER_EXECUTION_INFO) { + auto opt_header_ptr = + reinterpret_cast(header) + opt_header.offset; + auto opt_exec_info = + reinterpret_cast(opt_header_ptr); + return static_cast(opt_exec_info->title_id); + } + } + return 0; +} + +X_STATUS UserModule::LoadFromFile(std::string path) { + X_STATUS result = X_STATUS_UNSUCCESSFUL; + + auto file_system = kernel_state()->file_system(); + + // Resolve the file to open. + // TODO(benvanik): make this code shared? + auto fs_entry = file_system->ResolvePath(path); + if (!fs_entry) { + XELOGE("File not found: %s", path.c_str()); + return X_STATUS_NO_SUCH_FILE; + } + + path_ = fs_entry->absolute_path(); + name_ = NameFromPath(path_); + + // If the FS supports mapping, map the file in and load from that. + if (fs_entry->can_map()) { + // Map. + auto mmap = fs_entry->OpenMapped(MappedMemory::Mode::kRead); + if (!mmap) { + return result; + } + + // Load the module. + result = LoadFromMemory(mmap->data(), mmap->size()); + } else { + std::vector buffer(fs_entry->size()); + + // Open file for reading. + vfs::File* file = nullptr; + result = fs_entry->Open(vfs::FileAccess::kGenericRead, &file); + if (XFAILED(result)) { + return result; + } + + // Read entire file into memory. + // Ugh. + size_t bytes_read = 0; + result = file->ReadSync(buffer.data(), buffer.size(), 0, &bytes_read); + if (XFAILED(result)) { + return result; + } + + // Load the module. + result = LoadFromMemory(buffer.data(), bytes_read); + + // Close the file. + file->Destroy(); + } + + // Only XEX returns X_STATUS_PENDING + if (result != X_STATUS_PENDING) { + return result; + } + +<<<<<<< HEAD + if (cvars::xex_apply_patches) { + // Search for xexp patch file + auto patch_entry = kernel_state()->file_system()->ResolvePath(path_ + "p"); +======= + if (!FLAGS_xex_apply_patches) { + return LoadXexContinue(); + } + + auto module_path = fs_entry->path(); + + auto content_manager = kernel_state()->content_manager(); + + if (!file_system->IsSymbolicLink("update:")) { + // update:\\ path isn't symlinked, try searching for an update package +>>>>>>> emoose/title-updates + + xex2_opt_execution_info* exec_info = 0; + xex_module()->GetOptHeader(XEX_HEADER_EXECUTION_INFO, &exec_info); + + if (exec_info) { + content_manager->SetTitleIdOverride(exec_info->title_id); + + auto update_packages = content_manager->ListContent( + 0, (uint32_t)vfs::StfsContentType::kInstaller); + + for (auto& update : update_packages) { + auto result = content_manager->OpenContent("update", update); + + if (!file_system->ResolvePath("update:\\" + module_path + "p")) { + // XEXP/DLLP doesn't exist in this package, lets just close it + content_manager->CloseContent("update"); + continue; + } else { + // XEXP/DLLP found, break out of package loop + // TODO: verify XEXP/DLLP works first? + break; + } + } + } + } + + // Unset content_manager title ID override + content_manager->SetTitleIdOverride(0); + + // First try checking update:\ root for patch, otherwise try same path as XEX + auto patch_entry = file_system->ResolvePath("update:\\" + module_path + "p"); + if (!patch_entry) { + patch_entry = file_system->ResolvePath(path_ + "p"); + } + if (patch_entry) { + auto patch_path = patch_entry->absolute_path(); + + XELOGI("Loading XEX patch from %s", patch_path.c_str()); + + auto patch_module = object_ref(new UserModule(kernel_state_)); + result = patch_module->LoadFromFile(patch_path); + if (!result) { + result = patch_module->xex_module()->ApplyPatch(xex_module()); + if (result) { + XELOGE("Failed to apply XEX patch, code: %d", result); + } + } else { + XELOGE("Failed to load XEX patch, code: %d", result); + } + + if (result) { + return X_STATUS_UNSUCCESSFUL; + } + } + + return LoadXexContinue(); +} + +X_STATUS UserModule::LoadFromMemory(const void* addr, const size_t length) { + auto processor = kernel_state()->processor(); + + auto magic = xe::load_and_swap(addr); + if (magic == 'XEX2') { + module_format_ = kModuleFormatXex; + } else if (magic == 0x7F454C46 /* 0x7F 'ELF' */) { + module_format_ = kModuleFormatElf; + } else { + auto magic16 = xe::load_and_swap(addr); + if (magic16 == 0x4D5A) { + XELOGE("XNA executables are not yet implemented"); + return X_STATUS_NOT_IMPLEMENTED; + } else { + XELOGE("Unknown module magic: %.8X", magic); + return X_STATUS_NOT_IMPLEMENTED; + } + } + + if (module_format_ == kModuleFormatXex) { + // Prepare the module for execution. + // Runtime takes ownership. + auto xex_module = + std::make_unique(processor, kernel_state()); + if (!xex_module->Load(name_, path_, addr, length)) { + return X_STATUS_UNSUCCESSFUL; + } + processor_module_ = xex_module.get(); + if (!processor->AddModule(std::move(xex_module))) { + return X_STATUS_UNSUCCESSFUL; + } + + // Only XEX headers + image are loaded right now + // Caller will have to call LoadXexContinue after they've loaded in a patch + // (or after patch isn't found anywhere) + // or if this is an XEXP being loaded return success since there's nothing + // else to load + return this->xex_module()->is_patch() ? X_STATUS_SUCCESS : X_STATUS_PENDING; + + } else if (module_format_ == kModuleFormatElf) { + auto elf_module = + std::make_unique(processor, kernel_state()); + if (!elf_module->Load(name_, path_, addr, length)) { + return X_STATUS_UNSUCCESSFUL; + } + + entry_point_ = elf_module->entry_point(); + stack_size_ = 1024 * 1024; // 1 MB + is_dll_module_ = false; // Hardcoded not a DLL (for now) + + processor_module_ = elf_module.get(); + if (!processor->AddModule(std::move(elf_module))) { + return X_STATUS_UNSUCCESSFUL; + } + } + + OnLoad(); + + return X_STATUS_SUCCESS; +} + +X_STATUS UserModule::LoadXexContinue() { + // LoadXexContinue: finishes loading XEX after a patch has been applied (or + // patch wasn't found) + + if (!this->xex_module()) { + return X_STATUS_UNSUCCESSFUL; + } + + // If guest_xex_header is set we must have already loaded the XEX + if (guest_xex_header_) { + return X_STATUS_SUCCESS; + } + + // Finish XexModule load (PE sections/imports/symbols...) + if (!xex_module()->LoadContinue()) { + return X_STATUS_UNSUCCESSFUL; + } + + // Copy the xex2 header into guest memory. + auto header = this->xex_module()->xex_header(); + auto security_header = this->xex_module()->xex_security_info(); + guest_xex_header_ = memory()->SystemHeapAlloc(header->header_size); + + uint8_t* xex_header_ptr = memory()->TranslateVirtual(guest_xex_header_); + std::memcpy(xex_header_ptr, header, header->header_size); + + // Cache some commonly used headers... + this->xex_module()->GetOptHeader(XEX_HEADER_ENTRY_POINT, &entry_point_); + this->xex_module()->GetOptHeader(XEX_HEADER_DEFAULT_STACK_SIZE, &stack_size_); + is_dll_module_ = !!(header->module_flags & XEX_MODULE_DLL_MODULE); + + // Setup the loader data entry + auto ldr_data = + memory()->TranslateVirtual(hmodule_ptr_); + + ldr_data->dll_base = 0; // GetProcAddress will read this. + ldr_data->xex_header_base = guest_xex_header_; + ldr_data->full_image_size = security_header->image_size; + ldr_data->image_base = this->xex_module()->base_address(); + ldr_data->entry_point = entry_point_; + + OnLoad(); + + return X_STATUS_SUCCESS; +} + +X_STATUS UserModule::Unload() { + if (module_format_ == kModuleFormatXex && + (!processor_module_ || !xex_module()->loaded())) { + // Quick abort. + return X_STATUS_SUCCESS; + } + + if (module_format_ == kModuleFormatXex && processor_module_ && + xex_module()->Unload()) { + OnUnload(); + return X_STATUS_SUCCESS; + } + + return X_STATUS_UNSUCCESSFUL; +} + +uint32_t UserModule::GetProcAddressByOrdinal(uint16_t ordinal) { + return xex_module()->GetProcAddress(ordinal); +} + +uint32_t UserModule::GetProcAddressByName(const char* name) { + return xex_module()->GetProcAddress(name); +} + +X_STATUS UserModule::GetSection(const char* 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, + &resource_header)) { + // 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) { + // Found! + *out_section_data = res.address; + *out_section_size = res.size; + + return X_STATUS_SUCCESS; + } + } + + return X_STATUS_NOT_FOUND; +} + +X_STATUS UserModule::GetOptHeader(xex2_header_keys key, void** out_ptr) { + assert_not_null(out_ptr); + + if (module_format_ == kModuleFormatElf) { + // Quick die. + return X_STATUS_UNSUCCESSFUL; + } + + bool ret = xex_module()->GetOptHeader(key, out_ptr); + if (!ret) { + return X_STATUS_NOT_FOUND; + } + + return X_STATUS_SUCCESS; +} + +X_STATUS UserModule::GetOptHeader(xex2_header_keys key, + uint32_t* out_header_guest_ptr) { + if (module_format_ == kModuleFormatElf) { + // Quick die. + return X_STATUS_UNSUCCESSFUL; + } + + auto header = + memory()->TranslateVirtual(guest_xex_header_); + if (!header) { + return X_STATUS_UNSUCCESSFUL; + } + return GetOptHeader(memory(), header, key, out_header_guest_ptr); +} + +X_STATUS UserModule::GetOptHeader(const Memory* memory, + const xex2_header* header, + xex2_header_keys key, + uint32_t* out_header_guest_ptr) { + assert_not_null(out_header_guest_ptr); + uint32_t field_value = 0; + bool field_found = false; + for (uint32_t i = 0; i < header->header_count; i++) { + auto& opt_header = header->headers[i]; + if (opt_header.key != key) { + continue; + } + field_found = true; + switch (opt_header.key & 0xFF) { + case 0x00: + // Return data stored in header value. + field_value = opt_header.value; + break; + case 0x01: + // Return pointer to data stored in header value. + field_value = memory->HostToGuestVirtual(&opt_header.value); + break; + default: + // Data stored at offset to header. + field_value = memory->HostToGuestVirtual(header) + opt_header.offset; + break; + } + break; + } + *out_header_guest_ptr = field_value; + if (!field_found) { + return X_STATUS_NOT_FOUND; + } + return X_STATUS_SUCCESS; +} + +bool UserModule::Save(ByteStream* stream) { + if (!XModule::Save(stream)) { + return false; + } + + // A lot of the information stored on this class can be reconstructed at + // runtime. + + return true; +} + +object_ref UserModule::Restore(KernelState* kernel_state, + ByteStream* stream, + std::string path) { + auto module = new UserModule(kernel_state); + + // XModule::Save took care of this earlier... + // TODO: Find a nicer way to represent that here. + if (!module->RestoreObject(stream)) { + return nullptr; + } + + auto result = module->LoadFromFile(path); + if (XFAILED(result)) { + XELOGD("UserModule::Restore LoadFromFile(%s) FAILED - code %.8X", + path.c_str(), result); + return nullptr; + } + + if (!kernel_state->RegisterUserModule(retain_object(module))) { + // Already loaded? + assert_always(); + } + + return object_ref(module); +} + +void UserModule::Dump() { + if (module_format_ == kModuleFormatElf) { + // Quick die. + return; + } + + StringBuffer sb; + + xe::cpu::ExportResolver* export_resolver = + kernel_state_->emulator()->export_resolver(); + 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); + + // Security header + auto security_info = xex_module()->xex_security_info(); + sb.AppendFormat("Security Header:\n"); + sb.AppendFormat(" Image Flags: %.8X\n", + (uint32_t)security_info->image_flags); + sb.AppendFormat(" Load Address: %.8X\n", + (uint32_t)security_info->load_address); + sb.AppendFormat(" Image Size: %.8X\n", + (uint32_t)security_info->image_size); + sb.AppendFormat(" Export Table: %.8X\n", + (uint32_t)security_info->export_table); + + // Optional headers + sb.AppendFormat("Optional Header Count: %d\n", + (uint32_t)header->header_count); + + for (uint32_t i = 0; i < header->header_count; i++) { + auto& opt_header = header->headers[i]; + + // Stash a pointer (although this isn't used in every case) + auto opt_header_ptr = + reinterpret_cast(header) + opt_header.offset; + switch (opt_header.key) { + case XEX_HEADER_RESOURCE_INFO: { + sb.AppendFormat(" XEX_HEADER_RESOURCE_INFO:\n"); + auto opt_resource_info = + reinterpret_cast(opt_header_ptr); + + uint32_t count = (opt_resource_info->size - 4) / 16; + for (uint32_t j = 0; j < count; j++) { + auto& res = opt_resource_info->resources[j]; + + // Manually NULL-terminate the name. + char name[9]; + std::memcpy(name, res.name, sizeof(res.name)); + name[8] = 0; + + sb.AppendFormat( + " %-8s %.8X-%.8X, %db\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"); + } break; + case XEX_HEADER_DELTA_PATCH_DESCRIPTOR: { + sb.AppendFormat(" XEX_HEADER_DELTA_PATCH_DESCRIPTOR (TODO):\n"); + } break; + case XEX_HEADER_BOUNDING_PATH: { + auto opt_bound_path = + reinterpret_cast(opt_header_ptr); + sb.AppendFormat(" XEX_HEADER_BOUNDING_PATH: %s\n", + opt_bound_path->path); + } break; + case XEX_HEADER_ORIGINAL_BASE_ADDRESS: { + sb.AppendFormat(" XEX_HEADER_ORIGINAL_BASE_ADDRESS: %.8X\n", + (uint32_t)opt_header.value); + } break; + case XEX_HEADER_ENTRY_POINT: { + sb.AppendFormat(" XEX_HEADER_ENTRY_POINT: %.8X\n", + (uint32_t)opt_header.value); + } break; + case XEX_HEADER_IMAGE_BASE_ADDRESS: { + sb.AppendFormat(" XEX_HEADER_IMAGE_BASE_ADDRESS: %.8X\n", + (uint32_t)opt_header.value); + } break; + case XEX_HEADER_IMPORT_LIBRARIES: { + sb.AppendFormat(" XEX_HEADER_IMPORT_LIBRARIES:\n"); + auto opt_import_libraries = + reinterpret_cast(opt_header_ptr); + + // FIXME: Don't know if 32 is the actual limit, but haven't seen more + // than 2. + const char* string_table[32]; + std::memset(string_table, 0, sizeof(string_table)); + + // Parse the string table + for (size_t j = 0, o = 0; j < opt_import_libraries->string_table.size && + o < opt_import_libraries->string_table.count; + o++) { + assert_true(o < xe::countof(string_table)); + const char* str = &opt_import_libraries->string_table.data[j]; + + string_table[o] = str; + j += std::strlen(str) + 1; + + // Padding + if ((j % 4) != 0) { + j += 4 - (j % 4); + } + } + + auto library_data = + reinterpret_cast(opt_import_libraries); + uint32_t library_offset = opt_import_libraries->string_table.size + 12; + while (library_offset < opt_import_libraries->size) { + auto library = reinterpret_cast( + library_data + library_offset); + if (!library->size) { + break; + } + auto name = string_table[library->name_index & 0xFF]; + assert_not_null(name); + sb.AppendFormat(" %s - %d imports\n", name, + (uint16_t)library->count); + + // Manually byteswap these because of the bitfields. + xex2_version version, version_min; + version.value = xe::byte_swap(library->version.value); + version_min.value = + xe::byte_swap(library->version_min.value); + sb.AppendFormat(" Version: %d.%d.%d.%d\n", version.major, + version.minor, version.build, version.qfe); + sb.AppendFormat(" Min Version: %d.%d.%d.%d\n", version_min.major, + version_min.minor, version_min.build, + version_min.qfe); + + library_offset += library->size; + } + } break; + case XEX_HEADER_CHECKSUM_TIMESTAMP: { + sb.AppendFormat(" XEX_HEADER_CHECKSUM_TIMESTAMP (TODO):\n"); + } break; + case XEX_HEADER_ORIGINAL_PE_NAME: { + auto opt_pe_name = + reinterpret_cast(opt_header_ptr); + sb.AppendFormat(" XEX_HEADER_ORIGINAL_PE_NAME: %s\n", + opt_pe_name->name); + } break; + case XEX_HEADER_STATIC_LIBRARIES: { + sb.AppendFormat(" XEX_HEADER_STATIC_LIBRARIES:\n"); + auto opt_static_libraries = + reinterpret_cast(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, + static_cast(library.version_major), + static_cast(library.version_minor), + static_cast(library.version_build), + static_cast(library.version_qfe)); + } + } break; + case XEX_HEADER_TLS_INFO: { + sb.AppendFormat(" XEX_HEADER_TLS_INFO:\n"); + auto opt_tls_info = + reinterpret_cast(opt_header_ptr); + + sb.AppendFormat(" Slot Count: %d\n", + static_cast(opt_tls_info->slot_count)); + sb.AppendFormat(" Raw Data Address: %.8X\n", + static_cast(opt_tls_info->raw_data_address)); + sb.AppendFormat(" Data Size: %d\n", + static_cast(opt_tls_info->data_size)); + sb.AppendFormat(" Raw Data Size: %d\n", + static_cast(opt_tls_info->raw_data_size)); + } break; + case XEX_HEADER_DEFAULT_STACK_SIZE: { + sb.AppendFormat(" XEX_HEADER_DEFAULT_STACK_SIZE: %d\n", + static_cast(opt_header.value)); + } break; + case XEX_HEADER_DEFAULT_FILESYSTEM_CACHE_SIZE: { + sb.AppendFormat(" XEX_HEADER_DEFAULT_FILESYSTEM_CACHE_SIZE: %d\n", + static_cast(opt_header.value)); + } break; + case XEX_HEADER_DEFAULT_HEAP_SIZE: { + sb.AppendFormat(" XEX_HEADER_DEFAULT_HEAP_SIZE: %d\n", + static_cast(opt_header.value)); + } break; + case XEX_HEADER_PAGE_HEAP_SIZE_AND_FLAGS: { + sb.AppendFormat(" XEX_HEADER_PAGE_HEAP_SIZE_AND_FLAGS (TODO):\n"); + } break; + case XEX_HEADER_SYSTEM_FLAGS: { + sb.AppendFormat(" XEX_HEADER_SYSTEM_FLAGS: %.8X\n", + static_cast(opt_header.value)); + } break; + case XEX_HEADER_EXECUTION_INFO: { + sb.AppendFormat(" XEX_HEADER_EXECUTION_INFO:\n"); + auto opt_exec_info = + reinterpret_cast(opt_header_ptr); + + sb.AppendFormat(" Media ID: %.8X\n", + static_cast(opt_exec_info->media_id)); + sb.AppendFormat(" Title ID: %.8X\n", + static_cast(opt_exec_info->title_id)); + sb.AppendFormat(" Savegame ID: %.8X\n", + static_cast(opt_exec_info->title_id)); + sb.AppendFormat(" Disc Number / Total: %d / %d\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", + uint32_t(opt_header.value)); + } break; + case XEX_HEADER_GAME_RATINGS: { + sb.AppendFormat(" XEX_HEADER_GAME_RATINGS (TODO):\n"); + } break; + case XEX_HEADER_LAN_KEY: { + sb.AppendFormat(" XEX_HEADER_LAN_KEY:"); + auto opt_lan_key = + reinterpret_cast(opt_header_ptr); + + for (int l = 0; l < 16; l++) { + sb.AppendFormat(" %.2X", opt_lan_key->key[l]); + } + sb.Append("\n"); + } break; + case XEX_HEADER_XBOX360_LOGO: { + sb.AppendFormat(" XEX_HEADER_XBOX360_LOGO (TODO):\n"); + } break; + case XEX_HEADER_MULTIDISC_MEDIA_IDS: { + sb.AppendFormat(" XEX_HEADER_MULTIDISC_MEDIA_IDS (TODO):\n"); + } break; + case XEX_HEADER_ALTERNATE_TITLE_IDS: { + sb.AppendFormat(" XEX_HEADER_ALTERNATE_TITLE_IDS (TODO):\n"); + } break; + case XEX_HEADER_ADDITIONAL_TITLE_MEMORY: { + sb.AppendFormat(" XEX_HEADER_ADDITIONAL_TITLE_MEMORY: %d\n", + uint32_t(opt_header.value)); + } break; + case XEX_HEADER_EXPORTS_BY_NAME: { + sb.AppendFormat(" XEX_HEADER_EXPORTS_BY_NAME:\n"); + auto dir = + reinterpret_cast(opt_header_ptr); + + auto exe_address = xex_module()->base_address(); + auto e = memory()->TranslateVirtual( + exe_address + dir->offset); + auto e_base = reinterpret_cast(e); + + // e->AddressOfX RVAs are relative to the IMAGE_EXPORT_DIRECTORY! + auto function_table = + reinterpret_cast(e_base + e->AddressOfFunctions); + // Names relative to directory. + auto name_table = + reinterpret_cast(e_base + e->AddressOfNames); + // Table of ordinals (by name). + auto ordinal_table = reinterpret_cast( + e_base + e->AddressOfNameOrdinals); + for (uint32_t n = 0; n < e->NumberOfNames; n++) { + auto name = reinterpret_cast(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); + } + } break; + default: { + sb.AppendFormat(" Unknown Header %.8X\n", (uint32_t)opt_header.key); + } break; + } + } + + sb.AppendFormat("Sections:\n"); + for (uint32_t i = 0, page = 0; i < security_info->page_descriptor_count; + i++) { + // Manually byteswap the bitfield data. + xex2_page_descriptor page_descriptor; + page_descriptor.value = + xe::byte_swap(security_info->page_descriptors[i].value); + + const char* type = "UNKNOWN"; + switch (page_descriptor.info) { + case XEX_SECTION_CODE: + type = "CODE "; + break; + case XEX_SECTION_DATA: + type = "RWDATA "; + break; + case XEX_SECTION_READONLY_DATA: + type = "RODATA "; + break; + } + + const uint32_t page_size = + xex_module()->base_address() < 0x90000000 ? 64 * 1024 : 4 * 1024; + uint32_t start_address = xex_module()->base_address() + (page * page_size); + 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, + end_address, page_descriptor.page_count * page_size); + page += page_descriptor.page_count; + } + + // Print out imports. + + auto import_libs = xex_module()->import_libraries(); + + sb.AppendFormat("Imports:\n"); + for (std::vector::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(), + library->imports.size()); + sb.AppendFormat(" Version: %d.%d.%d.%d\n", library->version.major, + library->version.minor, library->version.build, + library->version.qfe); + sb.AppendFormat(" Min Version: %d.%d.%d.%d\n", + library->min_version.major, library->min_version.minor, + library->min_version.build, library->min_version.qfe); + sb.AppendFormat("\n"); + + // Counts. + int known_count = 0; + int unknown_count = 0; + int impl_count = 0; + int unimpl_count = 0; + + for (std::vector::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_export) { + known_count++; + if (kernel_export->is_implemented()) { + impl_count++; + } else { + unimpl_count++; + } + } else { + unknown_count++; + unimpl_count++; + } + } else { + auto module = kernel_state_->GetModule(library->name.c_str()); + if (module) { + uint32_t export_addr = + module->GetProcAddressByOrdinal(info->ordinal); + if (export_addr) { + impl_count++; + known_count++; + } else { + unimpl_count++; + unknown_count++; + } + } else { + unimpl_count++; + unknown_count++; + } + } + } + float total_count = static_cast(library->imports.size()) / 100.0f; + sb.AppendFormat(" Total: %4llu\n", library->imports.size()); + sb.AppendFormat(" Known: %3d%% (%d known, %d unknown)\n", + static_cast(known_count / total_count), known_count, + unknown_count); + sb.AppendFormat( + " Implemented: %3d%% (%d implemented, %d unimplemented)\n", + static_cast(impl_count / total_count), impl_count, unimpl_count); + sb.AppendFormat("\n"); + + // Listing. + for (std::vector::const_iterator info = + library->imports.begin(); + info != library->imports.end(); ++info) { + const char* name = "UNKNOWN"; + 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_export) { + name = kernel_export->name; + implemented = kernel_export->is_implemented(); + } + } else { + auto module = kernel_state_->GetModule(library->name.c_str()); + if (module && module->GetProcAddressByOrdinal(info->ordinal)) { + // TODO(benvanik): name lookup. + implemented = true; + } + } + if (kernel_export && + kernel_export->type == cpu::Export::Type::kVariable) { + sb.AppendFormat(" V %.8X %.3X (%4d) %s %s\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", + info->value_address, info->thunk_address, + info->ordinal, info->ordinal, + implemented ? " " : "!!", name); + } + } + } + + sb.AppendFormat("\n"); + } + + xe::LogLine(xe::LogLevel::Info, 'i', sb.GetString()); +} + +} // namespace kernel +} // namespace xe diff --git a/src/xenia/kernel/xam/content_manager.cc.orig b/src/xenia/kernel/xam/content_manager.cc.orig new file mode 100644 index 000000000..608d018b6 --- /dev/null +++ b/src/xenia/kernel/xam/content_manager.cc.orig @@ -0,0 +1,354 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2015 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/kernel/xam/content_manager.h" + +#include + +#include "xenia/base/filesystem.h" +#include "xenia/base/string.h" +#include "xenia/kernel/kernel_state.h" +<<<<<<< HEAD +#include "xenia/kernel/user_module.h" +======= +#include "xenia/kernel/xam/content_package.h" +>>>>>>> emoose/stfs-packages +#include "xenia/kernel/xobject.h" +#include "xenia/vfs/devices/host_path_device.h" +#include "xenia/vfs/devices/stfs_container_device.h" + +namespace xe { +namespace kernel { +namespace xam { + +constexpr const wchar_t* const ContentManager::kStfsHeadersExtension; + +static const wchar_t* kGameUserContentDirName = L"profile"; + +<<<<<<< HEAD +static int content_device_id_ = 0; + +ContentPackage::ContentPackage(KernelState* kernel_state, std::string 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_) + "\\"; + + auto fs = kernel_state_->file_system(); + + std::unique_ptr device; + + // If this isn't a folder try mounting as STFS package + // Otherwise mount as a local host path + if (filesystem::PathExists(package_path) && + !filesystem::IsFolder(package_path)) { + device = + std::make_unique(device_path_, package_path); + } else { + device = std::make_unique(device_path_, package_path, + false); + } + + device->Initialize(); + fs->RegisterDevice(std::move(device)); + fs->RegisterSymbolicLink(root_name_ + ":", device_path_); +} + +ContentPackage::~ContentPackage() { + auto fs = kernel_state_->file_system(); + fs->UnregisterSymbolicLink(root_name_ + ":"); + fs->UnregisterDevice(device_path_); +} + +======= +>>>>>>> emoose/stfs-packages +ContentManager::ContentManager(KernelState* kernel_state, + std::wstring root_path) + : kernel_state_(kernel_state), root_path_(std::move(root_path)) {} + +ContentManager::~ContentManager() = default; + +uint32_t ContentManager::title_id() { + if (title_id_override_) { + return title_id_override_; + } + if (!kernel_state_->GetExecutableModule()) { + return -1; + } + return kernel_state_->title_id(); +} + +std::wstring ContentManager::ResolvePackageRoot(uint32_t content_type) { + wchar_t title_id_str[9] = L"00000000"; + std::swprintf(title_id_str, 9, L"%.8X", title_id()); + + wchar_t content_type_str[9] = L"00000000"; + std::swprintf(content_type_str, 9, L"%.8X", content_type); + + // Package root path: + // content_root/title_id/type_name/ + auto package_root = xe::join_paths( + root_path_, xe::join_paths(title_id_str, content_type_str)); + return package_root + xe::kWPathSeparator; +} + +std::wstring 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)); +<<<<<<< HEAD + + // Add slash to end of path if this is a folder + // (or package doesn't exist, meaning we're creating a new folder) + if (!xe::filesystem::PathExists(package_path) || + xe::filesystem::IsFolder(package_path)) { + package_path += xe::kPathSeparator; + } +======= +>>>>>>> emoose/stfs-packages + return package_path; +} + +std::vector ContentManager::ListContent(uint32_t device_id, + uint32_t content_type) { + std::vector result; + + // StfsHeader is a huge class - alloc on heap instead of stack + vfs::StfsHeader* header = new vfs::StfsHeader(); + + // Search path: + // content_root/title_id/type_name/* + auto package_root = ResolvePackageRoot(content_type); + auto file_infos = xe::filesystem::ListFiles(package_root); + for (const auto& file_info : file_infos) { + 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); + + auto headers_path = file_info.path + file_info.name; + if (file_info.type == xe::filesystem::FileInfo::Type::kDirectory) { + headers_path = headers_path + ContentManager::kStfsHeadersExtension; + } + + if (xe::filesystem::PathExists(headers_path)) { + // File is either package or directory that has .headers file + + if (file_info.type != xe::filesystem::FileInfo::Type::kDirectory) { + // Not a directory so must be a package, verify size to make sure + if (file_info.total_size <= vfs::StfsHeader::kHeaderLength) { + continue; // Invalid package (maybe .headers.bin) + } + } + + auto map = MappedMemory::Open(headers_path, MappedMemory::Mode::kRead, 0, + vfs::StfsHeader::kHeaderLength); + if (map) { + if (header->Read(map->data())) { + content_data.content_type = + static_cast(header->content_type); + content_data.display_name = header->display_names; + // TODO: select localized display name + // some games may expect different ones depending on language setting. + } + map->Close(); + } + } + + result.emplace_back(std::move(content_data)); + } + + delete header; + + return result; +} + +ContentPackage* ContentManager::ResolvePackage(const XCONTENT_DATA& data) { + auto package_path = ResolvePackagePath(data); + if (!xe::filesystem::PathExists(package_path)) { + return nullptr; + } + + auto global_lock = global_critical_region_.Acquire(); + + for (auto package : open_packages_) { + if (package->package_path() == package_path) { + return package; + } + } + + std::unique_ptr package; + + // Open as FolderContentPackage if the package is a folder or doesn't exist + if (xe::filesystem::IsFolder(package_path) || + !xe::filesystem::PathExists(package_path)) { + package = std::make_unique(kernel_state_, data, + package_path); + } else { + package = + std::make_unique(kernel_state_, data, package_path); + } + + return package.release(); +} + +bool ContentManager::ContentExists(const XCONTENT_DATA& data) { + auto path = ResolvePackagePath(data); + return xe::filesystem::PathExists(path); +} + +X_RESULT ContentManager::CreateContent(std::string root_name, + const XCONTENT_DATA& data) { + auto global_lock = global_critical_region_.Acquire(); + + auto package_path = ResolvePackagePath(data); + if (xe::filesystem::PathExists(package_path)) { + // Exists, must not! + return X_ERROR_ALREADY_EXISTS; + } + + for (auto package : open_packages_) { + if (package->package_path() == package_path) { + return X_ERROR_ALREADY_EXISTS; + } + } + + if (!xe::filesystem::CreateFolder(package_path)) { + return X_ERROR_ACCESS_DENIED; + } + + auto package = ResolvePackage(data); + if (!package) { + return X_ERROR_FUNCTION_FAILED; // Failed to create directory? + } + + if (!package->Mount(root_name)) { + return X_ERROR_DEVICE_NOT_CONNECTED; + } + + open_packages_.push_back(package); + + return X_ERROR_SUCCESS; +} + +X_RESULT ContentManager::OpenContent(std::string root_name, + const XCONTENT_DATA& data) { + auto global_lock = global_critical_region_.Acquire(); + + auto package = ResolvePackage(data); + if (!package) { + return X_ERROR_FILE_NOT_FOUND; + } + + if (!package->Mount(root_name)) { + return X_ERROR_DEVICE_NOT_CONNECTED; + } + + open_packages_.push_back(package); + + return X_ERROR_SUCCESS; +} + +X_RESULT ContentManager::CloseContent(std::string root_name) { + auto global_lock = global_critical_region_.Acquire(); + + for (auto it = open_packages_.begin(); it != open_packages_.end(); ++it) { + auto& root_names = (*it)->root_names(); + auto root = std::find(root_names.begin(), root_names.end(), root_name); + if (root != root_names.end()) { + if ((*it)->Unmount(root_name)) { + delete *it; + open_packages_.erase(it); + } + + return X_ERROR_SUCCESS; + } + } + + return X_ERROR_FILE_NOT_FOUND; +} + +X_RESULT ContentManager::GetContentThumbnail(const XCONTENT_DATA& data, + std::vector* buffer) { + auto global_lock = global_critical_region_.Acquire(); + + auto package = ResolvePackage(data); + if (!package) { + return X_ERROR_FILE_NOT_FOUND; + } + + return package->GetThumbnail(buffer); +} + +X_RESULT ContentManager::SetContentThumbnail(const XCONTENT_DATA& data, + std::vector buffer) { + auto global_lock = global_critical_region_.Acquire(); + + auto package = ResolvePackage(data); + if (!package) { + return X_ERROR_FILE_NOT_FOUND; + } + + return package->SetThumbnail(buffer); +} + +X_RESULT ContentManager::DeleteContent(const XCONTENT_DATA& data) { + auto global_lock = global_critical_region_.Acquire(); + +<<<<<<< HEAD + auto package_path = ResolvePackagePath(data); + if (xe::filesystem::PathExists(package_path)) { + if (xe::filesystem::IsFolder(package_path)) { + xe::filesystem::DeleteFolder(package_path); + } else { + // TODO: delete STFS package? + } + return X_ERROR_SUCCESS; + } else { +======= + auto package = ResolvePackage(data); + if (!package) { +>>>>>>> emoose/stfs-packages + return X_ERROR_FILE_NOT_FOUND; + } + + auto result = package->Delete(); + if (XSUCCEEDED(result)) { + auto it = std::find(open_packages_.begin(), open_packages_.end(), package); + if (it != open_packages_.end()) { + open_packages_.erase(it); + } + + delete package; + } + + return result; +} + +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()); + + // 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; +} + +} // namespace xam +} // namespace kernel +} // namespace xe diff --git a/src/xenia/kernel/xam/content_manager.h.orig b/src/xenia/kernel/xam/content_manager.h.orig new file mode 100644 index 000000000..260d47877 --- /dev/null +++ b/src/xenia/kernel/xam/content_manager.h.orig @@ -0,0 +1,108 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2015 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_KERNEL_XAM_CONTENT_MANAGER_H_ +#define XENIA_KERNEL_XAM_CONTENT_MANAGER_H_ + +#include +#include +#include +#include + +#include "xenia/base/memory.h" +#include "xenia/base/mutex.h" +#include "xenia/xbox.h" + +namespace xe { +namespace kernel { +class KernelState; +} // namespace kernel +} // namespace xe + +namespace xe { +namespace kernel { +namespace xam { + +class ContentPackage; + +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::string file_name; + + XCONTENT_DATA() = default; + explicit XCONTENT_DATA(const uint8_t* ptr) { + device_id = xe::load_and_swap(ptr + 0); + content_type = xe::load_and_swap(ptr + 4); + display_name = xe::load_and_swap(ptr + 8); + file_name = xe::load_and_swap(ptr + 8 + 128 * 2); + } + + void Write(uint8_t* ptr) { + xe::store_and_swap(ptr + 0, device_id); + xe::store_and_swap(ptr + 4, content_type); + xe::store_and_swap(ptr + 8, display_name); + xe::store_and_swap(ptr + 8 + 128 * 2, file_name); + } +}; + +class ContentManager { + public: + // Extension to append to folder path when searching for STFS headers + static constexpr const wchar_t* const kStfsHeadersExtension = L".headers.bin"; + + ContentManager(KernelState* kernel_state, std::wstring root_path); + ~ContentManager(); + + std::vector ListContent(uint32_t device_id, + uint32_t content_type); + + ContentPackage* ResolvePackage(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 GetContentThumbnail(const XCONTENT_DATA& data, + std::vector* buffer); + X_RESULT SetContentThumbnail(const XCONTENT_DATA& data, + std::vector buffer); + X_RESULT DeleteContent(const XCONTENT_DATA& data); + std::wstring ResolveGameUserContentPath(); + + void SetTitleIdOverride(uint32_t title_id) { title_id_override_ = title_id; } + + private: + uint32_t title_id(); + + std::wstring ResolvePackageRoot(uint32_t content_type); + std::wstring ResolvePackagePath(const XCONTENT_DATA& data); + + KernelState* kernel_state_; + std::wstring root_path_; + + // TODO(benvanik): remove use of global lock, it's bad here! + xe::global_critical_region global_critical_region_; +<<<<<<< HEAD + std::unordered_map open_packages_; + + uint32_t title_id_override_ = + 0; // can be used for games/apps that request content for other IDs +======= + std::vector open_packages_; +>>>>>>> emoose/stfs-packages +}; + +} // namespace xam +} // namespace kernel +} // namespace xe + +#endif // XENIA_KERNEL_XAM_CONTENT_MANAGER_H_ diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_modules.cc.orig b/src/xenia/kernel/xboxkrnl/xboxkrnl_modules.cc.orig new file mode 100644 index 000000000..2e0794874 --- /dev/null +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_modules.cc.orig @@ -0,0 +1,318 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2013 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/base/logging.h" +#include "xenia/cpu/processor.h" +#include "xenia/kernel/kernel_state.h" +#include "xenia/kernel/user_module.h" +#include "xenia/kernel/util/shim_utils.h" +#include "xenia/kernel/xboxkrnl/xboxkrnl_private.h" +#include "xenia/xbox.h" + +<<<<<<< HEAD +DEFINE_int32(game_language, 1, + "The language for the game to run in. 1=EN / 2=JP / 3=DE / 4=FR / " + "5=ES / 6=IT / 7=KR / 8=CN", + "General"); +======= +DEFINE_bool(xconfig_initial_setup, false, + "Enable the dashboard initial setup/OOBE", "Kernel"); +>>>>>>> emoose/dashboard + +namespace xe { +namespace kernel { +namespace xboxkrnl { + +X_STATUS xeExGetXConfigSetting(uint16_t category, uint16_t setting, + void* buffer, uint16_t buffer_size, + uint16_t* required_size) { + uint16_t setting_size = 0; + alignas(uint32_t) uint8_t value[4]; + + // TODO(benvanik): have real structs here that just get copied from. + // https://free60project.github.io/wiki/XConfig.html + // https://github.com/oukiar/freestyledash/blob/master/Freestyle/Tools/Generic/ExConfig.h + switch (category) { + case 0x0002: + // XCONFIG_SECURED_CATEGORY + switch (setting) { + case 0x0001: // XCONFIG_SECURED_MAC_ADDRESS (6 bytes) + return X_STATUS_SUCCESS; // Just return, easier than setting up code + // for different size configs + case 0x0002: // XCONFIG_SECURED_AV_REGION + setting_size = 4; + xe::store_and_swap(value, 0x00001000); // USA/Canada + break; + default: + assert_unhandled_case(setting); + return X_STATUS_INVALID_PARAMETER_2; + } + break; + case 0x0003: + // XCONFIG_USER_CATEGORY + switch (setting) { + case 0x0001: // XCONFIG_USER_TIME_ZONE_BIAS + case 0x0002: // XCONFIG_USER_TIME_ZONE_STD_NAME + case 0x0003: // XCONFIG_USER_TIME_ZONE_DLT_NAME + case 0x0004: // XCONFIG_USER_TIME_ZONE_STD_DATE + case 0x0005: // XCONFIG_USER_TIME_ZONE_DLT_DATE + case 0x0006: // XCONFIG_USER_TIME_ZONE_STD_BIAS + case 0x0007: // XCONFIG_USER_TIME_ZONE_DLT_BIAS + setting_size = 4; + // TODO(benvanik): get this value. + xe::store_and_swap(value, 0); + break; + case 0x0009: // XCONFIG_USER_LANGUAGE + setting_size = 4; + xe::store_and_swap(value, cvars::game_language); // English + break; + case 0x000A: // XCONFIG_USER_VIDEO_FLAGS + setting_size = 4; + xe::store_and_swap(value, 0x00040000); + break; + case 0x000C: // XCONFIG_USER_RETAIL_FLAGS + setting_size = 4; + // TODO(benvanik): get this value. + + // 0x40 = dashboard initial setup complete + xe::store_and_swap(value, + cvars::xconfig_initial_setup ? 0 : 0x40); + break; + case 0x000E: // XCONFIG_USER_COUNTRY + // Halo: Reach sub_82804888 - min 0x5, max 0x6E. + setting_size = 1; + // TODO(benvanik): get this value. + value[0] = 5; + break; + case 0x000F: // XCONFIG_USER_PC_FLAGS (parental control?) + setting_size = 1; + value[0] = 0; + break; + case 0x0010: // XCONFIG_USER_SMB_CONFIG (0x100 byte string) + // Just set the start of the buffer to 0 so that callers + // don't error from an un-inited buffer + setting_size = 4; + xe::store_and_swap(value, 0); + break; + default: + assert_unhandled_case(setting); + return X_STATUS_INVALID_PARAMETER_2; + } + break; + default: + assert_unhandled_case(category); + return X_STATUS_INVALID_PARAMETER_1; + } + + if (buffer) { + if (buffer_size < setting_size) { + return X_STATUS_BUFFER_TOO_SMALL; + } + std::memcpy(buffer, value, setting_size); + } else { + if (buffer_size) { + return X_STATUS_INVALID_PARAMETER_3; + } + } + if (required_size) { + *required_size = setting_size; + } + + return X_STATUS_SUCCESS; +} + +dword_result_t ExGetXConfigSetting(word_t category, word_t setting, + lpdword_t buffer_ptr, word_t buffer_size, + lpword_t required_size_ptr) { + uint16_t required_size = 0; + X_STATUS result = xeExGetXConfigSetting(category, setting, buffer_ptr, + buffer_size, &required_size); + + if (required_size_ptr) { + *required_size_ptr = required_size; + } + + return result; +} +DECLARE_XBOXKRNL_EXPORT1(ExGetXConfigSetting, kModules, kImplemented); + +dword_result_t XexCheckExecutablePrivilege(dword_t privilege) { + // BOOL + // DWORD Privilege + + // Privilege is bit position in xe_xex2_system_flags enum - so: + // Privilege=6 -> 0x00000040 -> XEX_SYSTEM_INSECURE_SOCKETS + uint32_t mask = 1 << privilege; + + auto module = kernel_state()->GetExecutableModule(); + if (!module) { + return 0; + } + + uint32_t flags = 0; + module->GetOptHeader(XEX_HEADER_SYSTEM_FLAGS, &flags); + + return (flags & mask) > 0; +} +DECLARE_XBOXKRNL_EXPORT1(XexCheckExecutablePrivilege, kModules, kImplemented); + +dword_result_t XexGetModuleHandle(lpstring_t module_name, + lpdword_t hmodule_ptr) { + object_ref module; + + if (!module_name) { + module = kernel_state()->GetExecutableModule(); + } else { + module = kernel_state()->GetModule(module_name); + } + + if (!module) { + *hmodule_ptr = 0; + return X_ERROR_NOT_FOUND; + } + + // NOTE: we don't retain the handle for return. + *hmodule_ptr = module->hmodule_ptr(); + + return X_ERROR_SUCCESS; +} +DECLARE_XBOXKRNL_EXPORT1(XexGetModuleHandle, kModules, kImplemented); + +dword_result_t XexGetModuleSection(lpvoid_t hmodule, lpstring_t name, + lpdword_t data_ptr, lpdword_t size_ptr) { + X_STATUS result = X_STATUS_SUCCESS; + + auto module = XModule::GetFromHModule(kernel_state(), hmodule); + if (module) { + uint32_t section_data = 0; + uint32_t section_size = 0; + result = module->GetSection(name, §ion_data, §ion_size); + if (XSUCCEEDED(result)) { + *data_ptr = section_data; + *size_ptr = section_size; + } + } else { + result = X_STATUS_INVALID_HANDLE; + } + + return result; +} +DECLARE_XBOXKRNL_EXPORT1(XexGetModuleSection, kModules, kImplemented); + +dword_result_t XexLoadImage(lpstring_t module_name, dword_t module_flags, + dword_t min_version, lpdword_t hmodule_ptr) { + X_STATUS result = X_STATUS_NO_SUCH_FILE; + + uint32_t hmodule = 0; + auto module = kernel_state()->GetModule(module_name); + 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); + if (user_module) { + user_module->Retain(); + hmodule = user_module->hmodule_ptr(); + result = X_STATUS_SUCCESS; + } + } + + // Increment the module's load count. + if (hmodule) { + auto ldr_data = + kernel_memory()->TranslateVirtual(hmodule); + ldr_data->load_count++; + } + + *hmodule_ptr = hmodule; + + return result; +} +DECLARE_XBOXKRNL_EXPORT1(XexLoadImage, kModules, kImplemented); + +dword_result_t XexUnloadImage(lpvoid_t hmodule) { + auto module = XModule::GetFromHModule(kernel_state(), hmodule); + if (!module) { + return X_STATUS_INVALID_HANDLE; + } + + // Can't unload kernel modules from user code. + if (module->module_type() != XModule::ModuleType::kKernelModule) { + auto ldr_data = hmodule.as(); + if (--ldr_data->load_count == 0) { + // No more references, free it. + module->Release(); + kernel_state()->object_table()->RemoveHandle(module->handle()); + } + } + + return X_STATUS_SUCCESS; +} +DECLARE_XBOXKRNL_EXPORT1(XexUnloadImage, kModules, kImplemented); + +dword_result_t XexGetProcedureAddress(lpvoid_t hmodule, dword_t ordinal, + lpdword_t out_function_ptr) { + // May be entry point? + assert_not_zero(ordinal); + + bool is_string_name = (ordinal & 0xFFFF0000) != 0; + auto string_name = + reinterpret_cast(kernel_memory()->TranslateVirtual(ordinal)); + + X_STATUS result = X_STATUS_INVALID_HANDLE; + + object_ref module; + if (!hmodule) { + module = kernel_state()->GetExecutableModule(); + } else { + module = XModule::GetFromHModule(kernel_state(), hmodule); + } + if (module) { + uint32_t ptr; + if (is_string_name) { + ptr = module->GetProcAddressByName(string_name); + } else { + ptr = module->GetProcAddressByOrdinal(ordinal); + } + if (ptr) { + *out_function_ptr = ptr; + result = X_STATUS_SUCCESS; + } else { + XELOGW("ERROR: XexGetProcedureAddress ordinal not found!"); + *out_function_ptr = 0; + result = X_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND; + } + } + + return result; +} +DECLARE_XBOXKRNL_EXPORT1(XexGetProcedureAddress, kModules, kImplemented); + +void ExRegisterTitleTerminateNotification( + pointer_t reg, dword_t create) { + if (create) { + // Adding. + kernel_state()->RegisterTitleTerminateNotification( + reg->notification_routine, reg->priority); + } else { + // Removing. + kernel_state()->RemoveTitleTerminateNotification(reg->notification_routine); + } +} +DECLARE_XBOXKRNL_EXPORT1(ExRegisterTitleTerminateNotification, kModules, + kImplemented); + +void RegisterModuleExports(xe::cpu::ExportResolver* export_resolver, + KernelState* kernel_state) {} + +} // namespace xboxkrnl +} // namespace kernel +} // namespace xe diff --git a/src/xenia/vfs/virtual_file_system.cc b/src/xenia/vfs/virtual_file_system.cc index aae42a1f0..15f37702b 100644 --- a/src/xenia/vfs/virtual_file_system.cc +++ b/src/xenia/vfs/virtual_file_system.cc @@ -66,6 +66,15 @@ bool VirtualFileSystem::UnregisterSymbolicLink(const std::string& path) { return true; } +bool VirtualFileSystem::IsSymbolicLink(const std::string& path) { + auto global_lock = global_critical_region_.Acquire(); + auto it = symlinks_.find(path); + if (it == symlinks_.end()) { + return false; + } + return true; +} + bool VirtualFileSystem::FindSymbolicLink(const std::string& path, std::string& target) { auto it = diff --git a/src/xenia/vfs/virtual_file_system.h b/src/xenia/vfs/virtual_file_system.h index 523299ad7..2d84525ac 100644 --- a/src/xenia/vfs/virtual_file_system.h +++ b/src/xenia/vfs/virtual_file_system.h @@ -33,6 +33,7 @@ class VirtualFileSystem { bool RegisterSymbolicLink(const std::string& path, const std::string& target); bool UnregisterSymbolicLink(const std::string& path); + bool VirtualFileSystem::IsSymbolicLink(const std::string& path); bool FindSymbolicLink(const std::string& path, std::string& target); Entry* ResolvePath(const std::string& path);