mirror of
				https://git.zaroz.cloud/nintendo-back-up/yuzu/yuzu.git
				synced 2025-05-12 00:45:25 +00:00 
			
		
		
		
	Merge pull request #1178 from DarkLordZach/nsp
file_sys: Add Nintendo Submissions Package (NSP) file format
This commit is contained in:
		
						commit
						faa9e066ab
					
				@ -49,6 +49,8 @@ add_library(core STATIC
 | 
				
			|||||||
    file_sys/savedata_factory.h
 | 
					    file_sys/savedata_factory.h
 | 
				
			||||||
    file_sys/sdmc_factory.cpp
 | 
					    file_sys/sdmc_factory.cpp
 | 
				
			||||||
    file_sys/sdmc_factory.h
 | 
					    file_sys/sdmc_factory.h
 | 
				
			||||||
 | 
					    file_sys/submission_package.cpp
 | 
				
			||||||
 | 
					    file_sys/submission_package.h
 | 
				
			||||||
    file_sys/vfs.cpp
 | 
					    file_sys/vfs.cpp
 | 
				
			||||||
    file_sys/vfs.h
 | 
					    file_sys/vfs.h
 | 
				
			||||||
    file_sys/vfs_concat.cpp
 | 
					    file_sys/vfs_concat.cpp
 | 
				
			||||||
@ -359,6 +361,8 @@ add_library(core STATIC
 | 
				
			|||||||
    loader/nro.h
 | 
					    loader/nro.h
 | 
				
			||||||
    loader/nso.cpp
 | 
					    loader/nso.cpp
 | 
				
			||||||
    loader/nso.h
 | 
					    loader/nso.h
 | 
				
			||||||
 | 
					    loader/nsp.cpp
 | 
				
			||||||
 | 
					    loader/nsp.h
 | 
				
			||||||
    loader/xci.cpp
 | 
					    loader/xci.cpp
 | 
				
			||||||
    loader/xci.h
 | 
					    loader/xci.h
 | 
				
			||||||
    memory.cpp
 | 
					    memory.cpp
 | 
				
			||||||
 | 
				
			|||||||
@ -231,18 +231,28 @@ void KeyManager::WriteKeyToFile(bool title_key, std::string_view keyname,
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
 | 
					void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
 | 
				
			||||||
    const auto iter = std::find_if(
 | 
					    if (s128_keys.find({id, field1, field2}) != s128_keys.end())
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    if (id == S128KeyType::Titlekey) {
 | 
				
			||||||
 | 
					        Key128 rights_id;
 | 
				
			||||||
 | 
					        std::memcpy(rights_id.data(), &field2, sizeof(u64));
 | 
				
			||||||
 | 
					        std::memcpy(rights_id.data() + sizeof(u64), &field1, sizeof(u64));
 | 
				
			||||||
 | 
					        WriteKeyToFile(true, Common::HexArrayToString(rights_id), key);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    const auto iter2 = std::find_if(
 | 
				
			||||||
        s128_file_id.begin(), s128_file_id.end(),
 | 
					        s128_file_id.begin(), s128_file_id.end(),
 | 
				
			||||||
        [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S128KeyType>> elem) {
 | 
					        [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S128KeyType>> elem) {
 | 
				
			||||||
            return std::tie(elem.second.type, elem.second.field1, elem.second.field2) ==
 | 
					            return std::tie(elem.second.type, elem.second.field1, elem.second.field2) ==
 | 
				
			||||||
                   std::tie(id, field1, field2);
 | 
					                   std::tie(id, field1, field2);
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    if (iter != s128_file_id.end())
 | 
					    if (iter2 != s128_file_id.end())
 | 
				
			||||||
        WriteKeyToFile(id == S128KeyType::Titlekey, iter->first, key);
 | 
					        WriteKeyToFile(false, iter2->first, key);
 | 
				
			||||||
    s128_keys[{id, field1, field2}] = key;
 | 
					    s128_keys[{id, field1, field2}] = key;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
 | 
					void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
 | 
				
			||||||
 | 
					    if (s256_keys.find({id, field1, field2}) != s256_keys.end())
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
    const auto iter = std::find_if(
 | 
					    const auto iter = std::find_if(
 | 
				
			||||||
        s256_file_id.begin(), s256_file_id.end(),
 | 
					        s256_file_id.begin(), s256_file_id.end(),
 | 
				
			||||||
        [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S256KeyType>> elem) {
 | 
					        [&id, &field1, &field2](const std::pair<std::string, KeyIndex<S256KeyType>> elem) {
 | 
				
			||||||
 | 
				
			|||||||
@ -17,6 +17,8 @@ enum class ResultStatus : u16;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace Core::Crypto {
 | 
					namespace Core::Crypto {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					constexpr u64 TICKET_FILE_TITLEKEY_OFFSET = 0x180;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using Key128 = std::array<u8, 0x10>;
 | 
					using Key128 = std::array<u8, 0x10>;
 | 
				
			||||||
using Key256 = std::array<u8, 0x20>;
 | 
					using Key256 = std::array<u8, 0x20>;
 | 
				
			||||||
using SHA256Hash = std::array<u8, 0x20>;
 | 
					using SHA256Hash = std::array<u8, 0x20>;
 | 
				
			||||||
 | 
				
			|||||||
@ -10,7 +10,9 @@
 | 
				
			|||||||
#include "common/logging/log.h"
 | 
					#include "common/logging/log.h"
 | 
				
			||||||
#include "core/file_sys/card_image.h"
 | 
					#include "core/file_sys/card_image.h"
 | 
				
			||||||
#include "core/file_sys/content_archive.h"
 | 
					#include "core/file_sys/content_archive.h"
 | 
				
			||||||
 | 
					#include "core/file_sys/nca_metadata.h"
 | 
				
			||||||
#include "core/file_sys/partition_filesystem.h"
 | 
					#include "core/file_sys/partition_filesystem.h"
 | 
				
			||||||
 | 
					#include "core/file_sys/submission_package.h"
 | 
				
			||||||
#include "core/file_sys/vfs_offset.h"
 | 
					#include "core/file_sys/vfs_offset.h"
 | 
				
			||||||
#include "core/loader/loader.h"
 | 
					#include "core/loader/loader.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -44,15 +46,19 @@ XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) {
 | 
				
			|||||||
            partitions[static_cast<size_t>(partition)] = std::make_shared<PartitionFilesystem>(raw);
 | 
					            partitions[static_cast<size_t>(partition)] = std::make_shared<PartitionFilesystem>(raw);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    secure_partition = std::make_shared<NSP>(
 | 
				
			||||||
 | 
					        main_hfs.GetFile(partition_names[static_cast<size_t>(XCIPartition::Secure)]));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const auto secure_ncas = secure_partition->GetNCAsCollapsed();
 | 
				
			||||||
 | 
					    std::copy(secure_ncas.begin(), secure_ncas.end(), std::back_inserter(ncas));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    program_nca_status = Loader::ResultStatus::ErrorXCIMissingProgramNCA;
 | 
					    program_nca_status = Loader::ResultStatus::ErrorXCIMissingProgramNCA;
 | 
				
			||||||
 | 
					    program =
 | 
				
			||||||
 | 
					        secure_partition->GetNCA(secure_partition->GetProgramTitleID(), ContentRecordType::Program);
 | 
				
			||||||
 | 
					    if (program != nullptr)
 | 
				
			||||||
 | 
					        program_nca_status = program->GetStatus();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    auto result = AddNCAFromPartition(XCIPartition::Secure);
 | 
					    auto result = AddNCAFromPartition(XCIPartition::Update);
 | 
				
			||||||
    if (result != Loader::ResultStatus::Success) {
 | 
					 | 
				
			||||||
        status = result;
 | 
					 | 
				
			||||||
        return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    result = AddNCAFromPartition(XCIPartition::Update);
 | 
					 | 
				
			||||||
    if (result != Loader::ResultStatus::Success) {
 | 
					    if (result != Loader::ResultStatus::Success) {
 | 
				
			||||||
        status = result;
 | 
					        status = result;
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
@ -89,6 +95,10 @@ VirtualDir XCI::GetPartition(XCIPartition partition) const {
 | 
				
			|||||||
    return partitions[static_cast<size_t>(partition)];
 | 
					    return partitions[static_cast<size_t>(partition)];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::shared_ptr<NSP> XCI::GetSecurePartitionNSP() const {
 | 
				
			||||||
 | 
					    return secure_partition;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
VirtualDir XCI::GetSecurePartition() const {
 | 
					VirtualDir XCI::GetSecurePartition() const {
 | 
				
			||||||
    return GetPartition(XCIPartition::Secure);
 | 
					    return GetPartition(XCIPartition::Secure);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -105,6 +115,20 @@ VirtualDir XCI::GetLogoPartition() const {
 | 
				
			|||||||
    return GetPartition(XCIPartition::Logo);
 | 
					    return GetPartition(XCIPartition::Logo);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					u64 XCI::GetProgramTitleID() const {
 | 
				
			||||||
 | 
					    return secure_partition->GetProgramTitleID();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::shared_ptr<NCA> XCI::GetProgramNCA() const {
 | 
				
			||||||
 | 
					    return program;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					VirtualFile XCI::GetProgramNCAFile() const {
 | 
				
			||||||
 | 
					    if (GetProgramNCA() == nullptr)
 | 
				
			||||||
 | 
					        return nullptr;
 | 
				
			||||||
 | 
					    return GetProgramNCA()->GetBaseFile();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const std::vector<std::shared_ptr<NCA>>& XCI::GetNCAs() const {
 | 
					const std::vector<std::shared_ptr<NCA>>& XCI::GetNCAs() const {
 | 
				
			||||||
    return ncas;
 | 
					    return ncas;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -19,6 +19,7 @@ namespace FileSys {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class NCA;
 | 
					class NCA;
 | 
				
			||||||
enum class NCAContentType : u8;
 | 
					enum class NCAContentType : u8;
 | 
				
			||||||
 | 
					class NSP;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum class GamecardSize : u8 {
 | 
					enum class GamecardSize : u8 {
 | 
				
			||||||
    S_1GB = 0xFA,
 | 
					    S_1GB = 0xFA,
 | 
				
			||||||
@ -71,11 +72,16 @@ public:
 | 
				
			|||||||
    u8 GetFormatVersion() const;
 | 
					    u8 GetFormatVersion() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    VirtualDir GetPartition(XCIPartition partition) const;
 | 
					    VirtualDir GetPartition(XCIPartition partition) const;
 | 
				
			||||||
 | 
					    std::shared_ptr<NSP> GetSecurePartitionNSP() const;
 | 
				
			||||||
    VirtualDir GetSecurePartition() const;
 | 
					    VirtualDir GetSecurePartition() const;
 | 
				
			||||||
    VirtualDir GetNormalPartition() const;
 | 
					    VirtualDir GetNormalPartition() const;
 | 
				
			||||||
    VirtualDir GetUpdatePartition() const;
 | 
					    VirtualDir GetUpdatePartition() const;
 | 
				
			||||||
    VirtualDir GetLogoPartition() const;
 | 
					    VirtualDir GetLogoPartition() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    u64 GetProgramTitleID() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::shared_ptr<NCA> GetProgramNCA() const;
 | 
				
			||||||
 | 
					    VirtualFile GetProgramNCAFile() const;
 | 
				
			||||||
    const std::vector<std::shared_ptr<NCA>>& GetNCAs() const;
 | 
					    const std::vector<std::shared_ptr<NCA>>& GetNCAs() const;
 | 
				
			||||||
    std::shared_ptr<NCA> GetNCAByType(NCAContentType type) const;
 | 
					    std::shared_ptr<NCA> GetNCAByType(NCAContentType type) const;
 | 
				
			||||||
    VirtualFile GetNCAFileByType(NCAContentType type) const;
 | 
					    VirtualFile GetNCAFileByType(NCAContentType type) const;
 | 
				
			||||||
@ -101,6 +107,8 @@ private:
 | 
				
			|||||||
    Loader::ResultStatus program_nca_status;
 | 
					    Loader::ResultStatus program_nca_status;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::vector<VirtualDir> partitions;
 | 
					    std::vector<VirtualDir> partitions;
 | 
				
			||||||
 | 
					    std::shared_ptr<NSP> secure_partition;
 | 
				
			||||||
 | 
					    std::shared_ptr<NCA> program;
 | 
				
			||||||
    std::vector<std::shared_ptr<NCA>> ncas;
 | 
					    std::vector<std::shared_ptr<NCA>> ncas;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
} // namespace FileSys
 | 
					} // namespace FileSys
 | 
				
			||||||
 | 
				
			|||||||
@ -21,7 +21,17 @@ NACP::NACP(VirtualFile file) : raw(std::make_unique<RawNACP>()) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const LanguageEntry& NACP::GetLanguageEntry(Language language) const {
 | 
					const LanguageEntry& NACP::GetLanguageEntry(Language language) const {
 | 
				
			||||||
    return raw->language_entries.at(static_cast<u8>(language));
 | 
					    if (language != Language::Default) {
 | 
				
			||||||
 | 
					        return raw->language_entries.at(static_cast<u8>(language));
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        for (const auto& language_entry : raw->language_entries) {
 | 
				
			||||||
 | 
					            if (!language_entry.GetApplicationName().empty())
 | 
				
			||||||
 | 
					                return language_entry;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Fallback to English
 | 
				
			||||||
 | 
					        return GetLanguageEntry(Language::AmericanEnglish);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::string NACP::GetApplicationName(Language language) const {
 | 
					std::string NACP::GetApplicationName(Language language) const {
 | 
				
			||||||
 | 
				
			|||||||
@ -9,6 +9,7 @@
 | 
				
			|||||||
#include <string>
 | 
					#include <string>
 | 
				
			||||||
#include "common/common_funcs.h"
 | 
					#include "common/common_funcs.h"
 | 
				
			||||||
#include "common/common_types.h"
 | 
					#include "common/common_types.h"
 | 
				
			||||||
 | 
					#include "common/swap.h"
 | 
				
			||||||
#include "core/file_sys/vfs.h"
 | 
					#include "core/file_sys/vfs.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace FileSys {
 | 
					namespace FileSys {
 | 
				
			||||||
@ -61,6 +62,8 @@ enum class Language : u8 {
 | 
				
			|||||||
    Korean = 12,
 | 
					    Korean = 12,
 | 
				
			||||||
    Taiwanese = 13,
 | 
					    Taiwanese = 13,
 | 
				
			||||||
    Chinese = 14,
 | 
					    Chinese = 14,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Default = 255,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static constexpr std::array<const char*, 15> LANGUAGE_NAMES = {
 | 
					static constexpr std::array<const char*, 15> LANGUAGE_NAMES = {
 | 
				
			||||||
@ -75,9 +78,9 @@ static constexpr std::array<const char*, 15> LANGUAGE_NAMES = {
 | 
				
			|||||||
class NACP {
 | 
					class NACP {
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
    explicit NACP(VirtualFile file);
 | 
					    explicit NACP(VirtualFile file);
 | 
				
			||||||
    const LanguageEntry& GetLanguageEntry(Language language = Language::AmericanEnglish) const;
 | 
					    const LanguageEntry& GetLanguageEntry(Language language = Language::Default) const;
 | 
				
			||||||
    std::string GetApplicationName(Language language = Language::AmericanEnglish) const;
 | 
					    std::string GetApplicationName(Language language = Language::Default) const;
 | 
				
			||||||
    std::string GetDeveloperName(Language language = Language::AmericanEnglish) const;
 | 
					    std::string GetDeveloperName(Language language = Language::Default) const;
 | 
				
			||||||
    u64 GetTitleId() const;
 | 
					    u64 GetTitleId() const;
 | 
				
			||||||
    std::string GetVersionString() const;
 | 
					    std::string GetVersionString() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -13,6 +13,7 @@
 | 
				
			|||||||
#include "core/file_sys/content_archive.h"
 | 
					#include "core/file_sys/content_archive.h"
 | 
				
			||||||
#include "core/file_sys/nca_metadata.h"
 | 
					#include "core/file_sys/nca_metadata.h"
 | 
				
			||||||
#include "core/file_sys/registered_cache.h"
 | 
					#include "core/file_sys/registered_cache.h"
 | 
				
			||||||
 | 
					#include "core/file_sys/submission_package.h"
 | 
				
			||||||
#include "core/file_sys/vfs_concat.h"
 | 
					#include "core/file_sys/vfs_concat.h"
 | 
				
			||||||
#include "core/loader/loader.h"
 | 
					#include "core/loader/loader.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -358,17 +359,21 @@ std::vector<RegisteredCacheEntry> RegisteredCache::ListEntriesFilter(
 | 
				
			|||||||
    return out;
 | 
					    return out;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static std::shared_ptr<NCA> GetNCAFromXCIForID(std::shared_ptr<XCI> xci, const NcaID& id) {
 | 
					static std::shared_ptr<NCA> GetNCAFromNSPForID(std::shared_ptr<NSP> nsp, const NcaID& id) {
 | 
				
			||||||
    const auto filename = fmt::format("{}.nca", Common::HexArrayToString(id, false));
 | 
					    const auto file = nsp->GetFile(fmt::format("{}.nca", Common::HexArrayToString(id, false)));
 | 
				
			||||||
    const auto iter =
 | 
					    if (file == nullptr)
 | 
				
			||||||
        std::find_if(xci->GetNCAs().begin(), xci->GetNCAs().end(),
 | 
					        return nullptr;
 | 
				
			||||||
                     [&filename](std::shared_ptr<NCA> nca) { return nca->GetName() == filename; });
 | 
					    return std::make_shared<NCA>(file);
 | 
				
			||||||
    return iter == xci->GetNCAs().end() ? nullptr : *iter;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
InstallResult RegisteredCache::InstallEntry(std::shared_ptr<XCI> xci, bool overwrite_if_exists,
 | 
					InstallResult RegisteredCache::InstallEntry(std::shared_ptr<XCI> xci, bool overwrite_if_exists,
 | 
				
			||||||
                                            const VfsCopyFunction& copy) {
 | 
					                                            const VfsCopyFunction& copy) {
 | 
				
			||||||
    const auto& ncas = xci->GetNCAs();
 | 
					    return InstallEntry(xci->GetSecurePartitionNSP(), overwrite_if_exists, copy);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					InstallResult RegisteredCache::InstallEntry(std::shared_ptr<NSP> nsp, bool overwrite_if_exists,
 | 
				
			||||||
 | 
					                                            const VfsCopyFunction& copy) {
 | 
				
			||||||
 | 
					    const auto& ncas = nsp->GetNCAsCollapsed();
 | 
				
			||||||
    const auto& meta_iter = std::find_if(ncas.begin(), ncas.end(), [](std::shared_ptr<NCA> nca) {
 | 
					    const auto& meta_iter = std::find_if(ncas.begin(), ncas.end(), [](std::shared_ptr<NCA> nca) {
 | 
				
			||||||
        return nca->GetType() == NCAContentType::Meta;
 | 
					        return nca->GetType() == NCAContentType::Meta;
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
@ -392,7 +397,7 @@ InstallResult RegisteredCache::InstallEntry(std::shared_ptr<XCI> xci, bool overw
 | 
				
			|||||||
    const auto cnmt_file = section0->GetFiles()[0];
 | 
					    const auto cnmt_file = section0->GetFiles()[0];
 | 
				
			||||||
    const CNMT cnmt(cnmt_file);
 | 
					    const CNMT cnmt(cnmt_file);
 | 
				
			||||||
    for (const auto& record : cnmt.GetContentRecords()) {
 | 
					    for (const auto& record : cnmt.GetContentRecords()) {
 | 
				
			||||||
        const auto nca = GetNCAFromXCIForID(xci, record.nca_id);
 | 
					        const auto nca = GetNCAFromNSPForID(nsp, record.nca_id);
 | 
				
			||||||
        if (nca == nullptr)
 | 
					        if (nca == nullptr)
 | 
				
			||||||
            return InstallResult::ErrorCopyFailed;
 | 
					            return InstallResult::ErrorCopyFailed;
 | 
				
			||||||
        const auto res2 = RawInstallNCA(nca, copy, overwrite_if_exists, record.nca_id);
 | 
					        const auto res2 = RawInstallNCA(nca, copy, overwrite_if_exists, record.nca_id);
 | 
				
			||||||
 | 
				
			|||||||
@ -17,6 +17,7 @@
 | 
				
			|||||||
namespace FileSys {
 | 
					namespace FileSys {
 | 
				
			||||||
class CNMT;
 | 
					class CNMT;
 | 
				
			||||||
class NCA;
 | 
					class NCA;
 | 
				
			||||||
 | 
					class NSP;
 | 
				
			||||||
class XCI;
 | 
					class XCI;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum class ContentRecordType : u8;
 | 
					enum class ContentRecordType : u8;
 | 
				
			||||||
@ -89,10 +90,12 @@ public:
 | 
				
			|||||||
        boost::optional<ContentRecordType> record_type = boost::none,
 | 
					        boost::optional<ContentRecordType> record_type = boost::none,
 | 
				
			||||||
        boost::optional<u64> title_id = boost::none) const;
 | 
					        boost::optional<u64> title_id = boost::none) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Raw copies all the ncas from the xci to the csache. Does some quick checks to make sure there
 | 
					    // Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure
 | 
				
			||||||
    // is a meta NCA and all of them are accessible.
 | 
					    // there is a meta NCA and all of them are accessible.
 | 
				
			||||||
    InstallResult InstallEntry(std::shared_ptr<XCI> xci, bool overwrite_if_exists = false,
 | 
					    InstallResult InstallEntry(std::shared_ptr<XCI> xci, bool overwrite_if_exists = false,
 | 
				
			||||||
                               const VfsCopyFunction& copy = &VfsRawCopy);
 | 
					                               const VfsCopyFunction& copy = &VfsRawCopy);
 | 
				
			||||||
 | 
					    InstallResult InstallEntry(std::shared_ptr<NSP> nsp, bool overwrite_if_exists = false,
 | 
				
			||||||
 | 
					                               const VfsCopyFunction& copy = &VfsRawCopy);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Due to the fact that we must use Meta-type NCAs to determine the existance of files, this
 | 
					    // Due to the fact that we must use Meta-type NCAs to determine the existance of files, this
 | 
				
			||||||
    // poses quite a challenge. Instead of creating a new meta NCA for this file, yuzu will create a
 | 
					    // poses quite a challenge. Instead of creating a new meta NCA for this file, yuzu will create a
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										236
									
								
								src/core/file_sys/submission_package.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										236
									
								
								src/core/file_sys/submission_package.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,236 @@
 | 
				
			|||||||
 | 
					// Copyright 2018 yuzu emulator team
 | 
				
			||||||
 | 
					// Licensed under GPLv2 or any later version
 | 
				
			||||||
 | 
					// Refer to the license.txt file included.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <fmt/ostream.h>
 | 
				
			||||||
 | 
					#include "common/assert.h"
 | 
				
			||||||
 | 
					#include "common/hex_util.h"
 | 
				
			||||||
 | 
					#include "core/file_sys/content_archive.h"
 | 
				
			||||||
 | 
					#include "core/file_sys/nca_metadata.h"
 | 
				
			||||||
 | 
					#include "core/file_sys/partition_filesystem.h"
 | 
				
			||||||
 | 
					#include "core/file_sys/submission_package.h"
 | 
				
			||||||
 | 
					#include "core/loader/loader.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace FileSys {
 | 
				
			||||||
 | 
					NSP::NSP(VirtualFile file_)
 | 
				
			||||||
 | 
					    : file(std::move(file_)),
 | 
				
			||||||
 | 
					      pfs(std::make_shared<PartitionFilesystem>(file)), status{Loader::ResultStatus::Success} {
 | 
				
			||||||
 | 
					    if (pfs->GetStatus() != Loader::ResultStatus::Success) {
 | 
				
			||||||
 | 
					        status = pfs->GetStatus();
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (IsDirectoryExeFS(pfs)) {
 | 
				
			||||||
 | 
					        extracted = true;
 | 
				
			||||||
 | 
					        exefs = pfs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const auto& files = pfs->GetFiles();
 | 
				
			||||||
 | 
					        const auto romfs_iter =
 | 
				
			||||||
 | 
					            std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& file) {
 | 
				
			||||||
 | 
					                return file->GetName().find(".romfs") != std::string::npos;
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        if (romfs_iter != files.end())
 | 
				
			||||||
 | 
					            romfs = *romfs_iter;
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    extracted = false;
 | 
				
			||||||
 | 
					    const auto files = pfs->GetFiles();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Core::Crypto::KeyManager keys;
 | 
				
			||||||
 | 
					    for (const auto& ticket_file : files) {
 | 
				
			||||||
 | 
					        if (ticket_file->GetExtension() == "tik") {
 | 
				
			||||||
 | 
					            if (ticket_file == nullptr ||
 | 
				
			||||||
 | 
					                ticket_file->GetSize() <
 | 
				
			||||||
 | 
					                    Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET + sizeof(Core::Crypto::Key128)) {
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Core::Crypto::Key128 key{};
 | 
				
			||||||
 | 
					            ticket_file->Read(key.data(), key.size(), Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET);
 | 
				
			||||||
 | 
					            std::string_view name_only(ticket_file->GetName());
 | 
				
			||||||
 | 
					            name_only.remove_suffix(4);
 | 
				
			||||||
 | 
					            const auto rights_id_raw = Common::HexStringToArray<16>(name_only);
 | 
				
			||||||
 | 
					            u128 rights_id;
 | 
				
			||||||
 | 
					            std::memcpy(rights_id.data(), rights_id_raw.data(), sizeof(u128));
 | 
				
			||||||
 | 
					            keys.SetKey(Core::Crypto::S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (const auto& outer_file : files) {
 | 
				
			||||||
 | 
					        if (outer_file->GetName().substr(outer_file->GetName().size() - 9) == ".cnmt.nca") {
 | 
				
			||||||
 | 
					            const auto nca = std::make_shared<NCA>(outer_file);
 | 
				
			||||||
 | 
					            if (nca->GetStatus() != Loader::ResultStatus::Success)
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            const auto section0 = nca->GetSubdirectories()[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for (const auto& inner_file : section0->GetFiles()) {
 | 
				
			||||||
 | 
					                if (inner_file->GetExtension() != "cnmt")
 | 
				
			||||||
 | 
					                    continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                const CNMT cnmt(inner_file);
 | 
				
			||||||
 | 
					                auto& ncas_title = ncas[cnmt.GetTitleID()];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                ncas_title[ContentRecordType::Meta] = nca;
 | 
				
			||||||
 | 
					                for (const auto& rec : cnmt.GetContentRecords()) {
 | 
				
			||||||
 | 
					                    const auto id_string = Common::HexArrayToString(rec.nca_id, false);
 | 
				
			||||||
 | 
					                    const auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string));
 | 
				
			||||||
 | 
					                    if (next_file == nullptr) {
 | 
				
			||||||
 | 
					                        LOG_WARNING(Service_FS,
 | 
				
			||||||
 | 
					                                    "NCA with ID {}.nca is listed in content metadata, but cannot "
 | 
				
			||||||
 | 
					                                    "be found in PFS. NSP appears to be corrupted.",
 | 
				
			||||||
 | 
					                                    id_string);
 | 
				
			||||||
 | 
					                        continue;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    auto next_nca = std::make_shared<NCA>(next_file);
 | 
				
			||||||
 | 
					                    if (next_nca->GetType() == NCAContentType::Program)
 | 
				
			||||||
 | 
					                        program_status[cnmt.GetTitleID()] = next_nca->GetStatus();
 | 
				
			||||||
 | 
					                    if (next_nca->GetStatus() == Loader::ResultStatus::Success)
 | 
				
			||||||
 | 
					                        ncas_title[rec.type] = std::move(next_nca);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					NSP::~NSP() = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Loader::ResultStatus NSP::GetStatus() const {
 | 
				
			||||||
 | 
					    return status;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Loader::ResultStatus NSP::GetProgramStatus(u64 title_id) const {
 | 
				
			||||||
 | 
					    const auto iter = program_status.find(title_id);
 | 
				
			||||||
 | 
					    if (iter == program_status.end())
 | 
				
			||||||
 | 
					        return Loader::ResultStatus::ErrorNSPMissingProgramNCA;
 | 
				
			||||||
 | 
					    return iter->second;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					u64 NSP::GetFirstTitleID() const {
 | 
				
			||||||
 | 
					    if (program_status.empty())
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					    return program_status.begin()->first;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					u64 NSP::GetProgramTitleID() const {
 | 
				
			||||||
 | 
					    const auto out = GetFirstTitleID();
 | 
				
			||||||
 | 
					    if ((out & 0x800) == 0)
 | 
				
			||||||
 | 
					        return out;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const auto ids = GetTitleIDs();
 | 
				
			||||||
 | 
					    const auto iter =
 | 
				
			||||||
 | 
					        std::find_if(ids.begin(), ids.end(), [](u64 tid) { return (tid & 0x800) == 0; });
 | 
				
			||||||
 | 
					    return iter == ids.end() ? out : *iter;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::vector<u64> NSP::GetTitleIDs() const {
 | 
				
			||||||
 | 
					    std::vector<u64> out;
 | 
				
			||||||
 | 
					    out.reserve(ncas.size());
 | 
				
			||||||
 | 
					    for (const auto& kv : ncas)
 | 
				
			||||||
 | 
					        out.push_back(kv.first);
 | 
				
			||||||
 | 
					    return out;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool NSP::IsExtractedType() const {
 | 
				
			||||||
 | 
					    return extracted;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					VirtualFile NSP::GetRomFS() const {
 | 
				
			||||||
 | 
					    return romfs;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					VirtualDir NSP::GetExeFS() const {
 | 
				
			||||||
 | 
					    return exefs;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::vector<std::shared_ptr<NCA>> NSP::GetNCAsCollapsed() const {
 | 
				
			||||||
 | 
					    if (extracted)
 | 
				
			||||||
 | 
					        LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
 | 
				
			||||||
 | 
					    std::vector<std::shared_ptr<NCA>> out;
 | 
				
			||||||
 | 
					    for (const auto& map : ncas) {
 | 
				
			||||||
 | 
					        for (const auto& inner_map : map.second)
 | 
				
			||||||
 | 
					            out.push_back(inner_map.second);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return out;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::multimap<u64, std::shared_ptr<NCA>> NSP::GetNCAsByTitleID() const {
 | 
				
			||||||
 | 
					    if (extracted)
 | 
				
			||||||
 | 
					        LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
 | 
				
			||||||
 | 
					    std::multimap<u64, std::shared_ptr<NCA>> out;
 | 
				
			||||||
 | 
					    for (const auto& map : ncas) {
 | 
				
			||||||
 | 
					        for (const auto& inner_map : map.second)
 | 
				
			||||||
 | 
					            out.emplace(map.first, inner_map.second);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return out;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> NSP::GetNCAs() const {
 | 
				
			||||||
 | 
					    return ncas;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type) const {
 | 
				
			||||||
 | 
					    if (extracted)
 | 
				
			||||||
 | 
					        LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const auto title_id_iter = ncas.find(title_id);
 | 
				
			||||||
 | 
					    if (title_id_iter == ncas.end())
 | 
				
			||||||
 | 
					        return nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const auto type_iter = title_id_iter->second.find(type);
 | 
				
			||||||
 | 
					    if (type_iter == title_id_iter->second.end())
 | 
				
			||||||
 | 
					        return nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return type_iter->second;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					VirtualFile NSP::GetNCAFile(u64 title_id, ContentRecordType type) const {
 | 
				
			||||||
 | 
					    if (extracted)
 | 
				
			||||||
 | 
					        LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
 | 
				
			||||||
 | 
					    const auto nca = GetNCA(title_id, type);
 | 
				
			||||||
 | 
					    if (nca != nullptr)
 | 
				
			||||||
 | 
					        return nca->GetBaseFile();
 | 
				
			||||||
 | 
					    return nullptr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::vector<Core::Crypto::Key128> NSP::GetTitlekey() const {
 | 
				
			||||||
 | 
					    if (extracted)
 | 
				
			||||||
 | 
					        LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
 | 
				
			||||||
 | 
					    std::vector<Core::Crypto::Key128> out;
 | 
				
			||||||
 | 
					    for (const auto& ticket_file : ticket_files) {
 | 
				
			||||||
 | 
					        if (ticket_file == nullptr ||
 | 
				
			||||||
 | 
					            ticket_file->GetSize() <
 | 
				
			||||||
 | 
					                Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET + sizeof(Core::Crypto::Key128)) {
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        out.emplace_back();
 | 
				
			||||||
 | 
					        ticket_file->Read(out.back().data(), out.back().size(),
 | 
				
			||||||
 | 
					                          Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return out;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::vector<VirtualFile> NSP::GetFiles() const {
 | 
				
			||||||
 | 
					    return pfs->GetFiles();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::vector<VirtualDir> NSP::GetSubdirectories() const {
 | 
				
			||||||
 | 
					    return pfs->GetSubdirectories();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::string NSP::GetName() const {
 | 
				
			||||||
 | 
					    return file->GetName();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					VirtualDir NSP::GetParentDirectory() const {
 | 
				
			||||||
 | 
					    return file->GetContainingDirectory();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool NSP::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					} // namespace FileSys
 | 
				
			||||||
							
								
								
									
										73
									
								
								src/core/file_sys/submission_package.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								src/core/file_sys/submission_package.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,73 @@
 | 
				
			|||||||
 | 
					// Copyright 2018 yuzu emulator team
 | 
				
			||||||
 | 
					// Licensed under GPLv2 or any later version
 | 
				
			||||||
 | 
					// Refer to the license.txt file included.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <array>
 | 
				
			||||||
 | 
					#include <map>
 | 
				
			||||||
 | 
					#include <vector>
 | 
				
			||||||
 | 
					#include "common/common_types.h"
 | 
				
			||||||
 | 
					#include "common/swap.h"
 | 
				
			||||||
 | 
					#include "core/file_sys/content_archive.h"
 | 
				
			||||||
 | 
					#include "core/file_sys/romfs_factory.h"
 | 
				
			||||||
 | 
					#include "core/file_sys/vfs.h"
 | 
				
			||||||
 | 
					#include "core/loader/loader.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace FileSys {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PartitionFilesystem;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class NSP : public ReadOnlyVfsDirectory {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    explicit NSP(VirtualFile file);
 | 
				
			||||||
 | 
					    ~NSP();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Loader::ResultStatus GetStatus() const;
 | 
				
			||||||
 | 
					    Loader::ResultStatus GetProgramStatus(u64 title_id) const;
 | 
				
			||||||
 | 
					    // Should only be used when one title id can be assured.
 | 
				
			||||||
 | 
					    u64 GetFirstTitleID() const;
 | 
				
			||||||
 | 
					    u64 GetProgramTitleID() const;
 | 
				
			||||||
 | 
					    std::vector<u64> GetTitleIDs() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool IsExtractedType() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Common (Can be safely called on both types)
 | 
				
			||||||
 | 
					    VirtualFile GetRomFS() const;
 | 
				
			||||||
 | 
					    VirtualDir GetExeFS() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Type 0 Only (Collection of NCAs + Certificate + Ticket + Meta XML)
 | 
				
			||||||
 | 
					    std::vector<std::shared_ptr<NCA>> GetNCAsCollapsed() const;
 | 
				
			||||||
 | 
					    std::multimap<u64, std::shared_ptr<NCA>> GetNCAsByTitleID() const;
 | 
				
			||||||
 | 
					    std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> GetNCAs() const;
 | 
				
			||||||
 | 
					    std::shared_ptr<NCA> GetNCA(u64 title_id, ContentRecordType type) const;
 | 
				
			||||||
 | 
					    VirtualFile GetNCAFile(u64 title_id, ContentRecordType type) const;
 | 
				
			||||||
 | 
					    std::vector<Core::Crypto::Key128> GetTitlekey() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::vector<VirtualFile> GetFiles() const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::vector<VirtualDir> GetSubdirectories() const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::string GetName() const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    VirtualDir GetParentDirectory() const override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
					    bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    VirtualFile file;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool extracted;
 | 
				
			||||||
 | 
					    Loader::ResultStatus status;
 | 
				
			||||||
 | 
					    std::map<u64, Loader::ResultStatus> program_status;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::shared_ptr<PartitionFilesystem> pfs;
 | 
				
			||||||
 | 
					    // Map title id -> {map type -> NCA}
 | 
				
			||||||
 | 
					    std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> ncas;
 | 
				
			||||||
 | 
					    std::vector<VirtualFile> ticket_files;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    VirtualFile romfs;
 | 
				
			||||||
 | 
					    VirtualDir exefs;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					} // namespace FileSys
 | 
				
			||||||
@ -61,7 +61,6 @@ AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    if (nacp_file != nullptr) {
 | 
					    if (nacp_file != nullptr) {
 | 
				
			||||||
        FileSys::NACP nacp(nacp_file);
 | 
					        FileSys::NACP nacp(nacp_file);
 | 
				
			||||||
        title_id = nacp.GetTitleId();
 | 
					 | 
				
			||||||
        name = nacp.GetApplicationName();
 | 
					        name = nacp.GetApplicationName();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -120,6 +119,7 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    auto& kernel = Core::System::GetInstance().Kernel();
 | 
					    auto& kernel = Core::System::GetInstance().Kernel();
 | 
				
			||||||
 | 
					    title_id = metadata.GetTitleID();
 | 
				
			||||||
    process->program_id = metadata.GetTitleID();
 | 
					    process->program_id = metadata.GetTitleID();
 | 
				
			||||||
    process->svc_access_mask.set();
 | 
					    process->svc_access_mask.set();
 | 
				
			||||||
    process->resource_limit =
 | 
					    process->resource_limit =
 | 
				
			||||||
@ -159,8 +159,6 @@ ResultStatus AppLoader_DeconstructedRomDirectory::ReadIcon(std::vector<u8>& buff
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ResultStatus AppLoader_DeconstructedRomDirectory::ReadProgramId(u64& out_program_id) {
 | 
					ResultStatus AppLoader_DeconstructedRomDirectory::ReadProgramId(u64& out_program_id) {
 | 
				
			||||||
    if (name.empty())
 | 
					 | 
				
			||||||
        return ResultStatus::ErrorNoControl;
 | 
					 | 
				
			||||||
    out_program_id = title_id;
 | 
					    out_program_id = title_id;
 | 
				
			||||||
    return ResultStatus::Success;
 | 
					    return ResultStatus::Success;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -15,6 +15,7 @@
 | 
				
			|||||||
#include "core/loader/nca.h"
 | 
					#include "core/loader/nca.h"
 | 
				
			||||||
#include "core/loader/nro.h"
 | 
					#include "core/loader/nro.h"
 | 
				
			||||||
#include "core/loader/nso.h"
 | 
					#include "core/loader/nso.h"
 | 
				
			||||||
 | 
					#include "core/loader/nsp.h"
 | 
				
			||||||
#include "core/loader/xci.h"
 | 
					#include "core/loader/xci.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Loader {
 | 
					namespace Loader {
 | 
				
			||||||
@ -34,6 +35,7 @@ FileType IdentifyFile(FileSys::VirtualFile file) {
 | 
				
			|||||||
    CHECK_TYPE(NCA)
 | 
					    CHECK_TYPE(NCA)
 | 
				
			||||||
    CHECK_TYPE(XCI)
 | 
					    CHECK_TYPE(XCI)
 | 
				
			||||||
    CHECK_TYPE(NAX)
 | 
					    CHECK_TYPE(NAX)
 | 
				
			||||||
 | 
					    CHECK_TYPE(NSP)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#undef CHECK_TYPE
 | 
					#undef CHECK_TYPE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -59,6 +61,8 @@ FileType GuessFromFilename(const std::string& name) {
 | 
				
			|||||||
        return FileType::NCA;
 | 
					        return FileType::NCA;
 | 
				
			||||||
    if (extension == "xci")
 | 
					    if (extension == "xci")
 | 
				
			||||||
        return FileType::XCI;
 | 
					        return FileType::XCI;
 | 
				
			||||||
 | 
					    if (extension == "nsp")
 | 
				
			||||||
 | 
					        return FileType::NSP;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return FileType::Unknown;
 | 
					    return FileType::Unknown;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -77,6 +81,8 @@ std::string GetFileTypeString(FileType type) {
 | 
				
			|||||||
        return "XCI";
 | 
					        return "XCI";
 | 
				
			||||||
    case FileType::NAX:
 | 
					    case FileType::NAX:
 | 
				
			||||||
        return "NAX";
 | 
					        return "NAX";
 | 
				
			||||||
 | 
					    case FileType::NSP:
 | 
				
			||||||
 | 
					        return "NSP";
 | 
				
			||||||
    case FileType::DeconstructedRomDirectory:
 | 
					    case FileType::DeconstructedRomDirectory:
 | 
				
			||||||
        return "Directory";
 | 
					        return "Directory";
 | 
				
			||||||
    case FileType::Error:
 | 
					    case FileType::Error:
 | 
				
			||||||
@ -87,7 +93,7 @@ std::string GetFileTypeString(FileType type) {
 | 
				
			|||||||
    return "unknown";
 | 
					    return "unknown";
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
constexpr std::array<const char*, 49> RESULT_MESSAGES{
 | 
					constexpr std::array<const char*, 50> RESULT_MESSAGES{
 | 
				
			||||||
    "The operation completed successfully.",
 | 
					    "The operation completed successfully.",
 | 
				
			||||||
    "The loader requested to load is already loaded.",
 | 
					    "The loader requested to load is already loaded.",
 | 
				
			||||||
    "The operation is not implemented.",
 | 
					    "The operation is not implemented.",
 | 
				
			||||||
@ -137,7 +143,7 @@ constexpr std::array<const char*, 49> RESULT_MESSAGES{
 | 
				
			|||||||
    "The AES Key Generation Source could not be found.",
 | 
					    "The AES Key Generation Source could not be found.",
 | 
				
			||||||
    "The SD Save Key Source could not be found.",
 | 
					    "The SD Save Key Source could not be found.",
 | 
				
			||||||
    "The SD NCA Key Source could not be found.",
 | 
					    "The SD NCA Key Source could not be found.",
 | 
				
			||||||
};
 | 
					    "The NSP file is missing a Program-type NCA."};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::ostream& operator<<(std::ostream& os, ResultStatus status) {
 | 
					std::ostream& operator<<(std::ostream& os, ResultStatus status) {
 | 
				
			||||||
    os << RESULT_MESSAGES.at(static_cast<size_t>(status));
 | 
					    os << RESULT_MESSAGES.at(static_cast<size_t>(status));
 | 
				
			||||||
@ -182,6 +188,10 @@ static std::unique_ptr<AppLoader> GetFileLoader(FileSys::VirtualFile file, FileT
 | 
				
			|||||||
    case FileType::NAX:
 | 
					    case FileType::NAX:
 | 
				
			||||||
        return std::make_unique<AppLoader_NAX>(std::move(file));
 | 
					        return std::make_unique<AppLoader_NAX>(std::move(file));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // NX NSP (Nintendo Submission Package) file format
 | 
				
			||||||
 | 
					    case FileType::NSP:
 | 
				
			||||||
 | 
					        return std::make_unique<AppLoader_NSP>(std::move(file));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // NX deconstructed ROM directory.
 | 
					    // NX deconstructed ROM directory.
 | 
				
			||||||
    case FileType::DeconstructedRomDirectory:
 | 
					    case FileType::DeconstructedRomDirectory:
 | 
				
			||||||
        return std::make_unique<AppLoader_DeconstructedRomDirectory>(std::move(file));
 | 
					        return std::make_unique<AppLoader_DeconstructedRomDirectory>(std::move(file));
 | 
				
			||||||
 | 
				
			|||||||
@ -29,6 +29,7 @@ enum class FileType {
 | 
				
			|||||||
    NSO,
 | 
					    NSO,
 | 
				
			||||||
    NRO,
 | 
					    NRO,
 | 
				
			||||||
    NCA,
 | 
					    NCA,
 | 
				
			||||||
 | 
					    NSP,
 | 
				
			||||||
    XCI,
 | 
					    XCI,
 | 
				
			||||||
    NAX,
 | 
					    NAX,
 | 
				
			||||||
    DeconstructedRomDirectory,
 | 
					    DeconstructedRomDirectory,
 | 
				
			||||||
@ -105,6 +106,7 @@ enum class ResultStatus : u16 {
 | 
				
			|||||||
    ErrorMissingAESKeyGenerationSource,
 | 
					    ErrorMissingAESKeyGenerationSource,
 | 
				
			||||||
    ErrorMissingSDSaveKeySource,
 | 
					    ErrorMissingSDSaveKeySource,
 | 
				
			||||||
    ErrorMissingSDNCAKeySource,
 | 
					    ErrorMissingSDNCAKeySource,
 | 
				
			||||||
 | 
					    ErrorNSPMissingProgramNCA,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
std::ostream& operator<<(std::ostream& os, ResultStatus status);
 | 
					std::ostream& operator<<(std::ostream& os, ResultStatus status);
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										135
									
								
								src/core/loader/nsp.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								src/core/loader/nsp.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,135 @@
 | 
				
			|||||||
 | 
					// Copyright 2018 yuzu emulator team
 | 
				
			||||||
 | 
					// Licensed under GPLv2 or any later version
 | 
				
			||||||
 | 
					// Refer to the license.txt file included.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "common/common_types.h"
 | 
				
			||||||
 | 
					#include "core/file_sys/card_image.h"
 | 
				
			||||||
 | 
					#include "core/file_sys/content_archive.h"
 | 
				
			||||||
 | 
					#include "core/file_sys/control_metadata.h"
 | 
				
			||||||
 | 
					#include "core/file_sys/nca_metadata.h"
 | 
				
			||||||
 | 
					#include "core/file_sys/romfs.h"
 | 
				
			||||||
 | 
					#include "core/file_sys/submission_package.h"
 | 
				
			||||||
 | 
					#include "core/hle/kernel/process.h"
 | 
				
			||||||
 | 
					#include "core/loader/deconstructed_rom_directory.h"
 | 
				
			||||||
 | 
					#include "core/loader/nca.h"
 | 
				
			||||||
 | 
					#include "core/loader/nsp.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Loader {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file)
 | 
				
			||||||
 | 
					    : AppLoader(file), nsp(std::make_unique<FileSys::NSP>(file)),
 | 
				
			||||||
 | 
					      title_id(nsp->GetProgramTitleID()) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (nsp->GetStatus() != ResultStatus::Success)
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    if (nsp->IsExtractedType())
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const auto control_nca =
 | 
				
			||||||
 | 
					        nsp->GetNCA(nsp->GetFirstTitleID(), FileSys::ContentRecordType::Control);
 | 
				
			||||||
 | 
					    if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success)
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const auto romfs = FileSys::ExtractRomFS(control_nca->GetRomFS());
 | 
				
			||||||
 | 
					    if (romfs == nullptr)
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (const auto& language : FileSys::LANGUAGE_NAMES) {
 | 
				
			||||||
 | 
					        icon_file = romfs->GetFile("icon_" + std::string(language) + ".dat");
 | 
				
			||||||
 | 
					        if (icon_file != nullptr)
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const auto nacp_raw = romfs->GetFile("control.nacp");
 | 
				
			||||||
 | 
					    if (nacp_raw == nullptr)
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					    nacp_file = std::make_shared<FileSys::NACP>(nacp_raw);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					AppLoader_NSP::~AppLoader_NSP() = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FileType AppLoader_NSP::IdentifyType(const FileSys::VirtualFile& file) {
 | 
				
			||||||
 | 
					    FileSys::NSP nsp(file);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (nsp.GetStatus() == ResultStatus::Success) {
 | 
				
			||||||
 | 
					        // Extracted Type case
 | 
				
			||||||
 | 
					        if (nsp.IsExtractedType() && nsp.GetExeFS() != nullptr &&
 | 
				
			||||||
 | 
					            FileSys::IsDirectoryExeFS(nsp.GetExeFS()) && nsp.GetRomFS() != nullptr) {
 | 
				
			||||||
 | 
					            return FileType::NSP;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Non-Ectracted Type case
 | 
				
			||||||
 | 
					        if (!nsp.IsExtractedType() &&
 | 
				
			||||||
 | 
					            nsp.GetNCA(nsp.GetFirstTitleID(), FileSys::ContentRecordType::Program) != nullptr &&
 | 
				
			||||||
 | 
					            AppLoader_NCA::IdentifyType(nsp.GetNCAFile(
 | 
				
			||||||
 | 
					                nsp.GetFirstTitleID(), FileSys::ContentRecordType::Program)) == FileType::NCA) {
 | 
				
			||||||
 | 
					            return FileType::NSP;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return FileType::Error;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ResultStatus AppLoader_NSP::Load(Kernel::SharedPtr<Kernel::Process>& process) {
 | 
				
			||||||
 | 
					    if (is_loaded) {
 | 
				
			||||||
 | 
					        return ResultStatus::ErrorAlreadyLoaded;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (nsp->IsExtractedType()) {
 | 
				
			||||||
 | 
					        secondary_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(nsp->GetExeFS());
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        if (title_id == 0)
 | 
				
			||||||
 | 
					            return ResultStatus::ErrorNSPMissingProgramNCA;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        secondary_loader = std::make_unique<AppLoader_NCA>(
 | 
				
			||||||
 | 
					            nsp->GetNCAFile(title_id, FileSys::ContentRecordType::Program));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (nsp->GetStatus() != ResultStatus::Success)
 | 
				
			||||||
 | 
					            return nsp->GetStatus();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (nsp->GetProgramStatus(title_id) != ResultStatus::Success)
 | 
				
			||||||
 | 
					            return nsp->GetProgramStatus(title_id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (nsp->GetNCA(title_id, FileSys::ContentRecordType::Program) == nullptr) {
 | 
				
			||||||
 | 
					            if (!Core::Crypto::KeyManager::KeyFileExists(false))
 | 
				
			||||||
 | 
					                return ResultStatus::ErrorMissingProductionKeyFile;
 | 
				
			||||||
 | 
					            return ResultStatus::ErrorNSPMissingProgramNCA;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const auto result = secondary_loader->Load(process);
 | 
				
			||||||
 | 
					    if (result != ResultStatus::Success)
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    is_loaded = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return ResultStatus::Success;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ResultStatus AppLoader_NSP::ReadRomFS(FileSys::VirtualFile& dir) {
 | 
				
			||||||
 | 
					    return secondary_loader->ReadRomFS(dir);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ResultStatus AppLoader_NSP::ReadProgramId(u64& out_program_id) {
 | 
				
			||||||
 | 
					    if (title_id == 0)
 | 
				
			||||||
 | 
					        return ResultStatus::ErrorNotInitialized;
 | 
				
			||||||
 | 
					    out_program_id = title_id;
 | 
				
			||||||
 | 
					    return ResultStatus::Success;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ResultStatus AppLoader_NSP::ReadIcon(std::vector<u8>& buffer) {
 | 
				
			||||||
 | 
					    if (icon_file == nullptr)
 | 
				
			||||||
 | 
					        return ResultStatus::ErrorNoControl;
 | 
				
			||||||
 | 
					    buffer = icon_file->ReadAllBytes();
 | 
				
			||||||
 | 
					    return ResultStatus::Success;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ResultStatus AppLoader_NSP::ReadTitle(std::string& title) {
 | 
				
			||||||
 | 
					    if (nacp_file == nullptr)
 | 
				
			||||||
 | 
					        return ResultStatus::ErrorNoControl;
 | 
				
			||||||
 | 
					    title = nacp_file->GetApplicationName();
 | 
				
			||||||
 | 
					    return ResultStatus::Success;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					} // namespace Loader
 | 
				
			||||||
							
								
								
									
										54
									
								
								src/core/loader/nsp.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								src/core/loader/nsp.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,54 @@
 | 
				
			|||||||
 | 
					// 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/file_sys/vfs.h"
 | 
				
			||||||
 | 
					#include "core/loader/loader.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace FileSys {
 | 
				
			||||||
 | 
					class NACP;
 | 
				
			||||||
 | 
					class NSP;
 | 
				
			||||||
 | 
					} // namespace FileSys
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Loader {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AppLoader_NCA;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Loads an XCI file
 | 
				
			||||||
 | 
					class AppLoader_NSP final : public AppLoader {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    explicit AppLoader_NSP(FileSys::VirtualFile file);
 | 
				
			||||||
 | 
					    ~AppLoader_NSP() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the type of the file
 | 
				
			||||||
 | 
					     * @param file std::shared_ptr<VfsFile> open file
 | 
				
			||||||
 | 
					     * @return FileType found, or FileType::Error if this loader doesn't know it
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    static FileType IdentifyType(const FileSys::VirtualFile& file);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    FileType GetFileType() override {
 | 
				
			||||||
 | 
					        return IdentifyType(file);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
 | 
				
			||||||
 | 
					    ResultStatus ReadProgramId(u64& out_program_id) override;
 | 
				
			||||||
 | 
					    ResultStatus ReadIcon(std::vector<u8>& buffer) override;
 | 
				
			||||||
 | 
					    ResultStatus ReadTitle(std::string& title) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					    std::unique_ptr<FileSys::NSP> nsp;
 | 
				
			||||||
 | 
					    std::unique_ptr<AppLoader> secondary_loader;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    FileSys::VirtualFile icon_file;
 | 
				
			||||||
 | 
					    std::shared_ptr<FileSys::NACP> nacp_file;
 | 
				
			||||||
 | 
					    u64 title_id;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace Loader
 | 
				
			||||||
@ -17,8 +17,7 @@ namespace Loader {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file)
 | 
					AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file)
 | 
				
			||||||
    : AppLoader(file), xci(std::make_unique<FileSys::XCI>(file)),
 | 
					    : AppLoader(file), xci(std::make_unique<FileSys::XCI>(file)),
 | 
				
			||||||
      nca_loader(std::make_unique<AppLoader_NCA>(
 | 
					      nca_loader(std::make_unique<AppLoader_NCA>(xci->GetProgramNCAFile())) {
 | 
				
			||||||
          xci->GetNCAFileByType(FileSys::NCAContentType::Program))) {
 | 
					 | 
				
			||||||
    if (xci->GetStatus() != ResultStatus::Success)
 | 
					    if (xci->GetStatus() != ResultStatus::Success)
 | 
				
			||||||
        return;
 | 
					        return;
 | 
				
			||||||
    const auto control_nca = xci->GetNCAByType(FileSys::NCAContentType::Control);
 | 
					    const auto control_nca = xci->GetNCAByType(FileSys::NCAContentType::Control);
 | 
				
			||||||
@ -64,11 +63,11 @@ ResultStatus AppLoader_XCI::Load(Kernel::SharedPtr<Kernel::Process>& process) {
 | 
				
			|||||||
    if (xci->GetProgramNCAStatus() != ResultStatus::Success)
 | 
					    if (xci->GetProgramNCAStatus() != ResultStatus::Success)
 | 
				
			||||||
        return xci->GetProgramNCAStatus();
 | 
					        return xci->GetProgramNCAStatus();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const auto nca = xci->GetNCAFileByType(FileSys::NCAContentType::Program);
 | 
					    const auto nca = xci->GetProgramNCA();
 | 
				
			||||||
    if (nca == nullptr && !Core::Crypto::KeyManager::KeyFileExists(false))
 | 
					    if (nca == nullptr && !Core::Crypto::KeyManager::KeyFileExists(false))
 | 
				
			||||||
        return ResultStatus::ErrorMissingProductionKeyFile;
 | 
					        return ResultStatus::ErrorMissingProductionKeyFile;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    auto result = nca_loader->Load(process);
 | 
					    const auto result = nca_loader->Load(process);
 | 
				
			||||||
    if (result != ResultStatus::Success)
 | 
					    if (result != ResultStatus::Success)
 | 
				
			||||||
        return result;
 | 
					        return result;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -432,7 +432,7 @@ void GameList::LoadInterfaceLayout() {
 | 
				
			|||||||
    item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder());
 | 
					    item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder());
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci"};
 | 
					const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci", "nsp"};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static bool HasSupportedFileExtension(const std::string& file_name) {
 | 
					static bool HasSupportedFileExtension(const std::string& file_name) {
 | 
				
			||||||
    const QFileInfo file = QFileInfo(QString::fromStdString(file_name));
 | 
					    const QFileInfo file = QFileInfo(QString::fromStdString(file_name));
 | 
				
			||||||
 | 
				
			|||||||
@ -34,7 +34,9 @@
 | 
				
			|||||||
#include "core/file_sys/content_archive.h"
 | 
					#include "core/file_sys/content_archive.h"
 | 
				
			||||||
#include "core/file_sys/registered_cache.h"
 | 
					#include "core/file_sys/registered_cache.h"
 | 
				
			||||||
#include "core/file_sys/savedata_factory.h"
 | 
					#include "core/file_sys/savedata_factory.h"
 | 
				
			||||||
 | 
					#include "core/file_sys/submission_package.h"
 | 
				
			||||||
#include "core/file_sys/vfs_real.h"
 | 
					#include "core/file_sys/vfs_real.h"
 | 
				
			||||||
 | 
					#include "core/hle/kernel/process.h"
 | 
				
			||||||
#include "core/hle/service/filesystem/filesystem.h"
 | 
					#include "core/hle/service/filesystem/filesystem.h"
 | 
				
			||||||
#include "core/loader/loader.h"
 | 
					#include "core/loader/loader.h"
 | 
				
			||||||
#include "core/perf_stats.h"
 | 
					#include "core/perf_stats.h"
 | 
				
			||||||
@ -76,6 +78,7 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1;
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
enum class CalloutFlag : uint32_t {
 | 
					enum class CalloutFlag : uint32_t {
 | 
				
			||||||
    Telemetry = 0x1,
 | 
					    Telemetry = 0x1,
 | 
				
			||||||
 | 
					    DRDDeprecation = 0x2,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static void ShowCalloutMessage(const QString& message, CalloutFlag flag) {
 | 
					static void ShowCalloutMessage(const QString& message, CalloutFlag flag) {
 | 
				
			||||||
@ -488,6 +491,23 @@ bool GMainWindow::LoadROM(const QString& filename) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())};
 | 
					    const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const auto drd_callout =
 | 
				
			||||||
 | 
					        (UISettings::values.callout_flags & static_cast<u32>(CalloutFlag::DRDDeprecation)) == 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (result == Core::System::ResultStatus::Success &&
 | 
				
			||||||
 | 
					        system.GetAppLoader().GetFileType() == Loader::FileType::DeconstructedRomDirectory &&
 | 
				
			||||||
 | 
					        drd_callout) {
 | 
				
			||||||
 | 
					        UISettings::values.callout_flags |= static_cast<u32>(CalloutFlag::DRDDeprecation);
 | 
				
			||||||
 | 
					        QMessageBox::warning(
 | 
				
			||||||
 | 
					            this, tr("Warning Outdated Game Format"),
 | 
				
			||||||
 | 
					            tr("You are using the deconstructed ROM directory format for this game, which is an "
 | 
				
			||||||
 | 
					               "outdated format that has been superseded by others such as NCA, NAX, XCI, or "
 | 
				
			||||||
 | 
					               "NSP. Deconstructed ROM directories lack icons, metadata, and update "
 | 
				
			||||||
 | 
					               "support.<br><br>For an explanation of the various Switch formats yuzu supports, <a "
 | 
				
			||||||
 | 
					               "href='https://yuzu-emu.org/wiki/overview-of-switch-game-formats'>check out our "
 | 
				
			||||||
 | 
					               "wiki</a>. This message will not be shown again."));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    render_window->DoneCurrent();
 | 
					    render_window->DoneCurrent();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (result != Core::System::ResultStatus::Success) {
 | 
					    if (result != Core::System::ResultStatus::Success) {
 | 
				
			||||||
@ -746,7 +766,8 @@ void GMainWindow::OnMenuLoadFolder() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void GMainWindow::OnMenuInstallToNAND() {
 | 
					void GMainWindow::OnMenuInstallToNAND() {
 | 
				
			||||||
    const QString file_filter =
 | 
					    const QString file_filter =
 | 
				
			||||||
        tr("Installable Switch File (*.nca *.xci);;Nintendo Content Archive (*.nca);;NX Cartridge "
 | 
					        tr("Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive "
 | 
				
			||||||
 | 
					           "(*.nca);;Nintendo Submissions Package (*.nsp);;NX Cartridge "
 | 
				
			||||||
           "Image (*.xci)");
 | 
					           "Image (*.xci)");
 | 
				
			||||||
    QString filename = QFileDialog::getOpenFileName(this, tr("Install File"),
 | 
					    QString filename = QFileDialog::getOpenFileName(this, tr("Install File"),
 | 
				
			||||||
                                                    UISettings::values.roms_path, file_filter);
 | 
					                                                    UISettings::values.roms_path, file_filter);
 | 
				
			||||||
@ -806,22 +827,34 @@ void GMainWindow::OnMenuInstallToNAND() {
 | 
				
			|||||||
               QMessageBox::Yes;
 | 
					               QMessageBox::Yes;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (filename.endsWith("xci", Qt::CaseInsensitive)) {
 | 
					    if (filename.endsWith("xci", Qt::CaseInsensitive) ||
 | 
				
			||||||
        const auto xci = std::make_shared<FileSys::XCI>(
 | 
					        filename.endsWith("nsp", Qt::CaseInsensitive)) {
 | 
				
			||||||
            vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read));
 | 
					
 | 
				
			||||||
        if (xci->GetStatus() != Loader::ResultStatus::Success) {
 | 
					        std::shared_ptr<FileSys::NSP> nsp;
 | 
				
			||||||
 | 
					        if (filename.endsWith("nsp", Qt::CaseInsensitive)) {
 | 
				
			||||||
 | 
					            nsp = std::make_shared<FileSys::NSP>(
 | 
				
			||||||
 | 
					                vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read));
 | 
				
			||||||
 | 
					            if (nsp->IsExtractedType())
 | 
				
			||||||
 | 
					                failed();
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            const auto xci = std::make_shared<FileSys::XCI>(
 | 
				
			||||||
 | 
					                vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read));
 | 
				
			||||||
 | 
					            nsp = xci->GetSecurePartitionNSP();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (nsp->GetStatus() != Loader::ResultStatus::Success) {
 | 
				
			||||||
            failed();
 | 
					            failed();
 | 
				
			||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        const auto res =
 | 
					        const auto res =
 | 
				
			||||||
            Service::FileSystem::GetUserNANDContents()->InstallEntry(xci, false, qt_raw_copy);
 | 
					            Service::FileSystem::GetUserNANDContents()->InstallEntry(nsp, false, qt_raw_copy);
 | 
				
			||||||
        if (res == FileSys::InstallResult::Success) {
 | 
					        if (res == FileSys::InstallResult::Success) {
 | 
				
			||||||
            success();
 | 
					            success();
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            if (res == FileSys::InstallResult::ErrorAlreadyExists) {
 | 
					            if (res == FileSys::InstallResult::ErrorAlreadyExists) {
 | 
				
			||||||
                if (overwrite()) {
 | 
					                if (overwrite()) {
 | 
				
			||||||
                    const auto res2 = Service::FileSystem::GetUserNANDContents()->InstallEntry(
 | 
					                    const auto res2 = Service::FileSystem::GetUserNANDContents()->InstallEntry(
 | 
				
			||||||
                        xci, true, qt_raw_copy);
 | 
					                        nsp, true, qt_raw_copy);
 | 
				
			||||||
                    if (res2 == FileSys::InstallResult::Success) {
 | 
					                    if (res2 == FileSys::InstallResult::Success) {
 | 
				
			||||||
                        success();
 | 
					                        success();
 | 
				
			||||||
                    } else {
 | 
					                    } else {
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user