From 6b338d860a33ee1ec13736de084472902450d123 Mon Sep 17 00:00:00 2001 From: marecl Date: Wed, 5 Nov 2025 14:51:02 +0100 Subject: [PATCH] Windows shenanigans Modified FS init flow Added mk/rmdir, fixed close() Added (p)read/write Fixed path being treated as relative on windows Fixed incorrect resolution of mountpoint roots (requires validation) --- src/core/file_sys/hostio/host_io_win32.h | 12 +- .../file_sys/hostio/src/host_io_win32.cpp | 58 ++++++++++ src/core/file_sys/quasifs/src/quasifs.cpp | 7 +- .../quasifs/src/quasifs_partition.cpp | 6 +- .../file_sys/quasifs/src/quasifs_vdriver.cpp | 21 ++-- src/core/libraries/kernel/file_system.cpp | 2 +- src/emulator.cpp | 106 +++++++++--------- src/emulator.h | 2 +- 8 files changed, 140 insertions(+), 74 deletions(-) diff --git a/src/core/file_sys/hostio/host_io_win32.h b/src/core/file_sys/hostio/host_io_win32.h index 0a470b3d3..f00d3fbb7 100644 --- a/src/core/file_sys/hostio/host_io_win32.h +++ b/src/core/file_sys/hostio/host_io_win32.h @@ -91,14 +91,14 @@ public: s32 Truncate(const fs::path& path, u64 size) override; s32 FTruncate(const s32 fd, u64 size) override; - // s64 Write(const s32 fd, const void* buf, u64 count) override; - // s64 Read(const s32 fd, void* buf, u64 count) override; + s64 Write(const s32 fd, const void* buf, u64 count) override; + s64 Read(const s32 fd, void* buf, u64 count) override; - // s64 PWrite(const s32 fd, const void* buf, u64 count, u64 offset) override; - // s64 PRead(const s32 fd, void* buf, u64 count, u64 offset) override; + s64 PWrite(const s32 fd, const void* buf, u64 count, u64 offset) override; + s64 PRead(const s32 fd, void* buf, u64 count, u64 offset) override; - // s32 MKDir(const fs::path& path, u16 mode = 0755) override; - // s32 RMDir(const fs::path& path) override; + s32 MKDir(const fs::path& path, u16 mode = 0755) override; + s32 RMDir(const fs::path& path) override; // s32 Stat(const fs::path& path, Libraries::Kernel::OrbisKernelStat* statbuf) override; // s32 FStat(const s32 fd, Libraries::Kernel::OrbisKernelStat* statbuf) override; diff --git a/src/core/file_sys/hostio/src/host_io_win32.cpp b/src/core/file_sys/hostio/src/host_io_win32.cpp index 3d15616bd..7211247fc 100644 --- a/src/core/file_sys/hostio/src/host_io_win32.cpp +++ b/src/core/file_sys/hostio/src/host_io_win32.cpp @@ -25,6 +25,8 @@ s32 HostIO_Win32::Creat(const fs::path& path, u16 mode) { s32 HostIO_Win32::Close(const s32 fd) { errno = 0; + if (fd < 0) + return -EBADF; s32 status = _close(fd); return 0 == status ? 0 : -errno; } @@ -84,4 +86,60 @@ int HostIO_Win32::FTruncate(const s32 fd, u64 size) { return status >= 0 ? status : -errno; } +s64 HostIO_Win32::Write(const s32 fd, const void* buf, u64 count) { + errno = 0; + s32 status = _write(fd, buf, count); + return status >= 0 ? status : -errno; +} + +s64 HostIO_Win32::Read(const s32 fd, void* buf, u64 count) { + errno = 0; + s32 status = _read(fd, buf, count); + return status >= 0 ? status : -errno; +} + +s64 HostIO_Win32::PWrite(const s32 fd, const void* buf, u64 count, u64 offset) { + errno = 0; + s64 bak = LSeek(fd, 0, SeekOrigin::CURRENT); + if (bak < 0) + return -errno; + LSeek(fd, offset, SeekOrigin::ORIGIN); + + s32 status = _write(fd, buf, count); + + LSeek(fd, bak, SeekOrigin::ORIGIN); + return status >= 0 ? status : -errno; +} + +s64 HostIO_Win32::PRead(const s32 fd, void* buf, u64 count, u64 offset) { + errno = 0; + s64 bak = LSeek(fd, 0, SeekOrigin::CURRENT); + if (bak < 0) + return -errno; + LSeek(fd, offset, SeekOrigin::ORIGIN); + + s32 status = _read(fd, buf, count); + + LSeek(fd, bak, SeekOrigin::ORIGIN); + return status >= 0 ? status : -errno; +} + +s32 HostIO_Win32::MKDir(const fs::path& path, u16 mode) { + errno = 0; + s32 status = _wmkdir(path.c_str()); + return status >= 0 ? status : -errno; +} + +s32 HostIO_Win32::RMDir(const fs::path& path) { + errno = 0; + s32 status = _wrmdir(path.c_str()); + return status >= 0 ? status : -errno; +} + +// s32 HostIO_Win32::Stat(const fs::path& path, Libraries::Kernel::OrbisKernelStat* statbuf) {} +// s32 HostIO_Win32::FStat(const s32 fd, Libraries::Kernel::OrbisKernelStat* statbuf) {} + +// s32 HostIO_Win32::Chmod(const fs::path& path, u16 mode) {} +// s32 HostIO_Win32::FChmod(const s32 fd, u16 mode) {} + } // namespace HostIODriver \ No newline at end of file diff --git a/src/core/file_sys/quasifs/src/quasifs.cpp b/src/core/file_sys/quasifs/src/quasifs.cpp index 0561b8002..b448a6462 100644 --- a/src/core/file_sys/quasifs/src/quasifs.cpp +++ b/src/core/file_sys/quasifs/src/quasifs.cpp @@ -228,10 +228,10 @@ int QFS::ForceInsert(const fs::path& path, const std::string& name, inode_ptr no // DO NOT, AND I SWEAR D O N O T touch this function // Debugging it is a royal PITA int QFS::Resolve(const fs::path& path, Resolved& res) { - if (path.empty()) - return -QUASI_EINVAL; - if (path.is_relative()) + if (path.empty() || !path.string().starts_with("/")) return -QUASI_EINVAL; + // if (path.is_relative()) + // return -QUASI_EINVAL; // on return: // node - last element of the path (if exists) @@ -303,6 +303,7 @@ int QFS::Resolve(const fs::path& path, Resolved& res) { res.mountpoint = mounted_partition; res.parent = mntparent; res.node = mntroot; + res.local_path = "/"; res.leaf = "/"; if (iter_path != "/") diff --git a/src/core/file_sys/quasifs/src/quasifs_partition.cpp b/src/core/file_sys/quasifs/src/quasifs_partition.cpp index 55eabd2cf..2c8c94c87 100644 --- a/src/core/file_sys/quasifs/src/quasifs_partition.cpp +++ b/src/core/file_sys/quasifs/src/quasifs_partition.cpp @@ -84,11 +84,11 @@ inode_ptr Partition::GetInodeByFileno(fileno_t fileno) { // DO NOT, AND I SWEAR D O N O T touch this function // Debugging it is a royal PITA int Partition::Resolve(fs::path& path, Resolved& res) { - if (path.empty()) + if (path.empty() || !path.string().starts_with("/")) return -QUASI_EINVAL; - if (path.is_relative()) - return -QUASI_EBADF; + // if (path.is_relative()) + // return -QUASI_EBADF; if (path.string().size() >= 256) return -QUASI_ENAMETOOLONG; diff --git a/src/core/file_sys/quasifs/src/quasifs_vdriver.cpp b/src/core/file_sys/quasifs/src/quasifs_vdriver.cpp index a2f946d64..df7f393b0 100644 --- a/src/core/file_sys/quasifs/src/quasifs_vdriver.cpp +++ b/src/core/file_sys/quasifs/src/quasifs_vdriver.cpp @@ -72,15 +72,20 @@ s32 QFS::OperationImpl::Open(const fs::path& path, int flags, u16 mode) { int vio_status = 0; if (part->IsHostMounted()) { - fs::path host_path_target{}; - if (int hostpath_status = part->GetHostPath(host_path_target, res.local_path); - hostpath_status != 0) - return hostpath_status; - if (hio_status = qfs.hio_driver.Open(host_path_target, flags, mode); hio_status < 0) - // hosts operation must succeed in order to continue - return hio_status; - host_used = true; + if (nullptr == res.node || (res.node && !res.node->is_dir())) { + // if doesn't exist, creation/throwing becomes host's problem + // however we might want to open a dir for dirents, which would be suboptimal + fs::path host_path_target{}; + if (int hostpath_status = part->GetHostPath(host_path_target, res.local_path); + hostpath_status != 0) + return hostpath_status; + + if (hio_status = qfs.hio_driver.Open(host_path_target, flags, mode); hio_status < 0) + // hosts operation must succeed in order to continue + return hio_status; + host_used = true; + } } qfs.vio_driver.SetCtx(&res, host_used, nullptr); diff --git a/src/core/libraries/kernel/file_system.cpp b/src/core/libraries/kernel/file_system.cpp index 678a6a0c8..6da85ece7 100644 --- a/src/core/libraries/kernel/file_system.cpp +++ b/src/core/libraries/kernel/file_system.cpp @@ -552,7 +552,7 @@ static s64 GetDents(s32 fd, char* buf, u64 nbytes, s64* basep) { s32 result = f->node->getdents(buf, nbytes, basep); if (result < 0) { - ErrSceToPosix(result); + *__Error() = -result; return -1; } diff --git a/src/emulator.cpp b/src/emulator.cpp index 69f27f6fd..abf45fe06 100644 --- a/src/emulator.cpp +++ b/src/emulator.cpp @@ -88,38 +88,23 @@ Emulator::Emulator() { Emulator::~Emulator() {} -void Emulator::LoadFilesystem(const std::string& id) { - const auto& mount_data_dir = Common::FS::GetUserPath(Common::FS::PathType::GameDataDir) / id; - if (!std::filesystem::exists(mount_data_dir)) { - std::filesystem::create_directory(mount_data_dir); - } - const auto& mount_temp_dir = Common::FS::GetUserPath(Common::FS::PathType::TempDataDir) / id; - if (std::filesystem::exists(mount_temp_dir)) { - // Temp folder should be cleared on each boot. - std::filesystem::remove_all(mount_temp_dir); - } - std::filesystem::create_directory(mount_temp_dir); - const auto& mount_download_dir = - Common::FS::GetUserPath(Common::FS::PathType::DownloadDir) / id; - if (!std::filesystem::exists(mount_download_dir)) { - std::filesystem::create_directory(mount_download_dir); - } - +void Emulator::LoadFilesystem(const std::filesystem::path& game_folder) { auto* qfs = Common::Singleton::Instance(); + qfs->Operation.MKDir("/app0", 0555); + qfs->Operation.MKDir("/hostapp", 0555); + qfs::partition_ptr partition_app0 = qfs::Partition::Create(game_folder, 0555, 512, 65536); + + qfs->Mount("/app0", partition_app0, qfs::MountOptions::MOUNT_NOOPT); + qfs->Mount("/hostapp", partition_app0, + qfs::MountOptions::MOUNT_NOOPT | qfs::MountOptions::MOUNT_BIND); + qfs->Operation.Chmod("/", 0777); qfs::partition_ptr partition_av_contents = qfs::Partition::Create("", 0775, 512, 16384); qfs::partition_ptr partition_av_contents_photo = qfs::Partition::Create("", 0755, 4096, 32768); qfs::partition_ptr partition_av_contents_thumbs = qfs::Partition::Create("", 0755, 4096, 32768); qfs::partition_ptr partition_av_contents_video = qfs::Partition::Create("", 0755, 4096, 32768); - - qfs::partition_ptr partition_data = qfs::Partition::Create(mount_data_dir, 0777, 4096, 32768); qfs::partition_ptr partition_dev = qfs::Partition::Create("", 0755, 16384, 16384); - // no idea what are the block sizes for these 3 - qfs::partition_ptr partition_download = - qfs::Partition::Create(mount_download_dir, 0777, 512, 65536); - - qfs::partition_ptr partition_temp = qfs::Partition::Create(mount_temp_dir, 0777, 512, 16384); qfs->Operation.MKDir("/av_contents", 0775); qfs->Operation.MKDir("/av_contents/photo", 0755); @@ -138,13 +123,7 @@ void Emulator::LoadFilesystem(const std::string& id) { qfs::MountOptions::MOUNT_RW); qfs->Mount("/av_contents/video", partition_av_contents_video, qfs::MountOptions::MOUNT_RW); - qfs->Mount("/data", partition_data, qfs::MountOptions::MOUNT_RW); qfs->Mount("/dev", partition_dev, qfs::MountOptions::MOUNT_RW); - qfs->Mount("/download0", partition_download, qfs::MountOptions::MOUNT_RW); - - qfs->Mount("/temp", partition_temp, qfs::MountOptions::MOUNT_RW); - qfs->Mount("/temp0", partition_temp, - qfs::MountOptions::MOUNT_RW | qfs::MountOptions::MOUNT_BIND); // // Setup /dev @@ -184,17 +163,11 @@ void Emulator::LoadFilesystem(const std::string& id) { if (int fd_dev = qfs->Operation.Open("/dev/stderr", QUASI_O_WRONLY); fd_dev != 2) LOG_CRITICAL(Kernel_Fs, "XDXDXD 2 != {}", fd_dev); - // - - const auto& mount_captures_dir = Common::FS::GetUserPath(Common::FS::PathType::CapturesDir); - if (!std::filesystem::exists(mount_captures_dir)) { - std::filesystem::create_directory(mount_captures_dir); - } - VideoCore::SetOutputDir(mount_captures_dir, id); - qfs->SyncHost(); - // qfs::printTree(qfs->GetRoot(), "/"); + qfs::printTree(qfs->GetRoot(), "/"); + + return; } void Emulator::Run(std::filesystem::path file, std::vector args, @@ -232,15 +205,10 @@ void Emulator::Run(std::filesystem::path file, std::vector args, // Certain games may use /hostapp as well such as CUSA001100 mnt->Mount(game_folder, "/hostapp", true); - auto* qfs = Common::Singleton::Instance(); - qfs->Operation.MKDir("/app0", 0555); - qfs::partition_ptr partition_app0 = qfs::Partition::Create(game_folder, 0555, 512, 65536); - - qfs->Mount("/app0", partition_app0, qfs::MountOptions::MOUNT_NOOPT); - qfs->Mount("/hostapp", partition_app0, - qfs::MountOptions::MOUNT_NOOPT | qfs::MountOptions::MOUNT_BIND); + this->LoadFilesystem(game_folder); // can't sync here, otherwise all mountpoints would need to be updated one by one + auto* qfs = Common::Singleton::Instance(); std::filesystem::path param_sfo_path{}; { qfs::Resolved res{}; @@ -278,7 +246,41 @@ void Emulator::Run(std::filesystem::path file, std::vector args, psf_attributes.raw = *raw_attributes; } - this->LoadFilesystem(id); + const auto& mount_captures_dir = Common::FS::GetUserPath(Common::FS::PathType::CapturesDir); + if (!std::filesystem::exists(mount_captures_dir)) { + std::filesystem::create_directory(mount_captures_dir); + } + VideoCore::SetOutputDir(mount_captures_dir, id); + + const auto& mount_data_dir = Common::FS::GetUserPath(Common::FS::PathType::GameDataDir) / id; + if (!std::filesystem::exists(mount_data_dir)) { + std::filesystem::create_directory(mount_data_dir); + } + const auto& mount_download_dir = + Common::FS::GetUserPath(Common::FS::PathType::DownloadDir) / id; + if (!std::filesystem::exists(mount_download_dir)) { + std::filesystem::create_directory(mount_download_dir); + } + const auto& mount_temp_dir = Common::FS::GetUserPath(Common::FS::PathType::TempDataDir) / id; + if (std::filesystem::exists(mount_temp_dir)) { + // Temp folder should be cleared on each boot. + std::filesystem::remove_all(mount_temp_dir); + } + std::filesystem::create_directory(mount_temp_dir); + + qfs::partition_ptr partition_data = qfs::Partition::Create(mount_data_dir, 0777, 4096, 32768); + qfs::partition_ptr partition_download = + qfs::Partition::Create(mount_download_dir, 0777, 512, 65536); + qfs::partition_ptr partition_temp = qfs::Partition::Create(mount_temp_dir, 0777, 512, 16384); + qfs->Mount("/data", partition_data, qfs::MountOptions::MOUNT_RW); + qfs->Mount("/download0", partition_download, qfs::MountOptions::MOUNT_RW); + qfs->Mount("/temp", partition_temp, qfs::MountOptions::MOUNT_RW); + qfs->Mount("/temp0", partition_temp, + qfs::MountOptions::MOUNT_RW | qfs::MountOptions::MOUNT_BIND); + qfs->SyncHost("/data"); + qfs->SyncHost("/donwload0"); + qfs->SyncHost("/temp"); + qfs->SyncHost("/temp0"); Config::load(Common::FS::GetUserPath(Common::FS::PathType::CustomConfigs) / (id + ".toml"), true); @@ -419,14 +421,14 @@ void Emulator::Run(std::filesystem::path file, std::vector args, g_window = window.get(); - const auto& mount_data_dir = Common::FS::GetUserPath(Common::FS::PathType::GameDataDir) / id; + // const auto& mount_data_dir = Common::FS::GetUserPath(Common::FS::PathType::GameDataDir) / id; if (!std::filesystem::exists(mount_data_dir)) { std::filesystem::create_directory(mount_data_dir); } mnt->Mount(mount_data_dir, "/data"); // should just exist, manually create with game serial // Mounting temp folders - const auto& mount_temp_dir = Common::FS::GetUserPath(Common::FS::PathType::TempDataDir) / id; + //const auto& mount_temp_dir = Common::FS::GetUserPath(Common::FS::PathType::TempDataDir) / id; if (std::filesystem::exists(mount_temp_dir)) { // Temp folder should be cleared on each boot. std::filesystem::remove_all(mount_temp_dir); @@ -435,14 +437,14 @@ void Emulator::Run(std::filesystem::path file, std::vector args, mnt->Mount(mount_temp_dir, "/temp0"); mnt->Mount(mount_temp_dir, "/temp"); - const auto& mount_download_dir = - Common::FS::GetUserPath(Common::FS::PathType::DownloadDir) / id; + // const auto& mount_download_dir = + // Common::FS::GetUserPath(Common::FS::PathType::DownloadDir) / id; if (!std::filesystem::exists(mount_download_dir)) { std::filesystem::create_directory(mount_download_dir); } mnt->Mount(mount_download_dir, "/download0"); - const auto& mount_captures_dir = Common::FS::GetUserPath(Common::FS::PathType::CapturesDir); + // const auto& mount_captures_dir = Common::FS::GetUserPath(Common::FS::PathType::CapturesDir); if (!std::filesystem::exists(mount_captures_dir)) { std::filesystem::create_directory(mount_captures_dir); } diff --git a/src/emulator.h b/src/emulator.h index a6710bf1f..5a965a6c2 100644 --- a/src/emulator.h +++ b/src/emulator.h @@ -42,7 +42,7 @@ public: private: void LoadSystemModules(const std::string& game_serial); - void LoadFilesystem(const std::string& id); + void LoadFilesystem(const std::filesystem::path& game_folder); Core::MemoryManager* memory; Input::GameController* controller;