diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc index b3edd18c5..9cf17667f 100644 --- a/src/xenia/emulator.cc +++ b/src/xenia/emulator.cc @@ -41,9 +41,12 @@ #include "xenia/ui/imgui_dialog.h" #include "xenia/vfs/devices/disc_image_device.h" #include "xenia/vfs/devices/host_path_device.h" +#include "xenia/vfs/devices/null_device.h" #include "xenia/vfs/devices/stfs_container_device.h" #include "xenia/vfs/virtual_file_system.h" +DECLARE_bool(mount_cache); + DEFINE_double(time_scalar, 1.0, "Scalar used to speed or slow time (1x, 2x, 1/2x, etc).", "General"); @@ -650,6 +653,19 @@ std::string Emulator::FindLaunchModule() { X_STATUS Emulator::CompleteLaunch(const std::wstring& path, const std::string& module_path) { + if (cvars::mount_cache) { + // Below are accessed directly by STFC/cache code baked into the game + // By using a NullDevice that just returns success to all IO requests, the + // cache code should hopefully progress without erroring out + auto null_files = {std::string("\\Partition0"), std::string("\\Cache0"), + std::string("\\Cache1")}; + auto null_device = + std::make_unique("\\Device\\Harddisk0", null_files); + if (null_device->Initialize()) { + file_system_->RegisterDevice(std::move(null_device)); + } + } + // Reset state. title_id_ = 0; game_title_ = L""; diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc index e910bfcf2..39a0f3c93 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_io.cc @@ -795,6 +795,24 @@ dword_result_t FscSetCacheElementCount(dword_t unk_0, dword_t unk_1) { } DECLARE_XBOXKRNL_EXPORT1(FscSetCacheElementCount, kFileSystem, kStub); +dword_result_t NtDeviceIoControlFile( + dword_t handle, dword_t event_handle, dword_t apc_routine, + dword_t apc_context, dword_t io_status_block, dword_t io_control_code, + lpvoid_t input_buffer, dword_t input_buffer_len, lpvoid_t output_buffer, + dword_t output_buffer_len) { + // Called by STFS/cache code, seems to check the sanity of returned values - + // the values below appear to pass this check + if (io_control_code == 0x74004) { + xe::store_and_swap(output_buffer, 0); + xe::store_and_swap(output_buffer + 8, 0xFF000); + } else if (io_control_code == 0x70000) { + xe::store_and_swap(output_buffer, 0xFF000 / 512); + xe::store_and_swap(output_buffer + 4, 512); + } + return X_STATUS_SUCCESS; +} +DECLARE_XBOXKRNL_EXPORT1(NtDeviceIoControlFile, kFileSystem, kStub); + void RegisterIoExports(xe::cpu::ExportResolver* export_resolver, KernelState* kernel_state) {} diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_modules.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_modules.cc index 9098a1321..498b92225 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_modules.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_modules.cc @@ -32,6 +32,22 @@ dword_result_t XexCheckExecutablePrivilege(dword_t privilege) { return 0; } + if (privilege == 0xB) { // TitleInsecureUtilityDrive + // If this privilege is set, the cache-partition code baked into most + // games skips a huge chunk of device-init code (registering a custom + // STFC filesystem handler with the kernel, etc), and just symlinks the + // cache partition to the existing device directly (I guess on 360 this + // would probably make it FATX, explaining the 'Insecure' part of it) + + // Thanks to this skip we can easily take control of the cache partition + // ourselves, just by symlinking it before the game does! + + // TODO: check if this skip-code is actually available on every game that + // uses cache - it's possible that early/later SDKs might not have it, and + // we won't be able to rely on using this cheat for everything... + return 1; + } + uint32_t flags = 0; module->GetOptHeader(XEX_HEADER_SYSTEM_FLAGS, &flags); diff --git a/src/xenia/vfs/devices/null_device.cc b/src/xenia/vfs/devices/null_device.cc new file mode 100644 index 000000000..a27bff29a --- /dev/null +++ b/src/xenia/vfs/devices/null_device.cc @@ -0,0 +1,62 @@ +/** + ****************************************************************************** + * 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/vfs/devices/null_device.h" + +#include "xenia/base/filesystem.h" +#include "xenia/base/logging.h" +#include "xenia/base/math.h" +#include "xenia/kernel/xfile.h" +#include "xenia/vfs/devices/null_entry.h" + +namespace xe { +namespace vfs { + +NullDevice::NullDevice(const std::string& mount_path, + const std::initializer_list& null_paths) + : Device(mount_path), null_paths_(null_paths) {} + +NullDevice::~NullDevice() = default; + +bool NullDevice::Initialize() { + auto root_entry = new NullEntry(this, nullptr, mount_path_); + root_entry->attributes_ = kFileAttributeDirectory; + root_entry_ = std::unique_ptr(root_entry); + + for (auto path : null_paths_) { + auto child = NullEntry::Create(this, root_entry, path); + root_entry->children_.push_back(std::unique_ptr(child)); + } + return true; +} + +void NullDevice::Dump(StringBuffer* string_buffer) { + auto global_lock = global_critical_region_.Acquire(); + root_entry_->Dump(string_buffer, 0); +} + +Entry* NullDevice::ResolvePath(const std::string& path) { + XELOGFS("NullDevice::ResolvePath(%s)", path.c_str()); + + auto root = root_entry_.get(); + if (path.empty()) { + return root_entry_.get(); + } + + for (auto& child : root->children()) { + if (!strcasecmp(child->path().c_str(), path.c_str())) { + return child.get(); + } + } + + return nullptr; +} + +} // namespace vfs +} // namespace xe diff --git a/src/xenia/vfs/devices/null_device.h b/src/xenia/vfs/devices/null_device.h new file mode 100644 index 000000000..0a87e739f --- /dev/null +++ b/src/xenia/vfs/devices/null_device.h @@ -0,0 +1,49 @@ +/** + ****************************************************************************** + * 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. * + ****************************************************************************** + */ + +#ifndef XENIA_VFS_DEVICES_NULL_DEVICE_H_ +#define XENIA_VFS_DEVICES_NULL_DEVICE_H_ + +#include + +#include "xenia/vfs/device.h" + +namespace xe { +namespace vfs { + +class NullEntry; + +class NullDevice : public Device { + public: + NullDevice(const std::string& mount_path, + const std::initializer_list& null_paths); + ~NullDevice() override; + + bool Initialize() override; + void Dump(StringBuffer* string_buffer) override; + Entry* ResolvePath(const std::string& path) override; + + bool is_read_only() const override { return false; } + + uint32_t total_allocation_units() const override { return 128 * 1024; } + uint32_t available_allocation_units() const override { return 128 * 1024; } + + // STFC/cache code seems to require the product of these two to equal 0x10000! + uint32_t sectors_per_allocation_unit() const override { return 1; } + uint32_t bytes_per_sector() const override { return 0x10000; } + + private: + std::unique_ptr root_entry_; + std::vector null_paths_; +}; + +} // namespace vfs +} // namespace xe + +#endif // XENIA_VFS_DEVICES_NULL_DEVICE_H_ diff --git a/src/xenia/vfs/devices/null_entry.cc b/src/xenia/vfs/devices/null_entry.cc new file mode 100644 index 000000000..a53727b2a --- /dev/null +++ b/src/xenia/vfs/devices/null_entry.cc @@ -0,0 +1,55 @@ +/** + ****************************************************************************** + * 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/vfs/devices/null_entry.h" + +#include "xenia/base/filesystem.h" +#include "xenia/base/logging.h" +#include "xenia/base/mapped_memory.h" +#include "xenia/base/math.h" +#include "xenia/base/string.h" +#include "xenia/vfs/device.h" +#include "xenia/vfs/devices/null_file.h" + +namespace xe { +namespace vfs { + +NullEntry::NullEntry(Device* device, Entry* parent, std::string path) + : Entry(device, parent, path) {} + +NullEntry::~NullEntry() = default; + +NullEntry* NullEntry::Create(Device* device, Entry* parent, + const std::string& path) { + auto entry = new NullEntry(device, parent, path); + + entry->create_timestamp_ = 0; + entry->access_timestamp_ = 0; + entry->write_timestamp_ = 0; + + entry->attributes_ = kFileAttributeNormal; + + entry->size_ = 0; + entry->allocation_size_ = 0; + return entry; +} + +X_STATUS NullEntry::Open(uint32_t desired_access, File** out_file) { + if (is_read_only() && (desired_access & (FileAccess::kFileWriteData | + FileAccess::kFileAppendData))) { + XELOGE("Attempting to open file for write access on read-only device"); + return X_STATUS_ACCESS_DENIED; + } + + *out_file = new NullFile(desired_access, this); + return X_STATUS_SUCCESS; +} + +} // namespace vfs +} // namespace xe diff --git a/src/xenia/vfs/devices/null_entry.h b/src/xenia/vfs/devices/null_entry.h new file mode 100644 index 000000000..e936b82ee --- /dev/null +++ b/src/xenia/vfs/devices/null_entry.h @@ -0,0 +1,42 @@ +/** + ****************************************************************************** + * 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. * + ****************************************************************************** + */ + +#ifndef XENIA_VFS_DEVICES_NULL_ENTRY_H_ +#define XENIA_VFS_DEVICES_NULL_ENTRY_H_ + +#include + +#include "xenia/base/filesystem.h" +#include "xenia/vfs/entry.h" + +namespace xe { +namespace vfs { + +class NullDevice; + +class NullEntry : public Entry { + public: + NullEntry(Device* device, Entry* parent, std::string path); + ~NullEntry() override; + + static NullEntry* Create(Device* device, Entry* parent, + const std::string& path); + + X_STATUS Open(uint32_t desired_access, File** out_file) override; + + bool can_map() const override { return false; } + + private: + friend class NullDevice; +}; + +} // namespace vfs +} // namespace xe + +#endif // XENIA_VFS_DEVICES_NULL_ENTRY_H_ diff --git a/src/xenia/vfs/devices/null_file.cc b/src/xenia/vfs/devices/null_file.cc new file mode 100644 index 000000000..b26a4b6cf --- /dev/null +++ b/src/xenia/vfs/devices/null_file.cc @@ -0,0 +1,52 @@ +/** + ****************************************************************************** + * 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/vfs/devices/null_file.h" + +#include "xenia/vfs/devices/null_entry.h" + +namespace xe { +namespace vfs { + +NullFile::NullFile(uint32_t file_access, NullEntry* entry) + : File(file_access, entry) {} + +NullFile::~NullFile() = default; + +void NullFile::Destroy() { delete this; } + +X_STATUS NullFile::ReadSync(void* buffer, size_t buffer_length, + size_t byte_offset, size_t* out_bytes_read) { + if (!(file_access_ & FileAccess::kFileReadData)) { + return X_STATUS_ACCESS_DENIED; + } + + return X_STATUS_SUCCESS; +} + +X_STATUS NullFile::WriteSync(const void* buffer, size_t buffer_length, + size_t byte_offset, size_t* out_bytes_written) { + if (!(file_access_ & + (FileAccess::kFileWriteData | FileAccess::kFileAppendData))) { + return X_STATUS_ACCESS_DENIED; + } + + return X_STATUS_SUCCESS; +} + +X_STATUS NullFile::SetLength(size_t length) { + if (!(file_access_ & FileAccess::kFileWriteData)) { + return X_STATUS_ACCESS_DENIED; + } + + return X_STATUS_SUCCESS; +} + +} // namespace vfs +} // namespace xe diff --git a/src/xenia/vfs/devices/null_file.h b/src/xenia/vfs/devices/null_file.h new file mode 100644 index 000000000..19bd181f6 --- /dev/null +++ b/src/xenia/vfs/devices/null_file.h @@ -0,0 +1,40 @@ +/** + ****************************************************************************** + * 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. * + ****************************************************************************** + */ + +#ifndef XENIA_VFS_DEVICES_NULL_FILE_H_ +#define XENIA_VFS_DEVICES_NULL_FILE_H_ + +#include + +#include "xenia/base/filesystem.h" +#include "xenia/vfs/file.h" + +namespace xe { +namespace vfs { + +class NullEntry; + +class NullFile : public File { + public: + NullFile(uint32_t file_access, NullEntry* entry); + ~NullFile() override; + + void Destroy() override; + + X_STATUS ReadSync(void* buffer, size_t buffer_length, size_t byte_offset, + size_t* out_bytes_read) override; + X_STATUS WriteSync(const void* buffer, size_t buffer_length, + size_t byte_offset, size_t* out_bytes_written) override; + X_STATUS SetLength(size_t length) override; +}; + +} // namespace vfs +} // namespace xe + +#endif // XENIA_VFS_DEVICES_NULL_FILE_H_