diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc index 36a56c946..92ddabe4f 100644 --- a/src/xenia/emulator.cc +++ b/src/xenia/emulator.cc @@ -42,6 +42,7 @@ #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" @@ -279,7 +280,7 @@ X_STATUS Emulator::LaunchXexFile(const std::filesystem::path& path) { // and then get that symlinked to game:\, so // -> game:\foo.xex - auto mount_path = "\\Device\\Harddisk0\\Partition0"; + auto mount_path = "\\Device\\Harddisk0\\Partition1"; // Register the local directory in the virtual filesystem. auto parent_path = path.parent_path(); @@ -672,6 +673,25 @@ static std::string format_version(xex2_version version) { X_STATUS Emulator::CompleteLaunch(const std::filesystem::path& path, const std::string_view module_path) { + // Setup NullDevices for raw HDD partition accesses + // Cache/STFC code baked into games tries reading/writing to these + // By using a NullDevice that just returns success to all IO requests it + // should allow games to believe cache/raw disk was accessed successfully + + // NOTE: this should probably be moved to xenia_main.cc, but right now we need + // to register the \Device\Harddisk0\ NullDevice _after_ the + // \Device\Harddisk0\Partition1 HostPathDevice, otherwise requests to + // Partition1 will go to this. Registering during CompleteLaunch allows us to + // make sure any HostPathDevices are ready beforehand. + // (see comment above cache:\ device registration for more info about why) + auto null_paths = {std::string("\\Partition0"), std::string("\\Cache0"), + std::string("\\Cache1")}; + auto null_device = + std::make_unique("\\Device\\Harddisk0", null_paths); + if (null_device->Initialize()) { + file_system_->RegisterDevice(std::move(null_device)); + } + // Reset state. title_id_ = std::nullopt; title_name_ = ""; diff --git a/src/xenia/vfs/devices/null_device.cc b/src/xenia/vfs/devices/null_device.cc new file mode 100644 index 000000000..e7b014d7b --- /dev/null +++ b/src/xenia/vfs/devices/null_device.cc @@ -0,0 +1,62 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2021 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), name_("NullDevice") {} + +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_view path) { + XELOGFS("NullDevice::ResolvePath({})", path); + + auto root = root_entry_.get(); + if (path.empty()) { + return root_entry_.get(); + } + + for (auto& child : root->children()) { + if (!strcasecmp(child->path().c_str(), path.data())) { + 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..6ac8f2a35 --- /dev/null +++ b/src/xenia/vfs/devices/null_device.h @@ -0,0 +1,57 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2021 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_view path) override; + + bool is_read_only() const override { return false; } + + const std::string& name() const override { return name_; } + uint32_t attributes() const override { return 0; } + uint32_t component_name_max_length() const override { return 40; } + + uint32_t total_allocation_units() const override { return 0x10; } + uint32_t available_allocation_units() const override { return 0x10; } + + // STFC/cache code seems to require the product of the next two to equal + // 0x10000 + uint32_t sectors_per_allocation_unit() const override { return 0x80; } + + // STFC requires <= 0x1000 + uint32_t bytes_per_sector() const override { return 0x200; } + + private: + std::string name_; + 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..45b511c40 --- /dev/null +++ b/src/xenia/vfs/devices/null_entry.cc @@ -0,0 +1,55 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2021 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..84f01cb95 --- /dev/null +++ b/src/xenia/vfs/devices/null_entry.h @@ -0,0 +1,42 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2021 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..e04fd8397 --- /dev/null +++ b/src/xenia/vfs/devices/null_file.cc @@ -0,0 +1,52 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2021 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..32a6be25c --- /dev/null +++ b/src/xenia/vfs/devices/null_file.h @@ -0,0 +1,40 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2021 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_