diff options
28 files changed, 697 insertions, 1064 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 5dee41abc..5e904a53f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,8 @@ CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_SDL2 "Download bundled SDL2 binaries" ON option(ENABLE_QT "Enable the Qt frontend" ON) CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" ON "ENABLE_SDL2;MSVC" OFF) +option(YUZU_USE_BUNDLED_UNICORN "Build/Download bundled Unicorn" ON) + if(NOT EXISTS ${CMAKE_SOURCE_DIR}/.git/hooks/pre-commit) message(STATUS "Copying pre-commit hook") file(COPY hooks/pre-commit @@ -209,8 +211,7 @@ else() endif() # If unicorn isn't found, msvc -> download bundled unicorn; everyone else -> build external -find_package(Unicorn QUIET) -if (NOT UNICORN_FOUND) +if (YUZU_USE_BUNDLED_UNICORN) if (MSVC) message(STATUS "unicorn not found, falling back to bundled") # Detect toolchain and platform @@ -249,7 +250,7 @@ if (NOT UNICORN_FOUND) find_package(PythonInterp 2.7 REQUIRED) add_custom_command(OUTPUT ${LIBUNICORN_LIBRARY} - COMMAND ${CMAKE_COMMAND} -E env UNICORN_ARCHS="aarch64" PYTHON="${PYTHON_EXECUTABLE}" /bin/sh make.sh + COMMAND ${CMAKE_COMMAND} -E env UNICORN_ARCHS="aarch64" PYTHON="${PYTHON_EXECUTABLE}" /bin/sh make.sh macos-universal-no WORKING_DIRECTORY ${UNICORN_PREFIX} ) # ALL makes this custom target build every time @@ -259,6 +260,8 @@ if (NOT UNICORN_FOUND) ) unset(UNICORN_LIB_NAME) endif() +else() + find_package(Unicorn REQUIRED) endif() if (UNICORN_FOUND) diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 1af80769a..d132ab969 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -17,7 +17,7 @@ if ($ENV{CI}) string(SUBSTRING ${WORD} 1 -1 REMAINDER) string(TOUPPER ${FIRST_LETTER} FIRST_LETTER) # this leaves a trailing space on the last word, but we actually want that - # because of how its styled in the title bar. + # because of how it's styled in the title bar. set(REPO_NAME "${REPO_NAME}${FIRST_LETTER}${REMAINDER} ") endforeach() endif() diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 7153c4f3f..433e7e596 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -6,21 +6,17 @@ add_library(core STATIC core.h core_timing.cpp core_timing.h - file_sys/archive_backend.cpp - file_sys/archive_backend.h - file_sys/directory_backend.h - file_sys/disk_archive.cpp - file_sys/disk_archive.h + file_sys/directory.h file_sys/errors.h - file_sys/file_backend.h - file_sys/ivfc_archive.cpp - file_sys/ivfc_archive.h + file_sys/filesystem.cpp + file_sys/filesystem.h file_sys/path_parser.cpp file_sys/path_parser.h - file_sys/savedata_archive.cpp - file_sys/savedata_archive.h - file_sys/title_metadata.cpp - file_sys/title_metadata.h + file_sys/romfs_factory.cpp + file_sys/romfs_factory.h + file_sys/romfs_filesystem.cpp + file_sys/romfs_filesystem.h + file_sys/storage.h frontend/emu_window.cpp frontend/emu_window.h frontend/framebuffer_layout.cpp @@ -101,6 +97,10 @@ add_library(core STATIC hle/service/audio/audio.h hle/service/audio/audout_u.cpp hle/service/audio/audout_u.h + hle/service/filesystem/filesystem.cpp + hle/service/filesystem/filesystem.h + hle/service/filesystem/fsp_srv.cpp + hle/service/filesystem/fsp_srv.h hle/service/hid/hid.cpp hle/service/hid/hid.h hle/service/lm/lm.cpp diff --git a/src/core/file_sys/directory_backend.h b/src/core/file_sys/directory.h index 0c93f2074..5a40bf472 100644 --- a/src/core/file_sys/directory_backend.h +++ b/src/core/file_sys/directory.h @@ -1,4 +1,4 @@ -// Copyright 2014 Citra Emulator Project +// Copyright 2018 yuzu emulator team // Licensed under GPLv2 or any later version // Refer to the license.txt file included. diff --git a/src/core/file_sys/disk_archive.cpp b/src/core/file_sys/disk_archive.cpp deleted file mode 100644 index 98d80aabc..000000000 --- a/src/core/file_sys/disk_archive.cpp +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <algorithm> -#include <cstdio> -#include <memory> -#include "common/common_types.h" -#include "common/file_util.h" -#include "common/logging/log.h" -#include "core/file_sys/disk_archive.h" -#include "core/file_sys/errors.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// FileSys namespace - -namespace FileSys { - -ResultVal<size_t> DiskFile::Read(const u64 offset, const size_t length, u8* buffer) const { - if (!mode.read_flag) - return ERROR_INVALID_OPEN_FLAGS; - - file->Seek(offset, SEEK_SET); - return MakeResult<size_t>(file->ReadBytes(buffer, length)); -} - -ResultVal<size_t> DiskFile::Write(const u64 offset, const size_t length, const bool flush, - const u8* buffer) const { - if (!mode.write_flag) - return ERROR_INVALID_OPEN_FLAGS; - - file->Seek(offset, SEEK_SET); - size_t written = file->WriteBytes(buffer, length); - if (flush) - file->Flush(); - return MakeResult<size_t>(written); -} - -u64 DiskFile::GetSize() const { - return file->GetSize(); -} - -bool DiskFile::SetSize(const u64 size) const { - file->Resize(size); - file->Flush(); - return true; -} - -bool DiskFile::Close() const { - return file->Close(); -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -DiskDirectory::DiskDirectory(const std::string& path) : directory() { - unsigned size = FileUtil::ScanDirectoryTree(path, directory); - directory.size = size; - directory.isDirectory = true; - children_iterator = directory.children.begin(); -} - -u32 DiskDirectory::Read(const u32 count, Entry* entries) { - u32 entries_read = 0; - - while (entries_read < count && children_iterator != directory.children.cend()) { - const FileUtil::FSTEntry& file = *children_iterator; - const std::string& filename = file.virtualName; - Entry& entry = entries[entries_read]; - - LOG_TRACE(Service_FS, "File %s: size=%llu dir=%d", filename.c_str(), file.size, - file.isDirectory); - - // TODO(Link Mauve): use a proper conversion to UTF-16. - for (size_t j = 0; j < FILENAME_LENGTH; ++j) { - entry.filename[j] = filename[j]; - if (!filename[j]) - break; - } - - FileUtil::SplitFilename83(filename, entry.short_name, entry.extension); - - entry.is_directory = file.isDirectory; - entry.is_hidden = (filename[0] == '.'); - entry.is_read_only = 0; - entry.file_size = file.size; - - // We emulate a SD card where the archive bit has never been cleared, as it would be on - // most user SD cards. - // Some homebrews (blargSNES for instance) are known to mistakenly use the archive bit as a - // file bit. - entry.is_archive = !file.isDirectory; - - ++entries_read; - ++children_iterator; - } - return entries_read; -} - -} // namespace FileSys diff --git a/src/core/file_sys/disk_archive.h b/src/core/file_sys/disk_archive.h deleted file mode 100644 index eb9166df6..000000000 --- a/src/core/file_sys/disk_archive.h +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <cstddef> -#include <memory> -#include <string> -#include <vector> -#include "common/common_types.h" -#include "common/file_util.h" -#include "core/file_sys/archive_backend.h" -#include "core/file_sys/directory_backend.h" -#include "core/file_sys/file_backend.h" -#include "core/hle/result.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// FileSys namespace - -namespace FileSys { - -class DiskFile : public FileBackend { -public: - DiskFile(FileUtil::IOFile&& file_, const Mode& mode_) - : file(new FileUtil::IOFile(std::move(file_))) { - mode.hex = mode_.hex; - } - - ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override; - ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override; - u64 GetSize() const override; - bool SetSize(u64 size) const override; - bool Close() const override; - - void Flush() const override { - file->Flush(); - } - -protected: - Mode mode; - std::unique_ptr<FileUtil::IOFile> file; -}; - -class DiskDirectory : public DirectoryBackend { -public: - DiskDirectory(const std::string& path); - - ~DiskDirectory() override { - Close(); - } - - u32 Read(const u32 count, Entry* entries) override; - - bool Close() const override { - return true; - } - -protected: - u32 total_entries_in_directory; - FileUtil::FSTEntry directory; - - // We need to remember the last entry we returned, so a subsequent call to Read will continue - // from the next one. This iterator will always point to the next unread entry. - std::vector<FileUtil::FSTEntry>::iterator children_iterator; -}; - -} // namespace FileSys diff --git a/src/core/file_sys/archive_backend.cpp b/src/core/file_sys/filesystem.cpp index fc472b44f..82fdb3c46 100644 --- a/src/core/file_sys/archive_backend.cpp +++ b/src/core/file_sys/filesystem.cpp @@ -1,4 +1,4 @@ -// Copyright 2015 Citra Emulator Project +// Copyright 2018 yuzu emulator team // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -7,7 +7,7 @@ #include <sstream> #include "common/logging/log.h" #include "common/string_util.h" -#include "core/file_sys/archive_backend.h" +#include "core/file_sys/filesystem.h" #include "core/memory.h" namespace FileSys { diff --git a/src/core/file_sys/archive_backend.h b/src/core/file_sys/filesystem.h index 58f6c150c..02705506b 100644 --- a/src/core/file_sys/archive_backend.h +++ b/src/core/file_sys/filesystem.h @@ -1,4 +1,4 @@ -// Copyright 2014 Citra Emulator Project +// Copyright 2018 yuzu emulator team // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -15,7 +15,7 @@ namespace FileSys { -class FileBackend; +class StorageBackend; class DirectoryBackend; // Path string type @@ -71,9 +71,9 @@ struct ArchiveFormatInfo { }; static_assert(std::is_pod<ArchiveFormatInfo>::value, "ArchiveFormatInfo is not POD"); -class ArchiveBackend : NonCopyable { +class FileSystemBackend : NonCopyable { public: - virtual ~ArchiveBackend() {} + virtual ~FileSystemBackend() {} /** * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.) @@ -81,13 +81,12 @@ public: virtual std::string GetName() const = 0; /** - * Open a file specified by its path, using the specified mode - * @param path Path relative to the archive - * @param mode Mode to open the file with - * @return Opened file, or error code + * Create a file specified by its path + * @param path Path relative to the Archive + * @param size The size of the new file, filled with zeroes + * @return Result of the operation */ - virtual ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, - const Mode& mode) const = 0; + virtual ResultCode CreateFile(const Path& path, u64 size) const = 0; /** * Delete a file specified by its path @@ -97,12 +96,11 @@ public: virtual ResultCode DeleteFile(const Path& path) const = 0; /** - * Rename a File specified by its path - * @param src_path Source path relative to the archive - * @param dest_path Destination path relative to the archive + * Create a directory specified by its path + * @param path Path relative to the archive * @return Result of the operation */ - virtual ResultCode RenameFile(const Path& src_path, const Path& dest_path) const = 0; + virtual ResultCode CreateDirectory(const Path& path) const = 0; /** * Delete a directory specified by its path @@ -119,19 +117,12 @@ public: virtual ResultCode DeleteDirectoryRecursively(const Path& path) const = 0; /** - * Create a file specified by its path - * @param path Path relative to the Archive - * @param size The size of the new file, filled with zeroes - * @return Result of the operation - */ - virtual ResultCode CreateFile(const Path& path, u64 size) const = 0; - - /** - * Create a directory specified by its path - * @param path Path relative to the archive + * Rename a File specified by its path + * @param src_path Source path relative to the archive + * @param dest_path Destination path relative to the archive * @return Result of the operation */ - virtual ResultCode CreateDirectory(const Path& path) const = 0; + virtual ResultCode RenameFile(const Path& src_path, const Path& dest_path) const = 0; /** * Rename a Directory specified by its path @@ -142,6 +133,15 @@ public: virtual ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const = 0; /** + * Open a file specified by its path, using the specified mode + * @param path Path relative to the archive + * @param mode Mode to open the file with + * @return Opened file, or error code + */ + virtual ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const Path& path, + const Mode& mode) const = 0; + + /** * Open a directory specified by its path * @param path Path relative to the archive * @return Opened directory, or error code @@ -152,12 +152,12 @@ public: * Get the free space * @return The number of free bytes in the archive */ - virtual u64 GetFreeBytes() const = 0; + virtual u64 GetFreeSpaceSize() const = 0; }; -class ArchiveFactory : NonCopyable { +class FileSystemFactory : NonCopyable { public: - virtual ~ArchiveFactory() {} + virtual ~FileSystemFactory() {} /** * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.) @@ -169,7 +169,7 @@ public: * @param path Path to the archive * @return An ArchiveBackend corresponding operating specified archive path. */ - virtual ResultVal<std::unique_ptr<ArchiveBackend>> Open(const Path& path) = 0; + virtual ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) = 0; /** * Deletes the archive contents and then re-creates the base folder diff --git a/src/core/file_sys/path_parser.h b/src/core/file_sys/path_parser.h index b9f52f65d..184f59d55 100644 --- a/src/core/file_sys/path_parser.h +++ b/src/core/file_sys/path_parser.h @@ -6,7 +6,7 @@ #include <string> #include <vector> -#include "core/file_sys/archive_backend.h" +#include "core/file_sys/filesystem.h" namespace FileSys { diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp new file mode 100644 index 000000000..e0de49f05 --- /dev/null +++ b/src/core/file_sys/romfs_factory.cpp @@ -0,0 +1,38 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include <memory> +#include "common/common_types.h" +#include "common/logging/log.h" +#include "core/file_sys/romfs_factory.h" +#include "core/file_sys/romfs_filesystem.h" + +namespace FileSys { + +RomFS_Factory::RomFS_Factory(Loader::AppLoader& app_loader) { + // Load the RomFS from the app + if (Loader::ResultStatus::Success != app_loader.ReadRomFS(romfs_file, data_offset, data_size)) { + LOG_ERROR(Service_FS, "Unable to read RomFS!"); + } +} + +ResultVal<std::unique_ptr<FileSystemBackend>> RomFS_Factory::Open(const Path& path) { + auto archive = std::make_unique<RomFS_FileSystem>(romfs_file, data_offset, data_size); + return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive)); +} + +ResultCode RomFS_Factory::Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) { + LOG_ERROR(Service_FS, "Unimplemented Format archive %s", GetName().c_str()); + // TODO(bunnei): Find the right error code for this + return ResultCode(-1); +} + +ResultVal<ArchiveFormatInfo> RomFS_Factory::GetFormatInfo(const Path& path) const { + LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive %s", GetName().c_str()); + // TODO(bunnei): Find the right error code for this + return ResultCode(-1); +} + +} // namespace FileSys diff --git a/src/core/file_sys/romfs_factory.h b/src/core/file_sys/romfs_factory.h new file mode 100644 index 000000000..10ea13966 --- /dev/null +++ b/src/core/file_sys/romfs_factory.h @@ -0,0 +1,35 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <string> +#include <vector> +#include "common/common_types.h" +#include "core/file_sys/filesystem.h" +#include "core/hle/result.h" +#include "core/loader/loader.h" + +namespace FileSys { + +/// File system interface to the RomFS archive +class RomFS_Factory final : public FileSystemFactory { +public: + explicit RomFS_Factory(Loader::AppLoader& app_loader); + + std::string GetName() const override { + return "ArchiveFactory_RomFS"; + } + ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) override; + ResultCode Format(const Path& path, const FileSys::ArchiveFormatInfo& format_info) override; + ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override; + +private: + std::shared_ptr<FileUtil::IOFile> romfs_file; + u64 data_offset; + u64 data_size; +}; + +} // namespace FileSys diff --git a/src/core/file_sys/ivfc_archive.cpp b/src/core/file_sys/romfs_filesystem.cpp index b3c3f2c6f..ca1463d7c 100644 --- a/src/core/file_sys/ivfc_archive.cpp +++ b/src/core/file_sys/romfs_filesystem.cpp @@ -1,4 +1,4 @@ -// Copyright 2014 Citra Emulator Project +// Copyright 2018 yuzu emulator team // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -6,84 +6,80 @@ #include <memory> #include "common/common_types.h" #include "common/logging/log.h" -#include "core/file_sys/ivfc_archive.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// FileSys namespace +#include "core/file_sys/romfs_filesystem.h" namespace FileSys { -std::string IVFCArchive::GetName() const { - return "IVFC"; +std::string RomFS_FileSystem::GetName() const { + return "RomFS"; } -ResultVal<std::unique_ptr<FileBackend>> IVFCArchive::OpenFile(const Path& path, - const Mode& mode) const { - return MakeResult<std::unique_ptr<FileBackend>>( - std::make_unique<IVFCFile>(romfs_file, data_offset, data_size)); +ResultVal<std::unique_ptr<StorageBackend>> RomFS_FileSystem::OpenFile(const Path& path, + const Mode& mode) const { + return MakeResult<std::unique_ptr<StorageBackend>>( + std::make_unique<RomFS_Storage>(romfs_file, data_offset, data_size)); } -ResultCode IVFCArchive::DeleteFile(const Path& path) const { - LOG_CRITICAL(Service_FS, "Attempted to delete a file from an IVFC archive (%s).", +ResultCode RomFS_FileSystem::DeleteFile(const Path& path) const { + LOG_CRITICAL(Service_FS, "Attempted to delete a file from an ROMFS archive (%s).", GetName().c_str()); // TODO(bunnei): Use correct error code return ResultCode(-1); } -ResultCode IVFCArchive::RenameFile(const Path& src_path, const Path& dest_path) const { - LOG_CRITICAL(Service_FS, "Attempted to rename a file within an IVFC archive (%s).", +ResultCode RomFS_FileSystem::RenameFile(const Path& src_path, const Path& dest_path) const { + LOG_CRITICAL(Service_FS, "Attempted to rename a file within an ROMFS archive (%s).", GetName().c_str()); // TODO(wwylele): Use correct error code return ResultCode(-1); } -ResultCode IVFCArchive::DeleteDirectory(const Path& path) const { - LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an IVFC archive (%s).", +ResultCode RomFS_FileSystem::DeleteDirectory(const Path& path) const { + LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an ROMFS archive (%s).", GetName().c_str()); // TODO(wwylele): Use correct error code return ResultCode(-1); } -ResultCode IVFCArchive::DeleteDirectoryRecursively(const Path& path) const { - LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an IVFC archive (%s).", +ResultCode RomFS_FileSystem::DeleteDirectoryRecursively(const Path& path) const { + LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an ROMFS archive (%s).", GetName().c_str()); // TODO(wwylele): Use correct error code return ResultCode(-1); } -ResultCode IVFCArchive::CreateFile(const Path& path, u64 size) const { - LOG_CRITICAL(Service_FS, "Attempted to create a file in an IVFC archive (%s).", +ResultCode RomFS_FileSystem::CreateFile(const Path& path, u64 size) const { + LOG_CRITICAL(Service_FS, "Attempted to create a file in an ROMFS archive (%s).", GetName().c_str()); // TODO(bunnei): Use correct error code return ResultCode(-1); } -ResultCode IVFCArchive::CreateDirectory(const Path& path) const { - LOG_CRITICAL(Service_FS, "Attempted to create a directory in an IVFC archive (%s).", +ResultCode RomFS_FileSystem::CreateDirectory(const Path& path) const { + LOG_CRITICAL(Service_FS, "Attempted to create a directory in an ROMFS archive (%s).", GetName().c_str()); // TODO(wwylele): Use correct error code return ResultCode(-1); } -ResultCode IVFCArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const { - LOG_CRITICAL(Service_FS, "Attempted to rename a file within an IVFC archive (%s).", +ResultCode RomFS_FileSystem::RenameDirectory(const Path& src_path, const Path& dest_path) const { + LOG_CRITICAL(Service_FS, "Attempted to rename a file within an ROMFS archive (%s).", GetName().c_str()); // TODO(wwylele): Use correct error code return ResultCode(-1); } -ResultVal<std::unique_ptr<DirectoryBackend>> IVFCArchive::OpenDirectory(const Path& path) const { - return MakeResult<std::unique_ptr<DirectoryBackend>>(std::make_unique<IVFCDirectory>()); +ResultVal<std::unique_ptr<DirectoryBackend>> RomFS_FileSystem::OpenDirectory( + const Path& path) const { + return MakeResult<std::unique_ptr<DirectoryBackend>>(std::make_unique<ROMFSDirectory>()); } -u64 IVFCArchive::GetFreeBytes() const { - LOG_WARNING(Service_FS, "Attempted to get the free space in an IVFC archive"); +u64 RomFS_FileSystem::GetFreeSpaceSize() const { + LOG_WARNING(Service_FS, "Attempted to get the free space in an ROMFS archive"); return 0; } -//////////////////////////////////////////////////////////////////////////////////////////////////// - -ResultVal<size_t> IVFCFile::Read(const u64 offset, const size_t length, u8* buffer) const { +ResultVal<size_t> RomFS_Storage::Read(const u64 offset, const size_t length, u8* buffer) const { LOG_TRACE(Service_FS, "called offset=%llu, length=%zu", offset, length); romfs_file->Seek(data_offset + offset, SEEK_SET); size_t read_length = (size_t)std::min((u64)length, data_size - offset); @@ -91,19 +87,19 @@ ResultVal<size_t> IVFCFile::Read(const u64 offset, const size_t length, u8* buff return MakeResult<size_t>(romfs_file->ReadBytes(buffer, read_length)); } -ResultVal<size_t> IVFCFile::Write(const u64 offset, const size_t length, const bool flush, - const u8* buffer) const { - LOG_ERROR(Service_FS, "Attempted to write to IVFC file"); +ResultVal<size_t> RomFS_Storage::Write(const u64 offset, const size_t length, const bool flush, + const u8* buffer) const { + LOG_ERROR(Service_FS, "Attempted to write to ROMFS file"); // TODO(Subv): Find error code return MakeResult<size_t>(0); } -u64 IVFCFile::GetSize() const { +u64 RomFS_Storage::GetSize() const { return data_size; } -bool IVFCFile::SetSize(const u64 size) const { - LOG_ERROR(Service_FS, "Attempted to set the size of an IVFC file"); +bool RomFS_Storage::SetSize(const u64 size) const { + LOG_ERROR(Service_FS, "Attempted to set the size of an ROMFS file"); return false; } diff --git a/src/core/file_sys/ivfc_archive.h b/src/core/file_sys/romfs_filesystem.h index e6fbdfb1f..900ea567a 100644 --- a/src/core/file_sys/ivfc_archive.h +++ b/src/core/file_sys/romfs_filesystem.h @@ -1,4 +1,4 @@ -// Copyright 2014 Citra Emulator Project +// Copyright 2018 yuzu emulator team // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -10,30 +10,27 @@ #include <vector> #include "common/common_types.h" #include "common/file_util.h" -#include "core/file_sys/archive_backend.h" -#include "core/file_sys/directory_backend.h" -#include "core/file_sys/file_backend.h" +#include "core/file_sys/directory.h" +#include "core/file_sys/filesystem.h" +#include "core/file_sys/storage.h" #include "core/hle/result.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// FileSys namespace - namespace FileSys { /** - * Helper which implements an interface to deal with IVFC images used in some archives - * This should be subclassed by concrete archive types, which will provide the - * input data (load the raw IVFC archive) and override any required methods + * Helper which implements an interface to deal with Switch .istorage ROMFS images used in some + * archives This should be subclassed by concrete archive types, which will provide the input data + * (load the raw ROMFS archive) and override any required methods */ -class IVFCArchive : public ArchiveBackend { +class RomFS_FileSystem : public FileSystemBackend { public: - IVFCArchive(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size) + RomFS_FileSystem(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size) : romfs_file(file), data_offset(offset), data_size(size) {} std::string GetName() const override; - ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, - const Mode& mode) const override; + ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const Path& path, + const Mode& mode) const override; ResultCode DeleteFile(const Path& path) const override; ResultCode RenameFile(const Path& src_path, const Path& dest_path) const override; ResultCode DeleteDirectory(const Path& path) const override; @@ -42,7 +39,7 @@ public: ResultCode CreateDirectory(const Path& path) const override; ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override; ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override; - u64 GetFreeBytes() const override; + u64 GetFreeSpaceSize() const override; protected: std::shared_ptr<FileUtil::IOFile> romfs_file; @@ -50,9 +47,9 @@ protected: u64 data_size; }; -class IVFCFile : public FileBackend { +class RomFS_Storage : public StorageBackend { public: - IVFCFile(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size) + RomFS_Storage(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size) : romfs_file(file), data_offset(offset), data_size(size) {} ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override; @@ -70,7 +67,7 @@ private: u64 data_size; }; -class IVFCDirectory : public DirectoryBackend { +class ROMFSDirectory : public DirectoryBackend { public: u32 Read(const u32 count, Entry* entries) override { return 0; diff --git a/src/core/file_sys/savedata_archive.cpp b/src/core/file_sys/savedata_archive.cpp deleted file mode 100644 index d7b012f6e..000000000 --- a/src/core/file_sys/savedata_archive.cpp +++ /dev/null @@ -1,330 +0,0 @@ -// Copyright 2016 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "common/file_util.h" -#include "core/file_sys/disk_archive.h" -#include "core/file_sys/errors.h" -#include "core/file_sys/path_parser.h" -#include "core/file_sys/savedata_archive.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// FileSys namespace - -namespace FileSys { - -ResultVal<std::unique_ptr<FileBackend>> SaveDataArchive::OpenFile(const Path& path, - const Mode& mode) const { - LOG_DEBUG(Service_FS, "called path=%s mode=%01X", path.DebugStr().c_str(), mode.hex); - - const PathParser path_parser(path); - - if (!path_parser.IsValid()) { - LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str()); - return ERROR_INVALID_PATH; - } - - if (mode.hex == 0) { - LOG_ERROR(Service_FS, "Empty open mode"); - return ERROR_UNSUPPORTED_OPEN_FLAGS; - } - - if (mode.create_flag && !mode.write_flag) { - LOG_ERROR(Service_FS, "Create flag set but write flag not set"); - return ERROR_UNSUPPORTED_OPEN_FLAGS; - } - - const auto full_path = path_parser.BuildHostPath(mount_point); - - switch (path_parser.GetHostStatus(mount_point)) { - case PathParser::InvalidMountPoint: - LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str()); - return ERROR_FILE_NOT_FOUND; - case PathParser::PathNotFound: - LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str()); - return ERROR_PATH_NOT_FOUND; - case PathParser::FileInPath: - case PathParser::DirectoryFound: - LOG_ERROR(Service_FS, "Unexpected file or directory in %s", full_path.c_str()); - return ERROR_UNEXPECTED_FILE_OR_DIRECTORY; - case PathParser::NotFound: - if (!mode.create_flag) { - LOG_ERROR(Service_FS, "Non-existing file %s can't be open without mode create.", - full_path.c_str()); - return ERROR_FILE_NOT_FOUND; - } else { - // Create the file - FileUtil::CreateEmptyFile(full_path); - } - break; - case PathParser::FileFound: - break; // Expected 'success' case - } - - FileUtil::IOFile file(full_path, mode.write_flag ? "r+b" : "rb"); - if (!file.IsOpen()) { - LOG_CRITICAL(Service_FS, "(unreachable) Unknown error opening %s", full_path.c_str()); - return ERROR_FILE_NOT_FOUND; - } - - auto disk_file = std::make_unique<DiskFile>(std::move(file), mode); - return MakeResult<std::unique_ptr<FileBackend>>(std::move(disk_file)); -} - -ResultCode SaveDataArchive::DeleteFile(const Path& path) const { - const PathParser path_parser(path); - - if (!path_parser.IsValid()) { - LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str()); - return ERROR_INVALID_PATH; - } - - const auto full_path = path_parser.BuildHostPath(mount_point); - - switch (path_parser.GetHostStatus(mount_point)) { - case PathParser::InvalidMountPoint: - LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str()); - return ERROR_FILE_NOT_FOUND; - case PathParser::PathNotFound: - LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str()); - return ERROR_PATH_NOT_FOUND; - case PathParser::FileInPath: - case PathParser::DirectoryFound: - case PathParser::NotFound: - LOG_ERROR(Service_FS, "File not found %s", full_path.c_str()); - return ERROR_FILE_NOT_FOUND; - case PathParser::FileFound: - break; // Expected 'success' case - } - - if (FileUtil::Delete(full_path)) { - return RESULT_SUCCESS; - } - - LOG_CRITICAL(Service_FS, "(unreachable) Unknown error deleting %s", full_path.c_str()); - return ERROR_FILE_NOT_FOUND; -} - -ResultCode SaveDataArchive::RenameFile(const Path& src_path, const Path& dest_path) const { - const PathParser path_parser_src(src_path); - - // TODO: Verify these return codes with HW - if (!path_parser_src.IsValid()) { - LOG_ERROR(Service_FS, "Invalid src path %s", src_path.DebugStr().c_str()); - return ERROR_INVALID_PATH; - } - - const PathParser path_parser_dest(dest_path); - - if (!path_parser_dest.IsValid()) { - LOG_ERROR(Service_FS, "Invalid dest path %s", dest_path.DebugStr().c_str()); - return ERROR_INVALID_PATH; - } - - const auto src_path_full = path_parser_src.BuildHostPath(mount_point); - const auto dest_path_full = path_parser_dest.BuildHostPath(mount_point); - - if (FileUtil::Rename(src_path_full, dest_path_full)) { - return RESULT_SUCCESS; - } - - // TODO(bunnei): Use correct error code - return ResultCode(-1); -} - -template <typename T> -static ResultCode DeleteDirectoryHelper(const Path& path, const std::string& mount_point, - T deleter) { - const PathParser path_parser(path); - - if (!path_parser.IsValid()) { - LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str()); - return ERROR_INVALID_PATH; - } - - if (path_parser.IsRootDirectory()) - return ERROR_DIRECTORY_NOT_EMPTY; - - const auto full_path = path_parser.BuildHostPath(mount_point); - - switch (path_parser.GetHostStatus(mount_point)) { - case PathParser::InvalidMountPoint: - LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str()); - return ERROR_PATH_NOT_FOUND; - case PathParser::PathNotFound: - case PathParser::NotFound: - LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str()); - return ERROR_PATH_NOT_FOUND; - case PathParser::FileInPath: - case PathParser::FileFound: - LOG_ERROR(Service_FS, "Unexpected file or directory %s", full_path.c_str()); - return ERROR_UNEXPECTED_FILE_OR_DIRECTORY; - case PathParser::DirectoryFound: - break; // Expected 'success' case - } - - if (deleter(full_path)) { - return RESULT_SUCCESS; - } - - LOG_ERROR(Service_FS, "Directory not empty %s", full_path.c_str()); - return ERROR_DIRECTORY_NOT_EMPTY; -} - -ResultCode SaveDataArchive::DeleteDirectory(const Path& path) const { - return DeleteDirectoryHelper(path, mount_point, FileUtil::DeleteDir); -} - -ResultCode SaveDataArchive::DeleteDirectoryRecursively(const Path& path) const { - return DeleteDirectoryHelper( - path, mount_point, [](const std::string& p) { return FileUtil::DeleteDirRecursively(p); }); -} - -ResultCode SaveDataArchive::CreateFile(const FileSys::Path& path, u64 size) const { - const PathParser path_parser(path); - - if (!path_parser.IsValid()) { - LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str()); - return ERROR_INVALID_PATH; - } - - const auto full_path = path_parser.BuildHostPath(mount_point); - - switch (path_parser.GetHostStatus(mount_point)) { - case PathParser::InvalidMountPoint: - LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str()); - return ERROR_FILE_NOT_FOUND; - case PathParser::PathNotFound: - LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str()); - return ERROR_PATH_NOT_FOUND; - case PathParser::FileInPath: - LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str()); - return ERROR_UNEXPECTED_FILE_OR_DIRECTORY; - case PathParser::DirectoryFound: - case PathParser::FileFound: - LOG_ERROR(Service_FS, "%s already exists", full_path.c_str()); - return ERROR_FILE_ALREADY_EXISTS; - case PathParser::NotFound: - break; // Expected 'success' case - } - - if (size == 0) { - FileUtil::CreateEmptyFile(full_path); - return RESULT_SUCCESS; - } - - FileUtil::IOFile file(full_path, "wb"); - // Creates a sparse file (or a normal file on filesystems without the concept of sparse files) - // We do this by seeking to the right size, then writing a single null byte. - if (file.Seek(size - 1, SEEK_SET) && file.WriteBytes("", 1) == 1) { - return RESULT_SUCCESS; - } - - LOG_ERROR(Service_FS, "Too large file"); - - // TODO(bunnei): Use correct error code - return ResultCode(-1); -} - -ResultCode SaveDataArchive::CreateDirectory(const Path& path) const { - const PathParser path_parser(path); - - if (!path_parser.IsValid()) { - LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str()); - return ERROR_INVALID_PATH; - } - - const auto full_path = path_parser.BuildHostPath(mount_point); - - switch (path_parser.GetHostStatus(mount_point)) { - case PathParser::InvalidMountPoint: - LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str()); - return ERROR_FILE_NOT_FOUND; - case PathParser::PathNotFound: - LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str()); - return ERROR_PATH_NOT_FOUND; - case PathParser::FileInPath: - LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str()); - return ERROR_UNEXPECTED_FILE_OR_DIRECTORY; - case PathParser::DirectoryFound: - case PathParser::FileFound: - LOG_ERROR(Service_FS, "%s already exists", full_path.c_str()); - return ERROR_DIRECTORY_ALREADY_EXISTS; - case PathParser::NotFound: - break; // Expected 'success' case - } - - if (FileUtil::CreateDir(mount_point + path.AsString())) { - return RESULT_SUCCESS; - } - - LOG_CRITICAL(Service_FS, "(unreachable) Unknown error creating %s", mount_point.c_str()); - - // TODO(bunnei): Use correct error code - return ResultCode(-1); -} - -ResultCode SaveDataArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const { - const PathParser path_parser_src(src_path); - - // TODO: Verify these return codes with HW - if (!path_parser_src.IsValid()) { - LOG_ERROR(Service_FS, "Invalid src path %s", src_path.DebugStr().c_str()); - return ERROR_INVALID_PATH; - } - - const PathParser path_parser_dest(dest_path); - - if (!path_parser_dest.IsValid()) { - LOG_ERROR(Service_FS, "Invalid dest path %s", dest_path.DebugStr().c_str()); - return ERROR_INVALID_PATH; - } - - const auto src_path_full = path_parser_src.BuildHostPath(mount_point); - const auto dest_path_full = path_parser_dest.BuildHostPath(mount_point); - - if (FileUtil::Rename(src_path_full, dest_path_full)) { - return RESULT_SUCCESS; - } - - // TODO(bunnei): Use correct error code - return ResultCode(-1); -} - -ResultVal<std::unique_ptr<DirectoryBackend>> SaveDataArchive::OpenDirectory( - const Path& path) const { - const PathParser path_parser(path); - - if (!path_parser.IsValid()) { - LOG_ERROR(Service_FS, "Invalid path %s", path.DebugStr().c_str()); - return ERROR_INVALID_PATH; - } - - const auto full_path = path_parser.BuildHostPath(mount_point); - - switch (path_parser.GetHostStatus(mount_point)) { - case PathParser::InvalidMountPoint: - LOG_CRITICAL(Service_FS, "(unreachable) Invalid mount point %s", mount_point.c_str()); - return ERROR_FILE_NOT_FOUND; - case PathParser::PathNotFound: - case PathParser::NotFound: - LOG_ERROR(Service_FS, "Path not found %s", full_path.c_str()); - return ERROR_PATH_NOT_FOUND; - case PathParser::FileInPath: - case PathParser::FileFound: - LOG_ERROR(Service_FS, "Unexpected file in path %s", full_path.c_str()); - return ERROR_UNEXPECTED_FILE_OR_DIRECTORY; - case PathParser::DirectoryFound: - break; // Expected 'success' case - } - - auto directory = std::make_unique<DiskDirectory>(full_path); - return MakeResult<std::unique_ptr<DirectoryBackend>>(std::move(directory)); -} - -u64 SaveDataArchive::GetFreeBytes() const { - // TODO: Stubbed to return 1GiB - return 1024 * 1024 * 1024; -} - -} // namespace FileSys diff --git a/src/core/file_sys/savedata_archive.h b/src/core/file_sys/savedata_archive.h deleted file mode 100644 index 176d35710..000000000 --- a/src/core/file_sys/savedata_archive.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2016 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <string> -#include "core/file_sys/archive_backend.h" -#include "core/file_sys/directory_backend.h" -#include "core/file_sys/file_backend.h" -#include "core/hle/result.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// FileSys namespace - -namespace FileSys { - -/// Archive backend for general save data archive type (SaveData and SystemSaveData) -class SaveDataArchive : public ArchiveBackend { -public: - explicit SaveDataArchive(const std::string& mount_point_) : mount_point(mount_point_) {} - - std::string GetName() const override { - return "SaveDataArchive: " + mount_point; - } - - ResultVal<std::unique_ptr<FileBackend>> OpenFile(const Path& path, - const Mode& mode) const override; - ResultCode DeleteFile(const Path& path) const override; - ResultCode RenameFile(const Path& src_path, const Path& dest_path) const override; - ResultCode DeleteDirectory(const Path& path) const override; - ResultCode DeleteDirectoryRecursively(const Path& path) const override; - ResultCode CreateFile(const Path& path, u64 size) const override; - ResultCode CreateDirectory(const Path& path) const override; - ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override; - ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory(const Path& path) const override; - u64 GetFreeBytes() const override; - -protected: - std::string mount_point; -}; - -} // namespace FileSys diff --git a/src/core/file_sys/file_backend.h b/src/core/file_sys/storage.h index 5e7c2bab4..2a6811831 100644 --- a/src/core/file_sys/file_backend.h +++ b/src/core/file_sys/storage.h @@ -1,4 +1,4 @@ -// Copyright 2014 Citra Emulator Project +// Copyright 2018 yuzu emulator team // Licensed under GPLv2 or any later version // Refer to the license.txt file included. @@ -8,15 +8,12 @@ #include "common/common_types.h" #include "core/hle/result.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// FileSys namespace - namespace FileSys { -class FileBackend : NonCopyable { +class StorageBackend : NonCopyable { public: - FileBackend() {} - virtual ~FileBackend() {} + StorageBackend() {} + virtual ~StorageBackend() {} /** * Read data from the file @@ -39,10 +36,9 @@ public: const u8* buffer) const = 0; /** - * Get the size of the file in bytes - * @return Size of the file in bytes + * Flushes the file */ - virtual u64 GetSize() const = 0; + virtual void Flush() const = 0; /** * Set the size of the file in bytes @@ -52,15 +48,16 @@ public: virtual bool SetSize(u64 size) const = 0; /** - * Close the file - * @return true if the file closed correctly + * Get the size of the file in bytes + * @return Size of the file in bytes */ - virtual bool Close() const = 0; + virtual u64 GetSize() const = 0; /** - * Flushes the file + * Close the file + * @return true if the file closed correctly */ - virtual void Flush() const = 0; + virtual bool Close() const = 0; }; } // namespace FileSys diff --git a/src/core/file_sys/title_metadata.cpp b/src/core/file_sys/title_metadata.cpp deleted file mode 100644 index e29ba6064..000000000 --- a/src/core/file_sys/title_metadata.cpp +++ /dev/null @@ -1,163 +0,0 @@ -// Copyright 2017 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <cinttypes> -#include "common/alignment.h" -#include "common/file_util.h" -#include "common/logging/log.h" -#include "core/file_sys/title_metadata.h" -#include "core/loader/loader.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// FileSys namespace - -namespace FileSys { - -static u32 GetSignatureSize(u32 signature_type) { - switch (signature_type) { - case Rsa4096Sha1: - case Rsa4096Sha256: - return 0x200; - - case Rsa2048Sha1: - case Rsa2048Sha256: - return 0x100; - - case EllipticSha1: - case EcdsaSha256: - return 0x3C; - } -} - -Loader::ResultStatus TitleMetadata::Load() { - FileUtil::IOFile file(filepath, "rb"); - if (!file.IsOpen()) - return Loader::ResultStatus::Error; - - if (!file.ReadBytes(&signature_type, sizeof(u32_be))) - return Loader::ResultStatus::Error; - - // Signature lengths are variable, and the body follows the signature - u32 signature_size = GetSignatureSize(signature_type); - - tmd_signature.resize(signature_size); - if (!file.ReadBytes(&tmd_signature[0], signature_size)) - return Loader::ResultStatus::Error; - - // The TMD body start position is rounded to the nearest 0x40 after the signature - size_t body_start = Common::AlignUp(signature_size + sizeof(u32), 0x40); - file.Seek(body_start, SEEK_SET); - - // Read our TMD body, then load the amount of ContentChunks specified - if (file.ReadBytes(&tmd_body, sizeof(TitleMetadata::Body)) != sizeof(TitleMetadata::Body)) - return Loader::ResultStatus::Error; - - for (u16 i = 0; i < tmd_body.content_count; i++) { - ContentChunk chunk; - if (file.ReadBytes(&chunk, sizeof(ContentChunk)) == sizeof(ContentChunk)) { - tmd_chunks.push_back(chunk); - } else { - LOG_ERROR(Service_FS, "Malformed TMD %s, failed to load content chunk index %u!", - filepath.c_str(), i); - return Loader::ResultStatus::ErrorInvalidFormat; - } - } - - return Loader::ResultStatus::Success; -} - -Loader::ResultStatus TitleMetadata::Save() { - UNIMPLEMENTED(); - return Loader::ResultStatus::Success; -} - -u64 TitleMetadata::GetTitleID() const { - return tmd_body.title_id; -} - -u32 TitleMetadata::GetTitleType() const { - return tmd_body.title_type; -} - -u16 TitleMetadata::GetTitleVersion() const { - return tmd_body.title_version; -} - -u64 TitleMetadata::GetSystemVersion() const { - return tmd_body.system_version; -} - -size_t TitleMetadata::GetContentCount() const { - return tmd_chunks.size(); -} - -u32 TitleMetadata::GetBootContentID() const { - return tmd_chunks[TMDContentIndex::Main].id; -} - -u32 TitleMetadata::GetManualContentID() const { - return tmd_chunks[TMDContentIndex::Manual].id; -} - -u32 TitleMetadata::GetDLPContentID() const { - return tmd_chunks[TMDContentIndex::DLP].id; -} - -void TitleMetadata::SetTitleID(u64 title_id) { - tmd_body.title_id = title_id; -} - -void TitleMetadata::SetTitleType(u32 type) { - tmd_body.title_type = type; -} - -void TitleMetadata::SetTitleVersion(u16 version) { - tmd_body.title_version = version; -} - -void TitleMetadata::SetSystemVersion(u64 version) { - tmd_body.system_version = version; -} - -void TitleMetadata::AddContentChunk(const ContentChunk& chunk) { - tmd_chunks.push_back(chunk); -} - -void TitleMetadata::Print() const { - LOG_DEBUG(Service_FS, "%s - %u chunks", filepath.c_str(), - static_cast<u32>(tmd_body.content_count)); - - // Content info describes ranges of content chunks - LOG_DEBUG(Service_FS, "Content info:"); - for (size_t i = 0; i < tmd_body.contentinfo.size(); i++) { - if (tmd_body.contentinfo[i].command_count == 0) - break; - - LOG_DEBUG(Service_FS, " Index %04X, Command Count %04X", - static_cast<u32>(tmd_body.contentinfo[i].index), - static_cast<u32>(tmd_body.contentinfo[i].command_count)); - } - - // For each content info, print their content chunk range - for (size_t i = 0; i < tmd_body.contentinfo.size(); i++) { - u16 index = static_cast<u16>(tmd_body.contentinfo[i].index); - u16 count = static_cast<u16>(tmd_body.contentinfo[i].command_count); - - if (count == 0) - continue; - - LOG_DEBUG(Service_FS, "Content chunks for content info index %zu:", i); - for (u16 j = index; j < index + count; j++) { - // Don't attempt to print content we don't have - if (j > tmd_body.content_count) - break; - - const ContentChunk& chunk = tmd_chunks[j]; - LOG_DEBUG(Service_FS, " ID %08X, Index %04X, Type %04x, Size %016" PRIX64, - static_cast<u32>(chunk.id), static_cast<u32>(chunk.index), - static_cast<u32>(chunk.type), static_cast<u64>(chunk.size)); - } - } -} -} // namespace FileSys diff --git a/src/core/file_sys/title_metadata.h b/src/core/file_sys/title_metadata.h deleted file mode 100644 index a4c7d1089..000000000 --- a/src/core/file_sys/title_metadata.h +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright 2017 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <array> -#include <string> -#include <vector> -#include "common/common_types.h" -#include "common/swap.h" - -namespace Loader { -enum class ResultStatus; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// FileSys namespace - -namespace FileSys { - -enum TMDSignatureType : u32 { - Rsa4096Sha1 = 0x10000, - Rsa2048Sha1 = 0x10001, - EllipticSha1 = 0x10002, - Rsa4096Sha256 = 0x10003, - Rsa2048Sha256 = 0x10004, - EcdsaSha256 = 0x10005 -}; - -enum TMDContentTypeFlag : u16 { - Encrypted = 1 << 1, - Disc = 1 << 2, - CFM = 1 << 3, - Optional = 1 << 14, - Shared = 1 << 15 -}; - -/** - * Helper which implements an interface to read and write Title Metadata (TMD) files. - * If a file path is provided and the file exists, it can be parsed and used, otherwise - * it must be created. The TMD file can then be interpreted, modified and/or saved. - */ -class TitleMetadata { -public: - struct ContentChunk { - u32_be id; - u16_be index; - u16_be type; - u64_be size; - std::array<u8, 0x20> hash; - }; - - static_assert(sizeof(ContentChunk) == 0x30, "TMD ContentChunk structure size is wrong"); - - struct ContentInfo { - u16_be index; - u16_be command_count; - std::array<u8, 0x20> hash; - }; - - static_assert(sizeof(ContentInfo) == 0x24, "TMD ContentInfo structure size is wrong"); - -#pragma pack(push, 1) - - struct Body { - std::array<u8, 0x40> issuer; - u8 version; - u8 ca_crl_version; - u8 signer_crl_version; - u8 reserved; - u64_be system_version; - u64_be title_id; - u32_be title_type; - u16_be group_id; - u32_be savedata_size; - u32_be srl_private_savedata_size; - std::array<u8, 4> reserved_2; - u8 srl_flag; - std::array<u8, 0x31> reserved_3; - u32_be access_rights; - u16_be title_version; - u16_be content_count; - u16_be boot_content; - std::array<u8, 2> reserved_4; - std::array<u8, 0x20> contentinfo_hash; - std::array<ContentInfo, 64> contentinfo; - }; - - static_assert(sizeof(Body) == 0x9C4, "TMD body structure size is wrong"); - -#pragma pack(pop) - - explicit TitleMetadata(std::string& path) : filepath(std::move(path)) {} - Loader::ResultStatus Load(); - Loader::ResultStatus Save(); - - u64 GetTitleID() const; - u32 GetTitleType() const; - u16 GetTitleVersion() const; - u64 GetSystemVersion() const; - size_t GetContentCount() const; - u32 GetBootContentID() const; - u32 GetManualContentID() const; - u32 GetDLPContentID() const; - - void SetTitleID(u64 title_id); - void SetTitleType(u32 type); - void SetTitleVersion(u16 version); - void SetSystemVersion(u64 version); - void AddContentChunk(const ContentChunk& chunk); - - void Print() const; - -private: - enum TMDContentIndex { Main = 0, Manual = 1, DLP = 2 }; - - Body tmd_body; - u32_be signature_type; - std::vector<u8> tmd_signature; - std::vector<ContentChunk> tmd_chunks; - - std::string filepath; -}; - -} // namespace FileSys diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index 2f3ccb689..2405da0c6 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp @@ -57,9 +57,10 @@ const u32 SIGTERM = 15; const u32 MSG_WAITALL = 8; #endif -const u32 R15_REGISTER = 15; -const u32 CPSR_REGISTER = 25; -const u32 FPSCR_REGISTER = 58; +const u32 X30_REGISTER = 30; +const u32 SP_REGISTER = 31; +const u32 PC_REGISTER = 32; +const u32 CPSR_REGISTER = 33; // For sample XML files see the GDB source /gdb/features // GDB also wants the l character at the start @@ -68,48 +69,62 @@ static const char* target_xml = R"(l<?xml version="1.0"?> <!DOCTYPE target SYSTEM "gdb-target.dtd"> <target version="1.0"> - <feature name="org.gnu.gdb.arm.core"> - <reg name="r0" bitsize="32"/> - <reg name="r1" bitsize="32"/> - <reg name="r2" bitsize="32"/> - <reg name="r3" bitsize="32"/> - <reg name="r4" bitsize="32"/> - <reg name="r5" bitsize="32"/> - <reg name="r6" bitsize="32"/> - <reg name="r7" bitsize="32"/> - <reg name="r8" bitsize="32"/> - <reg name="r9" bitsize="32"/> - <reg name="r10" bitsize="32"/> - <reg name="r11" bitsize="32"/> - <reg name="r12" bitsize="32"/> - <reg name="sp" bitsize="32" type="data_ptr"/> - <reg name="lr" bitsize="32"/> - <reg name="pc" bitsize="32" type="code_ptr"/> - - <!-- The CPSR is register 25, rather than register 16, because - the FPA registers historically were placed between the PC - and the CPSR in the "g" packet. --> - - <reg name="cpsr" bitsize="32" regnum="25"/> - </feature> - <feature name="org.gnu.gdb.arm.vfp"> - <reg name="d0" bitsize="64" type="float"/> - <reg name="d1" bitsize="64" type="float"/> - <reg name="d2" bitsize="64" type="float"/> - <reg name="d3" bitsize="64" type="float"/> - <reg name="d4" bitsize="64" type="float"/> - <reg name="d5" bitsize="64" type="float"/> - <reg name="d6" bitsize="64" type="float"/> - <reg name="d7" bitsize="64" type="float"/> - <reg name="d8" bitsize="64" type="float"/> - <reg name="d9" bitsize="64" type="float"/> - <reg name="d10" bitsize="64" type="float"/> - <reg name="d11" bitsize="64" type="float"/> - <reg name="d12" bitsize="64" type="float"/> - <reg name="d13" bitsize="64" type="float"/> - <reg name="d14" bitsize="64" type="float"/> - <reg name="d15" bitsize="64" type="float"/> - <reg name="fpscr" bitsize="32" type="int" group="float"/> + <feature name="org.gnu.gdb.aarch64.core"> + <reg name="x0" bitsize="64"/> + <reg name="x1" bitsize="64"/> + <reg name="x2" bitsize="64"/> + <reg name="x3" bitsize="64"/> + <reg name="x4" bitsize="64"/> + <reg name="x5" bitsize="64"/> + <reg name="x6" bitsize="64"/> + <reg name="x7" bitsize="64"/> + <reg name="x8" bitsize="64"/> + <reg name="x9" bitsize="64"/> + <reg name="x10" bitsize="64"/> + <reg name="x11" bitsize="64"/> + <reg name="x12" bitsize="64"/> + <reg name="x13" bitsize="64"/> + <reg name="x14" bitsize="64"/> + <reg name="x15" bitsize="64"/> + <reg name="x16" bitsize="64"/> + <reg name="x17" bitsize="64"/> + <reg name="x18" bitsize="64"/> + <reg name="x19" bitsize="64"/> + <reg name="x20" bitsize="64"/> + <reg name="x21" bitsize="64"/> + <reg name="x22" bitsize="64"/> + <reg name="x23" bitsize="64"/> + <reg name="x24" bitsize="64"/> + <reg name="x25" bitsize="64"/> + <reg name="x26" bitsize="64"/> + <reg name="x27" bitsize="64"/> + <reg name="x28" bitsize="64"/> + <reg name="x29" bitsize="64"/> + <reg name="x30" bitsize="64"/> + <reg name="sp" bitsize="64" type="data_ptr"/> + + <reg name="pc" bitsize="64" type="code_ptr"/> + + <flags id="cpsr_flags" size="4"> + <field name="SP" start="0" end="0"/> + <field name="" start="1" end="1"/> + <field name="EL" start="2" end="3"/> + <field name="nRW" start="4" end="4"/> + <field name="" start="5" end="5"/> + <field name="F" start="6" end="6"/> + <field name="I" start="7" end="7"/> + <field name="A" start="8" end="8"/> + <field name="D" start="9" end="9"/> + + <field name="IL" start="20" end="20"/> + <field name="SS" start="21" end="21"/> + + <field name="V" start="28" end="28"/> + <field name="C" start="29" end="29"/> + <field name="Z" start="30" end="30"/> + <field name="N" start="31" end="31"/> + </flags> + <reg name="cpsr" bitsize="32" type="cpsr_flags"/> </feature> </target> )"; @@ -143,12 +158,12 @@ WSADATA InitData; struct Breakpoint { bool active; PAddr addr; - u32 len; + u64 len; }; -static std::map<u32, Breakpoint> breakpoints_execute; -static std::map<u32, Breakpoint> breakpoints_read; -static std::map<u32, Breakpoint> breakpoints_write; +static std::map<u64, Breakpoint> breakpoints_execute; +static std::map<u64, Breakpoint> breakpoints_read; +static std::map<u64, Breakpoint> breakpoints_write; /** * Turns hex string character into the equivalent byte. @@ -198,6 +213,21 @@ static u32 HexToInt(const u8* src, size_t len) { } /** + * Converts input hex string characters into an array of equivalent of u8 bytes. + * + * @param src Pointer to array of output hex string characters. + * @param len Length of src array. + */ +static u64 HexToLong(const u8* src, size_t len) { + u64 output = 0; + while (len-- > 0) { + output = (output << 4) | HexCharToValue(src[0]); + src++; + } + return output; +} + +/** * Converts input array of u8 bytes into their equivalent hex string characters. * * @param dest Pointer to buffer to store output hex string characters. @@ -234,8 +264,21 @@ static void GdbHexToMem(u8* dest, const u8* src, size_t len) { */ static void IntToGdbHex(u8* dest, u32 v) { for (int i = 0; i < 8; i += 2) { - dest[i + 1] = NibbleToHex(v >> (4 * i)); - dest[i] = NibbleToHex(v >> (4 * (i + 1))); + dest[i + 1] = NibbleToHex(static_cast<u8>(v >> (4 * i))); + dest[i] = NibbleToHex(static_cast<u8>(v >> (4 * (i + 1)))); + } +} + +/** + * Convert a u64 into a gdb-formatted hex string. + * + * @param dest Pointer to buffer to store output hex string characters. + * @param v Value to convert. + */ +static void LongToGdbHex(u8* dest, u64 v) { + for (int i = 0; i < 16; i += 2) { + dest[i + 1] = NibbleToHex(static_cast<u8>(v >> (4 * i))); + dest[i] = NibbleToHex(static_cast<u8>(v >> (4 * (i + 1)))); } } @@ -255,6 +298,22 @@ static u32 GdbHexToInt(const u8* src) { return output; } +/** + * Convert a gdb-formatted hex string into a u64. + * + * @param src Pointer to hex string. + */ +static u64 GdbHexToLong(const u8* src) { + u64 output = 0; + + for (int i = 0; i < 16; i += 2) { + output = (output << 4) | HexCharToValue(src[15 - i - 1]); + output = (output << 4) | HexCharToValue(src[15 - i]); + } + + return output; +} + /// Read a byte from the gdb client. static u8 ReadByte() { u8 c; @@ -277,7 +336,7 @@ static u8 CalculateChecksum(const u8* buffer, size_t length) { * * @param type Type of breakpoint list. */ -static std::map<u32, Breakpoint>& GetBreakpointList(BreakpointType type) { +static std::map<u64, Breakpoint>& GetBreakpointList(BreakpointType type) { switch (type) { case BreakpointType::Execute: return breakpoints_execute; @@ -297,19 +356,19 @@ static std::map<u32, Breakpoint>& GetBreakpointList(BreakpointType type) { * @param addr Address of breakpoint. */ static void RemoveBreakpoint(BreakpointType type, PAddr addr) { - std::map<u32, Breakpoint>& p = GetBreakpointList(type); + std::map<u64, Breakpoint>& p = GetBreakpointList(type); - auto bp = p.find(static_cast<u32>(addr)); + auto bp = p.find(static_cast<u64>(addr)); if (bp != p.end()) { LOG_DEBUG(Debug_GDBStub, "gdb: removed a breakpoint: %08x bytes at %08x of type %d\n", bp->second.len, bp->second.addr, type); - p.erase(static_cast<u32>(addr)); + p.erase(static_cast<u64>(addr)); } } BreakpointAddress GetNextBreakpointFromAddress(PAddr addr, BreakpointType type) { - std::map<u32, Breakpoint>& p = GetBreakpointList(type); - auto next_breakpoint = p.lower_bound(static_cast<u32>(addr)); + std::map<u64, Breakpoint>& p = GetBreakpointList(type); + auto next_breakpoint = p.lower_bound(static_cast<u64>(addr)); BreakpointAddress breakpoint; if (next_breakpoint != p.end()) { @@ -328,11 +387,11 @@ bool CheckBreakpoint(PAddr addr, BreakpointType type) { return false; } - std::map<u32, Breakpoint>& p = GetBreakpointList(type); + std::map<u64, Breakpoint>& p = GetBreakpointList(type); - auto bp = p.find(static_cast<u32>(addr)); + auto bp = p.find(static_cast<u64>(addr)); if (bp != p.end()) { - u32 len = bp->second.len; + u64 len = bp->second.len; // IDA Pro defaults to 4-byte breakpoints for all non-hardware breakpoints // no matter if it's a 4-byte or 2-byte instruction. When you execute a @@ -419,7 +478,7 @@ static void HandleQuery() { SendReply("T0"); } else if (strncmp(query, "Supported", strlen("Supported")) == 0) { // PacketSize needs to be large enough for target xml - SendReply("PacketSize=800;qXfer:features:read+"); + SendReply("PacketSize=2000;qXfer:features:read+"); } else if (strncmp(query, "Xfer:features:read:target.xml:", strlen("Xfer:features:read:target.xml:")) == 0) { SendReply(target_xml); @@ -450,10 +509,7 @@ static void SendSignal(u32 signal) { latest_signal = signal; - std::string buffer = - Common::StringFromFormat("T%02x%02x:%08x;%02x:%08x;", latest_signal, 15, - htonl(static_cast<u_long>(Core::CPU().GetPC())), 13, - htonl(static_cast<u_long>(Core::CPU().GetReg(13)))); + std::string buffer = Common::StringFromFormat("T%02x", latest_signal); LOG_DEBUG(Debug_GDBStub, "Response: %s", buffer.c_str()); SendReply(buffer.c_str()); } @@ -539,16 +595,12 @@ static void ReadRegister() { id |= HexCharToValue(command_buffer[2]); } - if (id <= R15_REGISTER) { - IntToGdbHex(reply, static_cast<u32>(Core::CPU().GetReg(static_cast<u64>(id)))); + if (id <= SP_REGISTER) { + LongToGdbHex(reply, Core::CPU().GetReg(static_cast<int>(id))); + } else if (id == PC_REGISTER) { + LongToGdbHex(reply, Core::CPU().GetPC()); } else if (id == CPSR_REGISTER) { IntToGdbHex(reply, Core::CPU().GetCPSR()); - } else if (id > CPSR_REGISTER && id < FPSCR_REGISTER) { - IntToGdbHex(reply, Core::CPU().GetVFPReg( - id - CPSR_REGISTER - - 1)); // VFP registers should start at 26, so one after CSPR_REGISTER - } else if (id == FPSCR_REGISTER) { - UNIMPLEMENTED(); } else { return SendReply("E01"); } @@ -563,21 +615,19 @@ static void ReadRegisters() { u8* bufptr = buffer; - for (int reg = 0; reg <= R15_REGISTER; reg++) { - IntToGdbHex(bufptr + reg * CHAR_BIT, static_cast<u32>(Core::CPU().GetReg(reg))); + for (int reg = 0; reg <= SP_REGISTER; reg++) { + LongToGdbHex(bufptr + reg * 16, Core::CPU().GetReg(reg)); } - bufptr += (16 * CHAR_BIT); + bufptr += (32 * 16); - IntToGdbHex(bufptr, Core::CPU().GetCPSR()); + LongToGdbHex(bufptr, Core::CPU().GetPC()); - bufptr += CHAR_BIT; + bufptr += 16; - for (int reg = 0; reg <= 31; reg++) { - IntToGdbHex(bufptr + reg * CHAR_BIT, Core::CPU().GetVFPReg(reg)); - } + IntToGdbHex(bufptr, Core::CPU().GetCPSR()); - bufptr += (32 * CHAR_BIT); + bufptr += 8; SendReply(reinterpret_cast<char*>(buffer)); } @@ -593,14 +643,12 @@ static void WriteRegister() { id |= HexCharToValue(command_buffer[2]); } - if (id <= R15_REGISTER) { - Core::CPU().SetReg(id, GdbHexToInt(buffer_ptr)); + if (id <= SP_REGISTER) { + Core::CPU().SetReg(id, GdbHexToLong(buffer_ptr)); + } else if (id == PC_REGISTER) { + Core::CPU().SetPC(GdbHexToLong(buffer_ptr)); } else if (id == CPSR_REGISTER) { Core::CPU().SetCPSR(GdbHexToInt(buffer_ptr)); - } else if (id > CPSR_REGISTER && id < FPSCR_REGISTER) { - Core::CPU().SetVFPReg(id - CPSR_REGISTER - 1, GdbHexToInt(buffer_ptr)); - } else if (id == FPSCR_REGISTER) { - UNIMPLEMENTED(); } else { return SendReply("E01"); } @@ -615,20 +663,14 @@ static void WriteRegisters() { if (command_buffer[0] != 'G') return SendReply("E01"); - for (int i = 0, reg = 0; reg <= FPSCR_REGISTER; i++, reg++) { - if (reg <= R15_REGISTER) { - Core::CPU().SetReg(reg, GdbHexToInt(buffer_ptr + i * CHAR_BIT)); + for (int i = 0, reg = 0; reg <= CPSR_REGISTER; i++, reg++) { + if (reg <= SP_REGISTER) { + Core::CPU().SetReg(reg, GdbHexToLong(buffer_ptr + i * 16)); + } else if (reg == PC_REGISTER) { + Core::CPU().SetPC(GdbHexToLong(buffer_ptr + i * 16)); } else if (reg == CPSR_REGISTER) { - Core::CPU().SetCPSR(GdbHexToInt(buffer_ptr + i * CHAR_BIT)); - } else if (reg == CPSR_REGISTER - 1) { - // Dummy FPA register, ignore - } else if (reg < CPSR_REGISTER) { - // Dummy FPA registers, ignore - i += 2; - } else if (reg > CPSR_REGISTER && reg < FPSCR_REGISTER) { - Core::CPU().SetVFPReg(reg - CPSR_REGISTER - 1, GdbHexToInt(buffer_ptr + i * CHAR_BIT)); - i++; // Skip padding - } else if (reg == FPSCR_REGISTER) { + Core::CPU().SetCPSR(GdbHexToInt(buffer_ptr + i * 16)); + } else { UNIMPLEMENTED(); } } @@ -642,13 +684,13 @@ static void ReadMemory() { auto start_offset = command_buffer + 1; auto addr_pos = std::find(start_offset, command_buffer + command_length, ','); - VAddr addr = HexToInt(start_offset, static_cast<u32>(addr_pos - start_offset)); + VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset)); start_offset = addr_pos + 1; - u32 len = - HexToInt(start_offset, static_cast<u32>((command_buffer + command_length) - start_offset)); + u64 len = + HexToLong(start_offset, static_cast<u64>((command_buffer + command_length) - start_offset)); - LOG_DEBUG(Debug_GDBStub, "gdb: addr: %08x len: %08x\n", addr, len); + LOG_DEBUG(Debug_GDBStub, "gdb: addr: %016llx len: %016llx\n", addr, len); if (len * 2 > sizeof(reply)) { SendReply("E01"); @@ -670,11 +712,11 @@ static void ReadMemory() { static void WriteMemory() { auto start_offset = command_buffer + 1; auto addr_pos = std::find(start_offset, command_buffer + command_length, ','); - VAddr addr = HexToInt(start_offset, static_cast<u32>(addr_pos - start_offset)); + VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset)); start_offset = addr_pos + 1; auto len_pos = std::find(start_offset, command_buffer + command_length, ':'); - u32 len = HexToInt(start_offset, static_cast<u32>(len_pos - start_offset)); + u64 len = HexToLong(start_offset, static_cast<u64>(len_pos - start_offset)); if (!Memory::IsValidVirtualAddress(addr)) { return SendReply("E00"); @@ -727,8 +769,8 @@ static void Continue() { * @param addr Address of breakpoint. * @param len Length of breakpoint. */ -static bool CommitBreakpoint(BreakpointType type, PAddr addr, u32 len) { - std::map<u32, Breakpoint>& p = GetBreakpointList(type); +static bool CommitBreakpoint(BreakpointType type, PAddr addr, u64 len) { + std::map<u64, Breakpoint>& p = GetBreakpointList(type); Breakpoint breakpoint; breakpoint.active = true; @@ -767,11 +809,11 @@ static void AddBreakpoint() { auto start_offset = command_buffer + 3; auto addr_pos = std::find(start_offset, command_buffer + command_length, ','); - PAddr addr = HexToInt(start_offset, static_cast<u32>(addr_pos - start_offset)); + PAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset)); start_offset = addr_pos + 1; - u32 len = - HexToInt(start_offset, static_cast<u32>((command_buffer + command_length) - start_offset)); + u64 len = + HexToLong(start_offset, static_cast<u64>((command_buffer + command_length) - start_offset)); if (type == BreakpointType::Access) { // Access is made up of Read and Write types, so add both breakpoints @@ -816,7 +858,7 @@ static void RemoveBreakpoint() { auto start_offset = command_buffer + 3; auto addr_pos = std::find(start_offset, command_buffer + command_length, ','); - PAddr addr = HexToInt(start_offset, static_cast<u32>(addr_pos - start_offset)); + PAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset)); if (type == BreakpointType::Access) { // Access is made up of Read and Write types, so add both breakpoints diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index 4c9b0de28..a27cfbc2d 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h @@ -305,6 +305,11 @@ inline u64 RequestParser::Pop() { } template <> +inline s64 RequestParser::Pop() { + return static_cast<s64>(Pop<u64>()); +} + +template <> inline bool RequestParser::Pop() { return Pop<u8>() != 0; } diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 10ddc4feb..656e1b4a7 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -19,6 +19,8 @@ enum class ErrorDescription : u32 { Success = 0, RemoteProcessDead = 301, + InvalidOffset = 6061, + InvalidLength = 6062, }; /** diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp new file mode 100644 index 000000000..4b47548fd --- /dev/null +++ b/src/core/hle/service/filesystem/filesystem.cpp @@ -0,0 +1,54 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <boost/container/flat_map.hpp> +#include "core/file_sys/filesystem.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/filesystem/fsp_srv.h" + +namespace Service { +namespace FileSystem { + +/** + * Map of registered file systems, identified by type. Once an file system is registered here, it + * is never removed until UnregisterFileSystems is called. + */ +static boost::container::flat_map<Type, std::unique_ptr<FileSys::FileSystemFactory>> filesystem_map; + +ResultCode RegisterFileSystem(std::unique_ptr<FileSys::FileSystemFactory>&& factory, Type type) { + auto result = filesystem_map.emplace(type, std::move(factory)); + + bool inserted = result.second; + ASSERT_MSG(inserted, "Tried to register more than one system with same id code"); + + auto& filesystem = result.first->second; + LOG_DEBUG(Service_FS, "Registered file system %s with id code 0x%08X", + filesystem->GetName().c_str(), static_cast<u32>(type)); + return RESULT_SUCCESS; +} + +ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type, + FileSys::Path& path) { + LOG_TRACE(Service_FS, "Opening FileSystem with type=%d", type); + + auto itr = filesystem_map.find(type); + if (itr == filesystem_map.end()) { + // TODO(bunnei): Find a better error code for this + return ResultCode(-1); + } + + return itr->second->Open(path); +} + +void UnregisterFileSystems() { + filesystem_map.clear(); +} + +void InstallInterfaces(SM::ServiceManager& service_manager) { + UnregisterFileSystems(); + std::make_shared<FSP_SRV>()->InstallAsService(service_manager); +} + +} // namespace FileSystem +} // namespace Service diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h new file mode 100644 index 000000000..a674c9493 --- /dev/null +++ b/src/core/hle/service/filesystem/filesystem.h @@ -0,0 +1,50 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include "common/common_types.h" +#include "core/hle/result.h" + +namespace FileSys { +class FileSystemBackend; +class FileSystemFactory; +class Path; +} // namespace FileSys + +namespace Service { + +namespace SM { +class ServiceManager; +} // namespace SM + +namespace FileSystem { + +/// Supported FileSystem types +enum class Type { + RomFS = 1, +}; + +/** + * Registers a FileSystem, instances of which can later be opened using its IdCode. + * @param factory FileSystem backend interface to use + * @param type Type used to access this type of FileSystem + */ +ResultCode RegisterFileSystem(std::unique_ptr<FileSys::FileSystemFactory>&& factory, Type type); + +/** + * Opens a file system + * @param type Type of the file system to open + * @param path Path to the file system, used with Binary paths + * @return FileSys::FileSystemBackend interface to the file system + */ +ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type, + FileSys::Path& path); + +/// Registers all Filesystem services with the specified service manager. +void InstallInterfaces(SM::ServiceManager& service_manager); + +} // namespace FileSystem +} // namespace Service diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp new file mode 100644 index 000000000..ef1915e5a --- /dev/null +++ b/src/core/hle/service/filesystem/fsp_srv.cpp @@ -0,0 +1,138 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/logging/log.h" +#include "core/core.h" +#include "core/file_sys/filesystem.h" +#include "core/file_sys/storage.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/client_port.h" +#include "core/hle/kernel/client_session.h" +#include "core/hle/service/filesystem/filesystem.h" +#include "core/hle/service/filesystem/fsp_srv.h" + +namespace Service { +namespace FileSystem { + +class IStorage final : public ServiceFramework<IStorage> { +public: + IStorage(std::unique_ptr<FileSys::StorageBackend>&& backend) + : ServiceFramework("IStorage"), backend(std::move(backend)) { + static const FunctionInfo functions[] = { + {0, &IStorage::Read, "Read"}, {1, nullptr, "Write"}, {2, nullptr, "Flush"}, + {3, nullptr, "SetSize"}, {4, nullptr, "GetSize"}, + }; + RegisterHandlers(functions); + } + +private: + std::unique_ptr<FileSys::StorageBackend> backend; + + void Read(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp{ctx}; + const s64 offset = rp.Pop<s64>(); + const s64 length = rp.Pop<s64>(); + const auto& descriptor = ctx.BufferDescriptorB()[0]; + + LOG_DEBUG(Service_FS, "called, offset=0x%llx, length=0x%llx", offset, length); + + // Error checking + ASSERT_MSG(length == descriptor.Size(), "unexpected size difference"); + if (length < 0) { + IPC::RequestBuilder rb{ctx, 2}; + rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength)); + return; + } + if (offset < 0) { + IPC::RequestBuilder rb{ctx, 2}; + rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset)); + return; + } + + // Read the data from the Storage backend + std::vector<u8> output(length); + ResultVal<size_t> res = backend->Read(offset, length, output.data()); + if (res.Failed()) { + IPC::RequestBuilder rb{ctx, 2}; + rb.Push(res.Code()); + return; + } + + // Write the data to memory + Memory::WriteBlock(descriptor.Address(), output.data(), descriptor.Size()); + + IPC::RequestBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); + } +}; + +FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { + static const FunctionInfo functions[] = { + {1, &FSP_SRV::Initalize, "Initalize"}, + {200, &FSP_SRV::OpenDataStorageByCurrentProcess, "OpenDataStorageByCurrentProcess"}, + {203, &FSP_SRV::OpenRomStorage, "OpenRomStorage"}, + {1005, &FSP_SRV::GetGlobalAccessLogMode, "GetGlobalAccessLogMode"}, + }; + RegisterHandlers(functions); +} + +void FSP_SRV::TryLoadRomFS() { + if (romfs) { + return; + } + FileSys::Path unused; + auto res = OpenFileSystem(Type::RomFS, unused); + if (res.Succeeded()) { + romfs = std::move(res.Unwrap()); + } +} + +void FSP_SRV::Initalize(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_FS, "(STUBBED) called"); + + IPC::RequestBuilder rb{ctx, 2}; + rb.Push(RESULT_SUCCESS); +} + +void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_FS, "(STUBBED) called"); + + IPC::RequestBuilder rb{ctx, 4}; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(5); +} + +void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) { + LOG_DEBUG(Service_FS, "called"); + + TryLoadRomFS(); + if (!romfs) { + // TODO (bunnei): Find the right error code to use here + LOG_CRITICAL(Service_FS, "no file system interface available!"); + IPC::RequestBuilder rb{ctx, 2}; + rb.Push(ResultCode(-1)); + return; + } + + // Attempt to open a StorageBackend interface to the RomFS + auto storage = romfs->OpenFile({}, {}); + if (storage.Failed()) { + LOG_CRITICAL(Service_FS, "no storage interface available!"); + IPC::RequestBuilder rb{ctx, 2}; + rb.Push(storage.Code()); + return; + } + + IPC::RequestBuilder rb{ctx, 2, 0, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface<IStorage>(std::move(storage.Unwrap())); +} + +void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) { + LOG_WARNING(Service_FS, "(STUBBED) called, using OpenDataStorageByCurrentProcess"); + OpenDataStorageByCurrentProcess(ctx); +} + +} // namespace FileSystem +} // namespace Service diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h new file mode 100644 index 000000000..15be8edc1 --- /dev/null +++ b/src/core/hle/service/filesystem/fsp_srv.h @@ -0,0 +1,34 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include "core/hle/service/service.h" + +namespace FileSys { +class FileSystemBackend; +} + +namespace Service { +namespace FileSystem { + +class FSP_SRV final : public ServiceFramework<FSP_SRV> { +public: + explicit FSP_SRV(); + ~FSP_SRV() = default; + +private: + void TryLoadRomFS(); + + void Initalize(Kernel::HLERequestContext& ctx); + void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx); + void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); + void OpenRomStorage(Kernel::HLERequestContext& ctx); + + std::unique_ptr<FileSys::FileSystemBackend> romfs; +}; + +} // namespace FileSystem +} // namespace Service diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 19213a2f4..3f5ce56c6 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -19,6 +19,7 @@ #include "core/hle/service/aoc/aoc_u.h" #include "core/hle/service/apm/apm.h" #include "core/hle/service/audio/audio.h" +#include "core/hle/service/filesystem/filesystem.h" #include "core/hle/service/hid/hid.h" #include "core/hle/service/lm/lm.h" #include "core/hle/service/nvdrv/nvdrv.h" @@ -172,6 +173,7 @@ void Init() { AOC::InstallInterfaces(*SM::g_service_manager); APM::InstallInterfaces(*SM::g_service_manager); Audio::InstallInterfaces(*SM::g_service_manager); + FileSystem::InstallInterfaces(*SM::g_service_manager); HID::InstallInterfaces(*SM::g_service_manager); LM::InstallInterfaces(*SM::g_service_manager); Nvidia::InstallInterfaces(*SM::g_service_manager); diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index 4bee5fb86..37030683b 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp @@ -7,14 +7,44 @@ #include "common/file_util.h" #include "common/logging/log.h" #include "common/string_util.h" +#include "core/file_sys/romfs_factory.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/resource_limit.h" +#include "core/hle/service/filesystem/filesystem.h" #include "core/loader/deconstructed_rom_directory.h" #include "core/loader/nso.h" #include "core/memory.h" namespace Loader { +static std::string FindRomFS(const std::string& directory) { + std::string filepath_romfs; + const auto callback = [&filepath_romfs](unsigned*, const std::string& directory, + const std::string& virtual_name) -> bool { + const std::string physical_name = directory + virtual_name; + if (FileUtil::IsDirectory(physical_name)) { + // Skip directories + return true; + } + + // Verify extension + const std::string extension = physical_name.substr(physical_name.find_last_of(".") + 1); + if (Common::ToLower(extension) != "istorage") { + return true; + } + + // Found it - we are done + filepath_romfs = std::move(physical_name); + return false; + }; + + // Search the specified directory recursively, looking for the first .istorage file, which will + // be used for the RomFS + FileUtil::ForeachDirectoryEntry(nullptr, directory, callback); + + return filepath_romfs; +} + AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileUtil::IOFile&& file, std::string filepath) : AppLoader(std::move(file)), filepath(std::move(filepath)) {} @@ -79,10 +109,10 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load( // Load NSO modules VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR}; + const std::string directory = filepath.substr(0, filepath.find_last_of("/\\")) + DIR_SEP; for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) { - const std::string path = - filepath.substr(0, filepath.find_last_of("/\\")) + DIR_SEP + module; + const std::string path = directory + DIR_SEP + module; const VAddr load_addr = next_load_addr; next_load_addr = AppLoader_NSO::LoadModule(path, load_addr); if (next_load_addr) { @@ -98,8 +128,43 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load( Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); process->Run(Memory::PROCESS_IMAGE_VADDR, 48, Kernel::DEFAULT_STACK_SIZE); + // Find the RomFS by searching for a ".istorage" file in this directory + filepath_romfs = FindRomFS(directory); + + // Register the RomFS if a ".istorage" file was found + if (!filepath_romfs.empty()) { + Service::FileSystem::RegisterFileSystem(std::make_unique<FileSys::RomFS_Factory>(*this), + Service::FileSystem::Type::RomFS); + } + is_loaded = true; return ResultStatus::Success; } +ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS( + std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, u64& size) { + + if (filepath_romfs.empty()) { + LOG_DEBUG(Loader, "No RomFS available"); + return ResultStatus::ErrorNotUsed; + } + + // We reopen the file, to allow its position to be independent + romfs_file = std::make_shared<FileUtil::IOFile>(filepath_romfs, "rb"); + if (!romfs_file->IsOpen()) { + return ResultStatus::Error; + } + + offset = 0; + size = romfs_file->GetSize(); + + LOG_DEBUG(Loader, "RomFS offset: 0x%08X", offset); + LOG_DEBUG(Loader, "RomFS size: 0x%08X", size); + + // Reset read pointer + file.Seek(0, SEEK_SET); + + return ResultStatus::Success; +} + } // namespace Loader diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h index 162541d54..26493de5e 100644 --- a/src/core/loader/deconstructed_rom_directory.h +++ b/src/core/loader/deconstructed_rom_directory.h @@ -35,7 +35,11 @@ public: ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; + ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, + u64& size) override; + private: + std::string filepath_romfs; std::string filepath; }; |