diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 73aec8ab0b..882c9ab59e 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -63,6 +63,10 @@ add_library(core STATIC file_sys/sdmc_factory.h file_sys/submission_package.cpp file_sys/submission_package.h + file_sys/system_archive/ng_word.cpp + file_sys/system_archive/ng_word.h + file_sys/system_archive/system_archive.cpp + file_sys/system_archive/system_archive.h file_sys/vfs.cpp file_sys/vfs.h file_sys/vfs_concat.cpp diff --git a/src/core/file_sys/system_archive/ng_word.cpp b/src/core/file_sys/system_archive/ng_word.cpp new file mode 100644 index 0000000000..d0acdbd494 --- /dev/null +++ b/src/core/file_sys/system_archive/ng_word.cpp @@ -0,0 +1,42 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include "common/common_types.h" +#include "core/file_sys/system_archive/ng_word.h" +#include "core/file_sys/vfs_vector.h" + +namespace FileSys::SystemArchive { + +namespace NgWord1Data { + +constexpr std::size_t NUMBER_WORD_TXT_FILES = 0x10; + +// Should this archive replacement mysteriously not work on a future game, consider updating. +constexpr std::array VERSION_DAT{0x0, 0x0, 0x0, 0x19}; // 5.1.0 System Version + +constexpr std::array WORD_TXT{ + 0xFE, 0xFF, 0x00, 0x5E, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x79, 0x00, 0x62, 0x00, + 0x61, 0x00, 0x64, 0x00, 0x77, 0x00, 0x6F, 0x00, 0x72, 0x00, 0x64, 0x00, 0x24, 0x00, 0x0A, +}; // "^verybadword$" in UTF-16 + +} // namespace NgWord1Data + +VirtualDir NgWord1() { + std::vector files(NgWord1Data::NUMBER_WORD_TXT_FILES); + + for (std::size_t i = 0; i < NgWord1Data::NUMBER_WORD_TXT_FILES; ++i) { + files[i] = std::make_shared>( + NgWord1Data::WORD_TXT, fmt::format("{}.txt", i)); + } + + files.push_back(std::make_shared>( + NgWord1Data::WORD_TXT, "common.txt")); + files.push_back(std::make_shared>( + NgWord1Data::VERSION_DAT, "version.dat")); + + return std::make_shared(files, std::vector{}, "data"); +} + +} // namespace FileSys::SystemArchive diff --git a/src/core/file_sys/system_archive/ng_word.h b/src/core/file_sys/system_archive/ng_word.h new file mode 100644 index 0000000000..f4bc673448 --- /dev/null +++ b/src/core/file_sys/system_archive/ng_word.h @@ -0,0 +1,13 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/file_sys/vfs_types.h" + +namespace FileSys::SystemArchive { + +VirtualDir NgWord1(); + +} // namespace FileSys::SystemArchive diff --git a/src/core/file_sys/system_archive/system_archive.cpp b/src/core/file_sys/system_archive/system_archive.cpp new file mode 100644 index 0000000000..c9c40a07df --- /dev/null +++ b/src/core/file_sys/system_archive/system_archive.cpp @@ -0,0 +1,91 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include "common/logging/log.h" +#include "core/file_sys/romfs.h" +#include "core/file_sys/system_archive/ng_word.h" +#include "core/file_sys/system_archive/system_archive.h" + +namespace FileSys::SystemArchive { + +constexpr u64 SYSTEM_ARCHIVE_BASE_TITLE_ID = 0x0100000000000800; +constexpr std::size_t SYSTEM_ARCHIVE_COUNT = 0x28; + +using SystemArchiveSupplier = std::function; + +struct SystemArchiveDescriptor { + u64 title_id; + const char* name; + SystemArchiveSupplier supplier; +}; + +const std::array SYSTEM_ARCHIVES = {{ + {0x0100000000000800, "CertStore", nullptr}, + {0x0100000000000801, "ErrorMessage", nullptr}, + {0x0100000000000802, "MiiModel", nullptr}, + {0x0100000000000803, "BrowserDll", nullptr}, + {0x0100000000000804, "Help", nullptr}, + {0x0100000000000805, "SharedFont", nullptr}, + {0x0100000000000806, "NgWord", &NgWord1}, + {0x0100000000000807, "SsidList", nullptr}, + {0x0100000000000808, "Dictionary", nullptr}, + {0x0100000000000809, "SystemVersion", nullptr}, + {0x010000000000080A, "AvatarImage", nullptr}, + {0x010000000000080B, "LocalNews", nullptr}, + {0x010000000000080C, "Eula", nullptr}, + {0x010000000000080D, "UrlBlackList", nullptr}, + {0x010000000000080E, "TimeZoneBinary", nullptr}, + {0x010000000000080F, "CertStoreCruiser", nullptr}, + {0x0100000000000810, "FontNintendoExtension", nullptr}, + {0x0100000000000811, "FontStandard", nullptr}, + {0x0100000000000812, "FontKorean", nullptr}, + {0x0100000000000813, "FontChineseTraditional", nullptr}, + {0x0100000000000814, "FontChineseSimple", nullptr}, + {0x0100000000000815, "FontBfcpx", nullptr}, + {0x0100000000000816, "SystemUpdate", nullptr}, + {0x0100000000000817, "0100000000000817", nullptr}, + {0x0100000000000818, "FirmwareDebugSettings", nullptr}, + {0x0100000000000819, "BootImagePackage", nullptr}, + {0x010000000000081A, "BootImagePackageSafe", nullptr}, + {0x010000000000081B, "BootImagePackageExFat", nullptr}, + {0x010000000000081C, "BootImagePackageExFatSafe", nullptr}, + {0x010000000000081D, "FatalMessage", nullptr}, + {0x010000000000081E, "ControllerIcon", nullptr}, + {0x010000000000081F, "PlatformConfigIcosa", nullptr}, + {0x0100000000000820, "PlatformConfigCopper", nullptr}, + {0x0100000000000821, "PlatformConfigHoag", nullptr}, + {0x0100000000000822, "ControllerFirmware", nullptr}, + {0x0100000000000823, "NgWord2", nullptr}, + {0x0100000000000824, "PlatformConfigIcosaMariko", nullptr}, + {0x0100000000000825, "ApplicationBlackList", nullptr}, + {0x0100000000000826, "RebootlessSystemUpdateVersion", nullptr}, + {0x0100000000000827, "ContentActionTable", nullptr}, +}}; + +VirtualFile SynthesizeSystemArchive(const u64 title_id) { + if (title_id < SYSTEM_ARCHIVES.front().title_id || title_id > SYSTEM_ARCHIVES.back().title_id) + return nullptr; + + const auto& desc = SYSTEM_ARCHIVES[title_id - SYSTEM_ARCHIVE_BASE_TITLE_ID]; + + LOG_INFO(Service_FS, "Synthesizing system archive '{}' (0x{:016X}).", desc.name, desc.title_id); + + if (desc.supplier == nullptr) + return nullptr; + + const auto dir = desc.supplier(); + + if (dir == nullptr) + return nullptr; + + const auto romfs = CreateRomFS(dir); + + if (romfs == nullptr) + return nullptr; + + LOG_INFO(Service_FS, " - System archive generation successful!"); + return romfs; +} +} // namespace FileSys::SystemArchive diff --git a/src/core/file_sys/system_archive/system_archive.h b/src/core/file_sys/system_archive/system_archive.h new file mode 100644 index 0000000000..724a8eb17e --- /dev/null +++ b/src/core/file_sys/system_archive/system_archive.h @@ -0,0 +1,14 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" +#include "core/file_sys/vfs_types.h" + +namespace FileSys::SystemArchive { + +VirtualFile SynthesizeSystemArchive(u64 title_id); + +} // namespace FileSys::SystemArchive diff --git a/src/core/file_sys/vfs_vector.cpp b/src/core/file_sys/vfs_vector.cpp index 808f31e81c..515626658e 100644 --- a/src/core/file_sys/vfs_vector.cpp +++ b/src/core/file_sys/vfs_vector.cpp @@ -3,7 +3,6 @@ // Refer to the license.txt file included. #include -#include #include #include "core/file_sys/vfs_vector.h" diff --git a/src/core/file_sys/vfs_vector.h b/src/core/file_sys/vfs_vector.h index 3e3f790c3d..ac36cb2ee7 100644 --- a/src/core/file_sys/vfs_vector.h +++ b/src/core/file_sys/vfs_vector.h @@ -4,10 +4,63 @@ #pragma once +#include #include "core/file_sys/vfs.h" namespace FileSys { +// An implementation of VfsFile that is backed by a statically-sized array +template +class ArrayVfsFile : public VfsFile { +public: + ArrayVfsFile(std::array data, std::string name = "", VirtualDir parent = nullptr) + : data(data), name(std::move(name)), parent(std::move(parent)) {} + + std::string GetName() const override { + return name; + } + + std::size_t GetSize() const override { + return size; + } + + bool Resize(std::size_t new_size) override { + return false; + } + + std::shared_ptr GetContainingDirectory() const override { + return parent; + } + + bool IsWritable() const override { + return false; + } + + bool IsReadable() const override { + return true; + } + + std::size_t Read(u8* data_, std::size_t length, std::size_t offset) const override { + const auto read = std::min(length, size - offset); + std::memcpy(data_, data.data() + offset, read); + return read; + } + + std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override { + return 0; + } + + bool Rename(std::string_view name) override { + this->name = name; + return true; + } + +private: + std::array data; + std::string name; + VirtualDir parent; +}; + // An implementation of VfsFile that is backed by a vector optionally supplied upon construction class VectorVfsFile : public VfsFile { public: diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index 694ec40ec1..d2ffd57768 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp @@ -20,6 +20,7 @@ #include "core/file_sys/nca_metadata.h" #include "core/file_sys/patch_manager.h" #include "core/file_sys/savedata_factory.h" +#include "core/file_sys/system_archive/system_archive.h" #include "core/file_sys/vfs.h" #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/process.h" @@ -831,6 +832,15 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) { auto data = OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data); if (data.Failed()) { + const auto archive = FileSys::SystemArchive::SynthesizeSystemArchive(title_id); + + if (archive != nullptr) { + IPC::ResponseBuilder rb{ctx, 2, 0, 1}; + rb.Push(RESULT_SUCCESS); + rb.PushIpcInterface(std::make_shared(archive)); + return; + } + // TODO(DarkLordZach): Find the right error code to use here LOG_ERROR(Service_FS, "could not open data storage with title_id={:016X}, storage_id={:02X}", title_id,