Merge cleanup and fixes

This commit is contained in:
Cancerous 2019-09-05 06:22:27 -04:00
parent 127b465ccc
commit ba46b6a6f8
24 changed files with 145 additions and 1687 deletions

View File

@ -14,7 +14,9 @@
#include "xenia/cpu/ppc/ppc_context.h"
#include "xenia/cpu/ppc/ppc_hir_builder.h"
DEFINE_bool(UE_HACK, true, "Hack for Unreal Engine 3 titles to run", "CPU");
DEFINE_bool(UE_Workaround, true,
"Workaround for Unreal Engine 3 titles to run, try disabling if other games have problems",
"CPU");
namespace xe {
namespace cpu {
@ -1083,7 +1085,7 @@ int InstrEmit_stfsx(PPCHIRBuilder& f, const InstrData& i) {
int InstrEmit_dcbf(PPCHIRBuilder& f, const InstrData& i) {
//UE Hack
if ((i.X.RB == 11) && (cvars::UE_HACK) && (f.CompareEQ(f.LoadGPR(i.X.RB), f.LoadConstantUint16(0xFEED)))) {
if ((i.X.RB == 11) && (cvars::UE_Workaround) && (f.CompareEQ(f.LoadGPR(i.X.RB), f.LoadConstantUint16(0xFEED)))) {
Value* val = f.Sub(f.LoadGPR(i.X.RB), f.LoadConstantUint64(0x0004));
f.StoreGPR(i.X.RB, val);
} else {

View File

@ -659,6 +659,7 @@ X_STATUS Emulator::CompleteLaunch(const std::wstring& path,
module->GetOptHeader(XEX_HEADER_EXECUTION_INFO, &info);
if (info) {
title_id_ = info->title_id;
xe::LogLineFormat(xe::LogLevel::Error, 'i', "Title ID : %.8X\n", title_id_);
}
// Try and load the resource database (xex only).

View File

@ -1,856 +0,0 @@
/**
******************************************************************************
* 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 <vector>
#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<const uint8_t*>(header) + opt_header.offset;
auto opt_exec_info =
reinterpret_cast<const xex2_opt_execution_info*>(opt_header_ptr);
return static_cast<uint32_t>(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<uint8_t> 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<UserModule>(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<uint32_t>(addr);
if (magic == 'XEX2') {
module_format_ = kModuleFormatXex;
} else if (magic == 0x7F454C46 /* 0x7F 'ELF' */) {
module_format_ = kModuleFormatElf;
} else {
auto magic16 = xe::load_and_swap<uint16_t>(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<cpu::XexModule>(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<cpu::ElfModule>(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<X_LDR_DATA_TABLE_ENTRY*>(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<const xex2_header*>(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> 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<UserModule>(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<const uint8_t*>(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<const xex2_opt_resource_info*>(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<const xex2_opt_bound_path*>(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<const xex2_opt_import_libraries*>(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<const uint8_t*>(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<const xex2_import_library*>(
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<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,
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<const xex2_opt_original_pe_name*>(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<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,
static_cast<uint16_t>(library.version_major),
static_cast<uint16_t>(library.version_minor),
static_cast<uint16_t>(library.version_build),
static_cast<uint16_t>(library.version_qfe));
}
} break;
case XEX_HEADER_TLS_INFO: {
sb.AppendFormat(" 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",
static_cast<uint32_t>(opt_tls_info->slot_count));
sb.AppendFormat(" Raw Data Address: %.8X\n",
static_cast<uint32_t>(opt_tls_info->raw_data_address));
sb.AppendFormat(" Data Size: %d\n",
static_cast<uint32_t>(opt_tls_info->data_size));
sb.AppendFormat(" Raw Data Size: %d\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",
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",
static_cast<uint32_t>(opt_header.value));
} break;
case XEX_HEADER_DEFAULT_HEAP_SIZE: {
sb.AppendFormat(" XEX_HEADER_DEFAULT_HEAP_SIZE: %d\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");
} break;
case XEX_HEADER_SYSTEM_FLAGS: {
sb.AppendFormat(" XEX_HEADER_SYSTEM_FLAGS: %.8X\n",
static_cast<uint32_t>(opt_header.value));
} break;
case XEX_HEADER_EXECUTION_INFO: {
sb.AppendFormat(" 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",
static_cast<uint32_t>(opt_exec_info->media_id));
sb.AppendFormat(" Title ID: %.8X\n",
static_cast<uint32_t>(opt_exec_info->title_id));
sb.AppendFormat(" Savegame ID: %.8X\n",
static_cast<uint32_t>(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<const xex2_opt_lan_key*>(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<const xex2_opt_data_directory*>(opt_header_ptr);
auto exe_address = xex_module()->base_address();
auto e = memory()->TranslateVirtual<const X_IMAGE_EXPORT_DIRECTORY*>(
exe_address + dir->offset);
auto e_base = reinterpret_cast<uintptr_t>(e);
// e->AddressOfX RVAs are relative to the IMAGE_EXPORT_DIRECTORY!
auto function_table =
reinterpret_cast<const uint32_t*>(e_base + e->AddressOfFunctions);
// Names relative to directory.
auto name_table =
reinterpret_cast<const uint32_t*>(e_base + e->AddressOfNames);
// Table of ordinals (by name).
auto ordinal_table = reinterpret_cast<const uint16_t*>(
e_base + e->AddressOfNameOrdinals);
for (uint32_t n = 0; n < e->NumberOfNames; n++) {
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);
}
} 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<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(),
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<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_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<float>(library->imports.size()) / 100.0f;
sb.AppendFormat(" Total: %4llu\n", library->imports.size());
sb.AppendFormat(" Known: %3d%% (%d known, %d unknown)\n",
static_cast<int>(known_count / total_count), known_count,
unknown_count);
sb.AppendFormat(
" Implemented: %3d%% (%d implemented, %d unimplemented)\n",
static_cast<int>(impl_count / total_count), impl_count, unimpl_count);
sb.AppendFormat("\n");
// Listing.
for (std::vector<cpu::XexModule::ImportLibraryFn>::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

View File

@ -110,7 +110,7 @@ X_RESULT XgiApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
}
case 0x000B0011: {
// TODO(PermaNull): reverse buffer contents.
//TEST
// TEST
XELOGD("XGISessionDelete");
return X_STATUS_SUCCESS;
}
@ -128,12 +128,12 @@ X_RESULT XgiApp::DispatchMessageSync(uint32_t message, uint32_t buffer_ptr,
return X_STATUS_SUCCESS;
}
case 0x000B0014: {
//TEST Gets Jetpac XBLA in game
// TEST Gets Jetpac XBLA in game
XELOGD("XGI_unknown");
return X_STATUS_SUCCESS;
}
case 0x000B0015: {
//TEST Gets Jetpac XBLA in game
// TEST Gets Jetpac XBLA in game
XELOGD("XGI_unknown");
return X_STATUS_SUCCESS;
}

View File

@ -28,9 +28,6 @@ constexpr const wchar_t* const ContentManager::kStfsHeadersExtension;
static const wchar_t* kGameUserContentDirName = L"profile";
static int content_device_id_ = 0;
ContentManager::ContentManager(KernelState* kernel_state,
std::wstring root_path)
: kernel_state_(kernel_state), root_path_(std::move(root_path)) {}
@ -54,6 +51,30 @@ std::wstring ContentManager::ResolvePackageRoot(uint32_t content_type) {
wchar_t content_type_str[9] = L"00000000";
std::swprintf(content_type_str, 9, L"%.8X", content_type);
std::wstring type_name;
switch (content_type) {
case 1:
// Save games.
type_name = L"00000001";
break;
case 2:
// DLC from the marketplace.
type_name = L"00000002";
break;
case 3:
// Publisher content?
type_name = L"00000003";
break;
case 0x000D0000:
// ???
type_name = L"000D0000";
break;
default:
type_name = L"00000000";
//assert_unhandled_case(data.content_type);
//return nullptr;
}
// Package root path:
// content_root/title_id/type_name/
auto package_root = xe::join_paths(

View File

@ -1,354 +0,0 @@
/**
******************************************************************************
* 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 <string>
#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<vfs::Device> 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<vfs::StfsContainerDevice>(device_path_, package_path);
} else {
device = std::make_unique<vfs::HostPathDevice>(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<XCONTENT_DATA> ContentManager::ListContent(uint32_t device_id,
uint32_t content_type) {
std::vector<XCONTENT_DATA> 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<uint32_t>(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<ContentPackage> 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<FolderContentPackage>(kernel_state_, data,
package_path);
} else {
package =
std::make_unique<StfsContentPackage>(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<uint8_t>* 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<uint8_t> 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

View File

@ -30,8 +30,6 @@ 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;

View File

@ -1,108 +0,0 @@
/**
******************************************************************************
* 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 <memory>
#include <string>
#include <unordered_map>
#include <vector>
#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<uint32_t>(ptr + 0);
content_type = xe::load_and_swap<uint32_t>(ptr + 4);
display_name = xe::load_and_swap<std::wstring>(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::string>(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<XCONTENT_DATA> 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<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();
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<std::string, ContentPackage*> open_packages_;
uint32_t title_id_override_ =
0; // can be used for games/apps that request content for other IDs
=======
std::vector<ContentPackage*> open_packages_;
>>>>>>> emoose/stfs-packages
};
} // namespace xam
} // namespace kernel
} // namespace xe
#endif // XENIA_KERNEL_XAM_CONTENT_MANAGER_H_

View File

@ -11,9 +11,7 @@
#include "xenia/kernel/kernel_state.h"
#include "xenia/kernel/util/shim_utils.h"
#include <gflags/gflags.h>
#include "xenia/base/cvar.h"
#include "xenia/base/clock.h"
#include "xenia/base/filesystem.h"
#include "xenia/base/logging.h"
@ -21,14 +19,12 @@
#include "xenia/kernel/util/crypto_utils.h"
#include "xenia/kernel/xam/user_profile.h"
DEFINE_string(profile_name, "XeniaUser", "Gamertag", "General");
namespace xe {
namespace kernel {
namespace xam {
DEFINE_string(profile_directory, "Content\\Profile\\",
"The directory to store profile data inside");
"The directory to store profile data inside", "Kernel");
constexpr uint32_t kDashboardID = 0xFFFE07D1;
@ -108,7 +104,7 @@ void UserProfile::EncryptAccountFile(const X_XAMACCOUNTINFO* input,
UserProfile::UserProfile() : dash_gpd_(kDashboardID) {
account_.xuid_online = 0xE000BABEBABEBABE;
wcscpy_s(account_.gamertag, cvars::profile_name);
wcscpy_s(account_.gamertag, L"XeniaUser");
// https://cs.rin.ru/forum/viewtopic.php?f=38&t=60668&hilit=gfwl+live&start=195
// https://github.com/arkem/py360/blob/master/py360/constants.py
@ -181,11 +177,11 @@ UserProfile::UserProfile() : dash_gpd_(kDashboardID) {
void UserProfile::LoadProfile() {
auto mmap_ =
MappedMemory::Open(xe::to_wstring(FLAGS_profile_directory) + L"Account",
MappedMemory::Open(xe::to_wstring(cvars::profile_directory) + L"Account",
MappedMemory::Mode::kRead);
if (mmap_) {
XELOGI("Loading Account file from path %sAccount",
FLAGS_profile_directory.c_str());
XELOGI("Loading Account file from path %SAccount",
xe::to_wstring(cvars::profile_directory).c_str());
X_XAMACCOUNTINFO tmp_acct;
bool success = DecryptAccountFile(mmap_->data(), &tmp_acct);
@ -203,10 +199,10 @@ void UserProfile::LoadProfile() {
mmap_->Close();
}
XELOGI("Loading profile GPDs from path %s", FLAGS_profile_directory.c_str());
XELOGI("Loading profile GPDs from path %S", xe::to_wstring(cvars::profile_directory).c_str());
mmap_ = MappedMemory::Open(
xe::to_wstring(FLAGS_profile_directory) + L"FFFE07D1.gpd",
xe::to_wstring(cvars::profile_directory) + L"FFFE07D1.gpd",
MappedMemory::Mode::kRead);
if (!mmap_) {
XELOGW(
@ -223,7 +219,7 @@ void UserProfile::LoadProfile() {
for (auto title : titles) {
wchar_t fname[256];
_swprintf(fname, L"%X.gpd", title.title_id);
mmap_ = MappedMemory::Open(xe::to_wstring(FLAGS_profile_directory) + fname,
mmap_ = MappedMemory::Open(xe::to_wstring(cvars::profile_directory) + fname,
MappedMemory::Mode::kRead);
if (!mmap_) {
XELOGE("Failed to open GPD for title %X (%s)!", title.title_id,
@ -451,16 +447,16 @@ bool UserProfile::UpdateGpd(uint32_t title_id, xdbf::GpdFile& gpd_data) {
return false;
}
if (!filesystem::PathExists(xe::to_wstring(FLAGS_profile_directory))) {
filesystem::CreateFolder(xe::to_wstring(FLAGS_profile_directory));
if (!filesystem::PathExists(xe::to_wstring(cvars::profile_directory))) {
filesystem::CreateFolder(xe::to_wstring(cvars::profile_directory));
}
wchar_t fname[256];
_swprintf(fname, L"%X.gpd", title_id);
filesystem::CreateFile(xe::to_wstring(FLAGS_profile_directory) + fname);
filesystem::CreateFile(xe::to_wstring(cvars::profile_directory) + fname);
auto mmap_ =
MappedMemory::Open(xe::to_wstring(FLAGS_profile_directory) + fname,
MappedMemory::Open(xe::to_wstring(cvars::profile_directory) + fname,
MappedMemory::Mode::kReadWrite, 0, gpd_length);
if (!mmap_) {
XELOGE("Failed to open %X.gpd for writing!", title_id);

View File

@ -313,7 +313,7 @@ class UserProfile {
uint64_t xuid() const { return account_.xuid_online; }
std::string name() const { return account_.GetGamertagString(); }
uint32_t signin_state() const { return 1; }
// uint32_t signin_state() const { return 1; }
void AddSetting(std::unique_ptr<Setting> setting);
Setting* GetSetting(uint32_t setting_id);

View File

@ -290,7 +290,9 @@ void XamLoaderLaunchTitle(lpstring_t raw_name, dword_t flags) {
auto& loader_data = xam->loader_data();
loader_data.launch_flags = flags;
XELOGI(
"XamLoaderLaunchTitle launching: (%S) with flags (%d)",
std::string(raw_name), flags);
// Translate the launch path to a full path.
if (raw_name && raw_name.value() == "") {
loader_data.launch_path = "game:\\default.xex";

View File

@ -182,7 +182,7 @@ X_HRESULT_result_t XamUserGetDeviceContext(dword_t user_index, dword_t unk,
// Games check the result - usually with some masking.
// If this function fails they assume zero, so let's fail AND
// set zero just to be safe.
*out_ptr = 0;
//*out_ptr = 0;
if (!user_index || (user_index & 0xFF) == 0xFF) {
return X_E_SUCCESS;
} else {

View File

@ -461,7 +461,7 @@ DECLARE_XAM_EXPORT1(NetDll_XNetXnAddrToMachineId, kNetworking, kStub);
void NetDll_XNetInAddrToString(dword_t caller, dword_t in_addr,
lpstring_t string_out, dword_t string_size) {
strncpy(string_out, "666.666.666.666", string_size);
strncpy(string_out, "127.0.0.1", string_size);
}
DECLARE_XAM_EXPORT1(NetDll_XNetInAddrToString, kNetworking, kStub);

View File

@ -18,7 +18,10 @@ namespace xe {
namespace kernel {
namespace xam {
dword_result_t XamNotifyCreateListenerInternal(qword_t mask) {
dword_result_t XamNotifyCreateListenerInternal(qword_t mask, dword_t unk,
dword_t one) {
// r4=1 may indicate user process?
auto listener =
object_ref<XNotifyListener>(new XNotifyListener(kernel_state()));
listener->Initialize(mask);
@ -28,10 +31,11 @@ dword_result_t XamNotifyCreateListenerInternal(qword_t mask) {
return handle;
}
DECLARE_XAM_EXPORT1(XamNotifyCreateListenerInternal, kNone, kImplemented);
DECLARE_XAM_EXPORT2(XamNotifyCreateListenerInternal, kNone, kImplemented,
kSketchy);
dword_result_t XamNotifyCreateListener(qword_t mask) {
return XamNotifyCreateListenerInternal(mask);
dword_result_t XamNotifyCreateListener(qword_t mask, dword_t one) {
return XamNotifyCreateListenerInternal(mask, 0, one);
}
DECLARE_XAM_EXPORT1(XamNotifyCreateListener, kNone, kImplemented);
@ -56,6 +60,9 @@ dword_result_t XNotifyGetNext(dword_t handle, dword_t match_id,
// Asking for a specific notification
id = match_id;
dequeued = listener->DequeueNotification(match_id, &param);
// TODO(Gliniak): Requires research. There is no such match_id!
if (!dequeued && !param)
dequeued = listener->DequeueNotification(&id, &param);
} else {
// Just get next.
dequeued = listener->DequeueNotification(&id, &param);
@ -71,7 +78,7 @@ dword_result_t XNotifyGetNext(dword_t handle, dword_t match_id,
return dequeued ? 1 : 0;
}
DECLARE_XAM_EXPORT1(XNotifyGetNext, kNone, kImplemented);
DECLARE_XAM_EXPORT2(XNotifyGetNext, kNone, kImplemented, kHighFrequency);
dword_result_t XNotifyDelayUI(dword_t delay_ms) {
// Ignored.

View File

@ -16,6 +16,11 @@
#include "xenia/kernel/xenumerator.h"
#include "xenia/kernel/xthread.h"
#include "xenia/xbox.h"
#include "xenia/base/cvar.h"
DEFINE_bool(signin_state, true,
"User signed in", "Kernel");
namespace xe {
namespace kernel {
@ -121,7 +126,7 @@ dword_result_t XamUserGetSigninState(dword_t user_index) {
if (user_index == 0 || (user_index & 0xFF) == 0xFF) {
const auto& user_profile = kernel_state()->user_profile();
return user_profile->signin_state();
return ((cvars::signin_state) ? 1 : 0);
} else {
return 0;
}
@ -152,7 +157,7 @@ X_HRESULT_result_t XamUserGetSigninInfo(dword_t user_index, dword_t flags,
const auto& user_profile = kernel_state()->user_profile();
info->xuid = user_profile->xuid();
info->signin_state = user_profile->signin_state();
info->signin_state = ((cvars::signin_state) ? 1 : 0);
std::strncpy(info->name, user_profile->name().data(), 15);
return X_E_SUCCESS;
}
@ -502,7 +507,7 @@ dword_result_t XamUserAreUsersFriends(dword_t user_index, dword_t unk1,
X_RESULT result = X_ERROR_SUCCESS;
const auto& user_profile = kernel_state()->user_profile();
if (user_profile->signin_state() == 0) {
if (((cvars::signin_state) ? 1 : 0) == 0) {
result = X_ERROR_NOT_LOGGED_ON;
} else {
// No friends!

View File

@ -434,17 +434,75 @@ void XeCryptHmacSha(lpvoid_t key, dword_t key_size_in, lpvoid_t inp_1,
dword_t inp_1_size, lpvoid_t inp_2, dword_t inp_2_size,
lpvoid_t inp_3, dword_t inp_3_size, lpvoid_t out,
dword_t out_size) {
util::HmacSha(key, key_size_in, inp_1, inp_1_size, inp_2, inp_2_size, inp_3,
inp_3_size, out, out_size);
uint32_t key_size = key_size_in;
sha1::SHA1 sha;
uint8_t kpad_i[0x40];
uint8_t kpad_o[0x40];
uint8_t tmp_key[0x40];
std::memset(kpad_i, 0x36, 0x40);
std::memset(kpad_o, 0x5C, 0x40);
// Setup HMAC key
// If > block size, use its hash
if (key_size > 0x40) {
sha1::SHA1 sha_key;
sha_key.processBytes(key, key_size);
sha_key.finalize((uint8_t*)tmp_key);
key_size = 0x14u;
} else {
std::memcpy(tmp_key, key, key_size);
}
for (uint32_t i = 0; i < key_size; i++) {
kpad_i[i] = tmp_key[i] ^ 0x36;
kpad_o[i] = tmp_key[i] ^ 0x5C;
}
// Inner
sha.processBytes(kpad_i, 0x40);
if (inp_1_size) {
sha.processBytes(inp_1, inp_1_size);
}
if (inp_2_size) {
sha.processBytes(inp_2, inp_2_size);
}
if (inp_3_size) {
sha.processBytes(inp_3, inp_3_size);
}
uint8_t digest[0x14];
sha.finalize(digest);
sha.reset();
// Outer
sha.processBytes(kpad_o, 0x40);
sha.processBytes(digest, 0x14);
sha.finalize(digest);
std::memcpy(out, digest, std::min((uint32_t)out_size, 0x14u));
}
DECLARE_XBOXKRNL_EXPORT1(XeCryptHmacSha, kNone, kImplemented);
// Keys
// TODO: Array of keys we need
// Retail key 0x19
static const uint8_t key19[] = {0xE1, 0xBC, 0x15, 0x9C, 0x73, 0xB1, 0xEA, 0xE9,
0xAB, 0x31, 0x70, 0xF3, 0xAD, 0x47, 0xEB, 0xF3};
dword_result_t XeKeysHmacSha(dword_t key_num, lpvoid_t inp_1,
dword_t inp_1_size, lpvoid_t inp_2,
dword_t inp_2_size, lpvoid_t inp_3,
dword_t inp_3_size, lpvoid_t out,
dword_t out_size) {
const uint8_t* key = util::GetXeKey(key_num);
const uint8_t* key = nullptr;
if (key_num == 0x19) {
key = key19;
}
if (key) {
XeCryptHmacSha((void*)key, 0x10, inp_1, inp_1_size, inp_2, inp_2_size,

View File

@ -136,8 +136,9 @@ DECLARE_XBOXKRNL_EXPORT2(RtlRaiseException, kDebug, kStub, kImportant);
void KeBugCheckEx(dword_t code, dword_t param1, dword_t param2, dword_t param3,
dword_t param4) {
XELOGD("*** STOP: 0x%.8X (0x%.8X, 0x%.8X, 0x%.8X, 0x%.8X)", code, param1,
XELOGE("*** STOP: 0x%.8X (0x%.8X, 0x%.8X, 0x%.8X, 0x%.8X)", code, param1,
param2, param3, param4);
XELOGE(" ### GUEST RAISE EXCEPTION - should have crashed here ###");
fflush(stdout);
//xe::debugging::Break();
assert_always();

View File

@ -14,14 +14,15 @@
#include "xenia/kernel/util/shim_utils.h"
#include "xenia/kernel/xboxkrnl/xboxkrnl_private.h"
#include "xenia/xbox.h"
#include "xenia/base/cvar.h"
DEFINE_bool(xconfig_initial_setup, false,
"Enable the dashboard initial setup/OOBE", "Kernel");
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");
namespace xe {
namespace kernel {
@ -90,7 +91,7 @@ X_STATUS xeExGetXConfigSetting(uint16_t category, uint16_t setting,
break;
case 0x000F: // XCONFIG_USER_PC_FLAGS (parental control?)
setting_size = 1;
value[0] = 0;
xe::store_and_swap<uint32_t>(value, 0);
break;
case 0x0010: // XCONFIG_USER_SMB_CONFIG (0x100 byte string)
// Just set the start of the buffer to 0 so that callers

View File

@ -1,318 +0,0 @@
/**
******************************************************************************
* 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<uint32_t>(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<uint32_t>(value, 0);
break;
case 0x0009: // XCONFIG_USER_LANGUAGE
setting_size = 4;
xe::store_and_swap<uint32_t>(value, cvars::game_language); // English
break;
case 0x000A: // XCONFIG_USER_VIDEO_FLAGS
setting_size = 4;
xe::store_and_swap<uint32_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(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<XModule> 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, &section_data, &section_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<X_LDR_DATA_TABLE_ENTRY*>(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<X_LDR_DATA_TABLE_ENTRY*>();
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<const char*>(kernel_memory()->TranslateVirtual(ordinal));
X_STATUS result = X_STATUS_INVALID_HANDLE;
object_ref<XModule> 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<X_EX_TITLE_TERMINATE_REGISTRATION> 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

View File

@ -137,6 +137,9 @@ dword_result_t ExCreateThread(lpdword_t handle_ptr, dword_t stack_size,
if (handle_ptr) {
if (creation_flags & 0x80) {
*handle_ptr = thread->guest_object();
} else if (!*handle_ptr && (creation_flags == X_CREATE_SUSPENDED)) {
// TODO(Gliniak): Temporary solution, requires more research // && !stack_size
*handle_ptr = thread->handle();
} else {
thread->RetainHandle();
*handle_ptr = thread->handle();

View File

@ -68,7 +68,7 @@ Emulator* XObject::emulator() const { return kernel_state_->emulator_; }
KernelState* XObject::kernel_state() const { return kernel_state_; }
Memory* XObject::memory() const { return kernel_state_->memory(); }
XObject::Type XObject::type() const { return type_; }
XObject::Type XObject::type() { return type_; }
void XObject::RetainHandle() {
kernel_state_->object_table()->RetainHandle(handles_[0]);

View File

@ -134,7 +134,7 @@ class XObject {
KernelState* kernel_state() const;
Memory* memory() const;
Type type() const;
Type type();
// Returns the primary handle of this object.
X_HANDLE handle() const { return handles_[0]; }

View File

@ -85,7 +85,6 @@ bool VirtualFileSystem::UnregisterSymbolicLink(const std::string& path) {
symlinks_.erase(it);
return true;
}
bool VirtualFileSystem::IsSymbolicLink(const std::string& path) {
auto global_lock = global_critical_region_.Acquire();
auto it = symlinks_.find(path);

View File

@ -34,7 +34,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 IsSymbolicLink(const std::string& path);
bool FindSymbolicLink(const std::string& path, std::string& target);
Entry* ResolvePath(const std::string& path);