diff --git a/common/common_funcs.h b/common/common_funcs.h new file mode 100755 index 0000000..2567d69 --- /dev/null +++ b/common/common_funcs.h @@ -0,0 +1,147 @@ +// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +#if !defined(ARCHITECTURE_x86_64) +#include // for exit +#endif +#include "common_types.h" + +/// Textually concatenates two tokens. The double-expansion is required by the C preprocessor. +#define CONCAT2(x, y) DO_CONCAT2(x, y) +#define DO_CONCAT2(x, y) x##y + +/// Helper macros to insert unused bytes or words to properly align structs. These values will be +/// zero-initialized. +#define INSERT_PADDING_BYTES(num_bytes) \ + [[maybe_unused]] std::array CONCAT2(pad, __LINE__) {} +#define INSERT_PADDING_WORDS(num_words) \ + [[maybe_unused]] std::array CONCAT2(pad, __LINE__) {} + +/// These are similar to the INSERT_PADDING_* macros but do not zero-initialize the contents. +/// This keeps the structure trivial to construct. +#define INSERT_PADDING_BYTES_NOINIT(num_bytes) \ + [[maybe_unused]] std::array CONCAT2(pad, __LINE__) +#define INSERT_PADDING_WORDS_NOINIT(num_words) \ + [[maybe_unused]] std::array CONCAT2(pad, __LINE__) + +#ifndef _MSC_VER + +#if defined(ARCHITECTURE_x86_64) +#define Crash() __asm__ __volatile__("int $3") +#elif defined(ARCHITECTURE_arm64) +#define Crash() __asm__ __volatile__("brk #0") +#else +#define Crash() exit(1) +#endif + +#define LTO_NOINLINE __attribute__((noinline)) + +#else // _MSC_VER + +#define LTO_NOINLINE + +// Locale Cross-Compatibility +#define locale_t _locale_t + +extern "C" { +__declspec(dllimport) void __stdcall DebugBreak(void); +} +#define Crash() DebugBreak() + +#endif // _MSC_VER ndef + +#define DECLARE_ENUM_FLAG_OPERATORS(type) \ + [[nodiscard]] constexpr type operator|(type a, type b) noexcept { \ + using T = std::underlying_type_t; \ + return static_cast(static_cast(a) | static_cast(b)); \ + } \ + [[nodiscard]] constexpr type operator&(type a, type b) noexcept { \ + using T = std::underlying_type_t; \ + return static_cast(static_cast(a) & static_cast(b)); \ + } \ + [[nodiscard]] constexpr type operator^(type a, type b) noexcept { \ + using T = std::underlying_type_t; \ + return static_cast(static_cast(a) ^ static_cast(b)); \ + } \ + [[nodiscard]] constexpr type operator<<(type a, type b) noexcept { \ + using T = std::underlying_type_t; \ + return static_cast(static_cast(a) << static_cast(b)); \ + } \ + [[nodiscard]] constexpr type operator>>(type a, type b) noexcept { \ + using T = std::underlying_type_t; \ + return static_cast(static_cast(a) >> static_cast(b)); \ + } \ + constexpr type& operator|=(type& a, type b) noexcept { \ + a = a | b; \ + return a; \ + } \ + constexpr type& operator&=(type& a, type b) noexcept { \ + a = a & b; \ + return a; \ + } \ + constexpr type& operator^=(type& a, type b) noexcept { \ + a = a ^ b; \ + return a; \ + } \ + constexpr type& operator<<=(type& a, type b) noexcept { \ + a = a << b; \ + return a; \ + } \ + constexpr type& operator>>=(type& a, type b) noexcept { \ + a = a >> b; \ + return a; \ + } \ + [[nodiscard]] constexpr type operator~(type key) noexcept { \ + using T = std::underlying_type_t; \ + return static_cast(~static_cast(key)); \ + } \ + [[nodiscard]] constexpr bool True(type key) noexcept { \ + using T = std::underlying_type_t; \ + return static_cast(key) != 0; \ + } \ + [[nodiscard]] constexpr bool False(type key) noexcept { \ + using T = std::underlying_type_t; \ + return static_cast(key) == 0; \ + } + +#define YUZU_NON_COPYABLE(cls) \ + cls(const cls&) = delete; \ + cls& operator=(const cls&) = delete + +#define YUZU_NON_MOVEABLE(cls) \ + cls(cls&&) = delete; \ + cls& operator=(cls&&) = delete + +namespace Common { + +[[nodiscard]] constexpr u32 MakeMagic(char a, char b, char c, char d) { + return u32(a) | u32(b) << 8 | u32(c) << 16 | u32(d) << 24; +} + +[[nodiscard]] constexpr u64 MakeMagic(char a, char b, char c, char d, char e, char f, char g, + char h) { + return u64(a) << 0 | u64(b) << 8 | u64(c) << 16 | u64(d) << 24 | u64(e) << 32 | u64(f) << 40 | + u64(g) << 48 | u64(h) << 56; +} + +// std::size() does not support zero-size C arrays. We're fixing that. +template +constexpr auto Size(const C& c) -> decltype(c.size()) { + return std::size(c); +} + +template +constexpr std::size_t Size(const C& c) { + if constexpr (sizeof(C) == 0) { + return 0; + } else { + return std::size(c); + } +} + +} // namespace Common diff --git a/core/fs/bis_factory.cpp b/core/fs/bis_factory.cpp index f32db19..ed91217 100644 --- a/core/fs/bis_factory.cpp +++ b/core/fs/bis_factory.cpp @@ -5,9 +5,9 @@ #include #include "../common/fs/path_util.h" -#include "core/fs/bis_factory.h" -#include "core/fs/registered_cache.h" -#include "core/fs/vfs/vfs.h" +#include "bis_factory.h" +#include "registered_cache.h" +#include "vfs/vfs.h" namespace FileSys { diff --git a/core/fs/card_image.cpp b/core/fs/card_image.cpp index 293dcbe..eb6c253 100644 --- a/core/fs/card_image.cpp +++ b/core/fs/card_image.cpp @@ -8,16 +8,16 @@ #include -#include "common/logging/log.h" -#include "core/crypto/key_manager.h" -#include "core/fs/card_image.h" -#include "core/fs/content_archive.h" -#include "core/fs/nca_metadata.h" -#include "core/fs/partition_filesystem.h" -#include "core/fs/submission_package.h" -#include "core/fs/vfs/vfs_offset.h" -#include "core/fs/vfs/vfs_vector.h" -#include "core/loader/loader.h" +#include "../common/logging/log.h" +#include "crypto/key_manager.h" +#include "card_image.h" +#include "content_archive.h" +#include "nca_metadata.h" +#include "partition_filesystem.h" +#include "submission_package.h" +#include "vfs/vfs_offset.h" +#include "vfs/vfs_vector.h" +#include "loader/loader.h" namespace FileSys { diff --git a/core/loader/loader.h b/core/loader/loader.h new file mode 100755 index 0000000..b4dc4f7 --- /dev/null +++ b/core/loader/loader.h @@ -0,0 +1,342 @@ +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "common/common_funcs.h" +#include "common/common_types.h" +#include "fs/control_metadata.h" +#include "fs/vfs/vfs.h" + +namespace Core { +class System; +} + +namespace FileSys { +class NACP; +} // namespace FileSys + +namespace Kernel { +struct AddressMapping; +class KProcess; +} // namespace Kernel + +namespace Loader { + +/// File types supported by CTR +enum class FileType { + Error, + Unknown, + NSO, + NRO, + NCA, + NSP, + XCI, + NAX, + KIP, + DeconstructedRomDirectory, +}; + +/** + * Identifies the type of a bootable file based on the magic value in its header. + * @param file open file + * @return FileType of file + */ +FileType IdentifyFile(FileSys::VirtualFile file); + +/** + * Guess the type of a bootable file from its name + * @param name String name of bootable file + * @return FileType of file. Note: this will return FileType::Unknown if it is unable to determine + * a filetype, and will never return FileType::Error. + */ +FileType GuessFromFilename(const std::string& name); + +/** + * Convert a FileType into a string which can be displayed to the user. + */ +std::string GetFileTypeString(FileType type); + +/// Return type for functions in Loader namespace +enum class ResultStatus : u16 { + Success, + ErrorAlreadyLoaded, + ErrorNotImplemented, + ErrorNotInitialized, + ErrorBadNPDMHeader, + ErrorBadACIDHeader, + ErrorBadACIHeader, + ErrorBadFileAccessControl, + ErrorBadFileAccessHeader, + ErrorBadKernelCapabilityDescriptors, + ErrorBadPFSHeader, + ErrorIncorrectPFSFileSize, + ErrorBadNCAHeader, + ErrorMissingProductionKeyFile, + ErrorMissingHeaderKey, + ErrorIncorrectHeaderKey, + ErrorNCA2, + ErrorNCA0, + ErrorMissingTitlekey, + ErrorMissingTitlekek, + ErrorInvalidRightsID, + ErrorMissingKeyAreaKey, + ErrorIncorrectKeyAreaKey, + ErrorIncorrectTitlekeyOrTitlekek, + ErrorXCIMissingProgramNCA, + ErrorNCANotProgram, + ErrorNoExeFS, + ErrorBadXCIHeader, + ErrorXCIMissingPartition, + ErrorNullFile, + ErrorMissingNPDM, + Error32BitISA, + ErrorUnableToParseKernelMetadata, + ErrorNoRomFS, + ErrorIncorrectELFFileSize, + ErrorLoadingNRO, + ErrorLoadingNSO, + ErrorNoIcon, + ErrorNoControl, + ErrorBadNAXHeader, + ErrorIncorrectNAXFileSize, + ErrorNAXKeyHMACFailed, + ErrorNAXValidationHMACFailed, + ErrorNAXKeyDerivationFailed, + ErrorNAXInconvertibleToNCA, + ErrorBadNAXFilePath, + ErrorMissingSDSeed, + ErrorMissingSDKEKSource, + ErrorMissingAESKEKGenerationSource, + ErrorMissingAESKeyGenerationSource, + ErrorMissingSDSaveKeySource, + ErrorMissingSDNCAKeySource, + ErrorNSPMissingProgramNCA, + ErrorBadBKTRHeader, + ErrorBKTRSubsectionNotAfterRelocation, + ErrorBKTRSubsectionNotAtEnd, + ErrorBadRelocationBlock, + ErrorBadSubsectionBlock, + ErrorBadRelocationBuckets, + ErrorBadSubsectionBuckets, + ErrorMissingBKTRBaseRomFS, + ErrorNoPackedUpdate, + ErrorBadKIPHeader, + ErrorBLZDecompressionFailed, + ErrorBadINIHeader, + ErrorINITooManyKIPs, + ErrorIntegrityVerificationNotImplemented, + ErrorIntegrityVerificationFailed, +}; + +std::string GetResultStatusString(ResultStatus status); +std::ostream& operator<<(std::ostream& os, ResultStatus status); + +/// Interface for loading an application +class AppLoader { +public: + YUZU_NON_COPYABLE(AppLoader); + YUZU_NON_MOVEABLE(AppLoader); + + struct LoadParameters { + s32 main_thread_priority; + u64 main_thread_stack_size; + }; + using LoadResult = std::pair>; + + explicit AppLoader(FileSys::VirtualFile file_); + virtual ~AppLoader(); + + /** + * Returns the type of this file + * + * @return FileType corresponding to the loaded file + */ + virtual FileType GetFileType() const = 0; + + /** + * Load the application and return the created Process instance + * + * @param process The newly created process. + * @param system The system that this process is being loaded under. + * + * @return The status result of the operation. + */ + virtual LoadResult Load(Kernel::KProcess& process, Core::System& system) = 0; + + /** + * Try to verify the integrity of the file. + */ + virtual ResultStatus VerifyIntegrity(std::function progress_callback) { + return ResultStatus::ErrorIntegrityVerificationNotImplemented; + } + + /** + * Get the code (typically .code section) of the application + * + * @param[out] buffer Reference to buffer to store data + * + * @return ResultStatus result of function + */ + virtual ResultStatus ReadCode(std::vector& buffer) { + return ResultStatus::ErrorNotImplemented; + } + + /** + * Get the icon (typically icon section) of the application + * + * @param[out] buffer Reference to buffer to store data + * + * @return ResultStatus result of function + */ + virtual ResultStatus ReadIcon(std::vector& buffer) { + return ResultStatus::ErrorNotImplemented; + } + + /** + * Get the banner (typically banner section) of the application + * In the context of NX, this is the animation that displays in the bottom right of the screen + * when a game boots. Stored in GIF format. + * + * @param[out] buffer Reference to buffer to store data + * + * @return ResultStatus result of function + */ + virtual ResultStatus ReadBanner(std::vector& buffer) { + return ResultStatus::ErrorNotImplemented; + } + + /** + * Get the logo (typically logo section) of the application + * In the context of NX, this is the static image that displays in the top left of the screen + * when a game boots. Stored in JPEG format. + * + * @param[out] buffer Reference to buffer to store data + * + * @return ResultStatus result of function + */ + virtual ResultStatus ReadLogo(std::vector& buffer) { + return ResultStatus::ErrorNotImplemented; + } + + /** + * Get the program id of the application + * + * @param[out] out_program_id Reference to store program id into + * + * @return ResultStatus result of function + */ + virtual ResultStatus ReadProgramId(u64& out_program_id) { + return ResultStatus::ErrorNotImplemented; + } + + /** + * Get the program ids of the application + * + * @param[out] out_program_ids Reference to store program ids into + * + * @return ResultStatus result of function + */ + virtual ResultStatus ReadProgramIds(std::vector& out_program_ids) { + return ResultStatus::ErrorNotImplemented; + } + + /** + * Get the RomFS of the application + * Since the RomFS can be huge, we return a file reference instead of copying to a buffer + * + * @param[out] out_file The directory containing the RomFS + * + * @return ResultStatus result of function + */ + virtual ResultStatus ReadRomFS(FileSys::VirtualFile& out_file) { + return ResultStatus::ErrorNotImplemented; + } + + /** + * Get the raw update of the application, should it come packed with one + * + * @param[out] out_file The raw update NCA file (Program-type) + * + * @return ResultStatus result of function + */ + virtual ResultStatus ReadUpdateRaw(FileSys::VirtualFile& out_file) { + return ResultStatus::ErrorNotImplemented; + } + + /** + * Get whether or not updates can be applied to the RomFS. + * By default, this is true, however for formats where it cannot be guaranteed that the RomFS is + * the base game it should be set to false. + * + * @return bool indicating whether or not the RomFS is updatable. + */ + virtual bool IsRomFSUpdatable() const { + return true; + } + + /** + * Get the title of the application + * + * @param[out] title Reference to store the application title into + * + * @return ResultStatus result of function + */ + virtual ResultStatus ReadTitle(std::string& title) { + return ResultStatus::ErrorNotImplemented; + } + + /** + * Get the control data (CNMT) of the application + * + * @param[out] control Reference to store the application control data into + * + * @return ResultStatus result of function + */ + virtual ResultStatus ReadControlData(FileSys::NACP& control) { + return ResultStatus::ErrorNotImplemented; + } + + /** + * Get the RomFS of the manual of the application + * + * @param[out] out_file The raw manual RomFS of the game + * + * @return ResultStatus result of function + */ + virtual ResultStatus ReadManualRomFS(FileSys::VirtualFile& out_file) { + return ResultStatus::ErrorNotImplemented; + } + + using Modules = std::map; + + virtual ResultStatus ReadNSOModules(Modules& modules) { + return ResultStatus::ErrorNotImplemented; + } + +protected: + FileSys::VirtualFile file; + bool is_loaded = false; +}; + +/** + * Identifies a bootable file and return a suitable loader + * + * @param system The system context. + * @param file The bootable file. + * @param program_index Specifies the index within the container of the program to launch. + * + * @return the best loader for this file. + */ +std::unique_ptr GetLoader(Core::System& system, FileSys::VirtualFile file, + u64 program_id = 0, std::size_t program_index = 0); + +} // namespace Loader