Merge cleanup and fixes
This commit is contained in:
parent
127b465ccc
commit
ba46b6a6f8
|
@ -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 {
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
|
|
|
@ -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_
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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, ¶m);
|
||||
// TODO(Gliniak): Requires research. There is no such match_id!
|
||||
if (!dequeued && !param)
|
||||
dequeued = listener->DequeueNotification(&id, ¶m);
|
||||
} else {
|
||||
// Just get next.
|
||||
dequeued = listener->DequeueNotification(&id, ¶m);
|
||||
|
@ -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.
|
||||
|
|
|
@ -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!
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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, §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<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
|
|
@ -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();
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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]; }
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue