Merge pull request #1396 from DarkLordZach/packed-updates

loader: Add support for packed updates
master
bunnei 2018-10-06 23:58:24 +07:00 committed by GitHub
commit 6e4d2e672d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 143 additions and 21 deletions

@ -184,8 +184,8 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
romfs = std::move(packed); romfs = std::move(packed);
} }
VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, ContentRecordType type,
ContentRecordType type) const { VirtualFile update_raw) const {
LOG_INFO(Loader, "Patching RomFS for title_id={:016X}, type={:02X}", title_id, LOG_INFO(Loader, "Patching RomFS for title_id={:016X}, type={:02X}", title_id,
static_cast<u8>(type)); static_cast<u8>(type));
@ -205,6 +205,13 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset,
FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0))); FormatTitleVersion(installed->GetEntryVersion(update_tid).get_value_or(0)));
romfs = new_nca->GetRomFS(); romfs = new_nca->GetRomFS();
} }
} else if (update_raw != nullptr) {
const auto new_nca = std::make_shared<NCA>(update_raw, romfs, ivfc_offset);
if (new_nca->GetStatus() == Loader::ResultStatus::Success &&
new_nca->GetRomFS() != nullptr) {
LOG_INFO(Loader, " RomFS: Update (PACKED) applied successfully");
romfs = new_nca->GetRomFS();
}
} }
// LayeredFS // LayeredFS
@ -224,7 +231,8 @@ static bool IsDirValidAndNonEmpty(const VirtualDir& dir) {
return dir != nullptr && (!dir->GetFiles().empty() || !dir->GetSubdirectories().empty()); return dir != nullptr && (!dir->GetFiles().empty() || !dir->GetSubdirectories().empty());
} }
std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames() const { std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames(
VirtualFile update_raw) const {
std::map<std::string, std::string, std::less<>> out; std::map<std::string, std::string, std::less<>> out;
const auto installed = Service::FileSystem::GetUnionContents(); const auto installed = Service::FileSystem::GetUnionContents();
@ -245,6 +253,8 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
"Update", "Update",
FormatTitleVersion(meta_ver.get(), TitleVersionFormat::ThreeElements)); FormatTitleVersion(meta_ver.get(), TitleVersionFormat::ThreeElements));
} }
} else if (update_raw != nullptr) {
out.insert_or_assign("Update", "PACKED");
} }
} }

@ -46,11 +46,13 @@ public:
// - Game Updates // - Game Updates
// - LayeredFS // - LayeredFS
VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset, VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset,
ContentRecordType type = ContentRecordType::Program) const; ContentRecordType type = ContentRecordType::Program,
VirtualFile update_raw = nullptr) const;
// Returns a vector of pairs between patch names and patch versions. // Returns a vector of pairs between patch names and patch versions.
// i.e. Update 3.2.2 will return {"Update", "3.2.2"} // i.e. Update 3.2.2 will return {"Update", "3.2.2"}
std::map<std::string, std::string, std::less<>> GetPatchVersionNames() const; std::map<std::string, std::string, std::less<>> GetPatchVersionNames(
VirtualFile update_raw = nullptr) const;
// Given title_id of the program, attempts to get the control data of the update and parse it, // Given title_id of the program, attempts to get the control data of the update and parse it,
// falling back to the base control data. // falling back to the base control data.

@ -30,12 +30,17 @@ RomFSFactory::RomFSFactory(Loader::AppLoader& app_loader) {
RomFSFactory::~RomFSFactory() = default; RomFSFactory::~RomFSFactory() = default;
void RomFSFactory::SetPackedUpdate(VirtualFile update_raw) {
this->update_raw = std::move(update_raw);
}
ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() { ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() {
if (!updatable) if (!updatable)
return MakeResult<VirtualFile>(file); return MakeResult<VirtualFile>(file);
const PatchManager patch_manager(Core::CurrentProcess()->GetTitleID()); const PatchManager patch_manager(Core::CurrentProcess()->GetTitleID());
return MakeResult<VirtualFile>(patch_manager.PatchRomFS(file, ivfc_offset)); return MakeResult<VirtualFile>(
patch_manager.PatchRomFS(file, ivfc_offset, ContentRecordType::Program, update_raw));
} }
ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, ContentRecordType type) { ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, ContentRecordType type) {

@ -32,11 +32,13 @@ public:
explicit RomFSFactory(Loader::AppLoader& app_loader); explicit RomFSFactory(Loader::AppLoader& app_loader);
~RomFSFactory(); ~RomFSFactory();
void SetPackedUpdate(VirtualFile update_raw);
ResultVal<VirtualFile> OpenCurrentProcess(); ResultVal<VirtualFile> OpenCurrentProcess();
ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type); ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type);
private: private:
VirtualFile file; VirtualFile file;
VirtualFile update_raw;
bool updatable; bool updatable;
u64 ivfc_offset; u64 ivfc_offset;
}; };

@ -259,8 +259,11 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
auto next_nca = std::make_shared<NCA>(next_file); auto next_nca = std::make_shared<NCA>(next_file);
if (next_nca->GetType() == NCAContentType::Program) if (next_nca->GetType() == NCAContentType::Program)
program_status[cnmt.GetTitleID()] = next_nca->GetStatus(); program_status[cnmt.GetTitleID()] = next_nca->GetStatus();
if (next_nca->GetStatus() == Loader::ResultStatus::Success) if (next_nca->GetStatus() == Loader::ResultStatus::Success ||
(next_nca->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS &&
(cnmt.GetTitleID() & 0x800) != 0)) {
ncas_title[rec.type] = std::move(next_nca); ncas_title[rec.type] = std::move(next_nca);
}
} }
break; break;

@ -264,6 +264,15 @@ ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory) {
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
void SetPackedUpdate(FileSys::VirtualFile update_raw) {
LOG_TRACE(Service_FS, "Setting packed update for romfs");
if (romfs_factory == nullptr)
return;
romfs_factory->SetPackedUpdate(std::move(update_raw));
}
ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess() { ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess() {
LOG_TRACE(Service_FS, "Opening RomFS for current process"); LOG_TRACE(Service_FS, "Opening RomFS for current process");

@ -39,6 +39,7 @@ ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory)
ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory); ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory);
ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory); ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory);
void SetPackedUpdate(FileSys::VirtualFile update_raw);
ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess(); ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess();
ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id, ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
FileSys::ContentRecordType type); FileSys::ContentRecordType type);

@ -93,7 +93,7 @@ std::string GetFileTypeString(FileType type) {
return "unknown"; return "unknown";
} }
constexpr std::array<const char*, 58> RESULT_MESSAGES{ constexpr std::array<const char*, 59> 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.",
@ -152,6 +152,7 @@ constexpr std::array<const char*, 58> RESULT_MESSAGES{
"The BKTR-type NCA has a bad Relocation bucket.", "The BKTR-type NCA has a bad Relocation bucket.",
"The BKTR-type NCA has a bad Subsection bucket.", "The BKTR-type NCA has a bad Subsection bucket.",
"The BKTR-type NCA is missing the base RomFS.", "The BKTR-type NCA is missing the base RomFS.",
"The NSP or XCI does not contain an update in addition to the base game.",
}; };
std::ostream& operator<<(std::ostream& os, ResultStatus status) { std::ostream& operator<<(std::ostream& os, ResultStatus status) {

@ -114,6 +114,7 @@ enum class ResultStatus : u16 {
ErrorBadRelocationBuckets, ErrorBadRelocationBuckets,
ErrorBadSubsectionBuckets, ErrorBadSubsectionBuckets,
ErrorMissingBKTRBaseRomFS, ErrorMissingBKTRBaseRomFS,
ErrorNoPackedUpdate,
}; };
std::ostream& operator<<(std::ostream& os, ResultStatus status); std::ostream& operator<<(std::ostream& os, ResultStatus status);
@ -196,10 +197,19 @@ public:
/** /**
* Get the RomFS of the application * Get the RomFS of the application
* Since the RomFS can be huge, we return a file reference instead of copying to a buffer * Since the RomFS can be huge, we return a file reference instead of copying to a buffer
* @param dir The directory containing the RomFS * @param file The directory containing the RomFS
* @return ResultStatus result of function * @return ResultStatus result of function
*/ */
virtual ResultStatus ReadRomFS(FileSys::VirtualFile& dir) { virtual ResultStatus ReadRomFS(FileSys::VirtualFile& file) {
return ResultStatus::ErrorNotImplemented;
}
/**
* Get the raw update of the application, should it come packed with one
* @param file The raw update NCA file (Program-type
* @return ResultStatus result of function
*/
virtual ResultStatus ReadUpdateRaw(FileSys::VirtualFile& file) {
return ResultStatus::ErrorNotImplemented; return ResultStatus::ErrorNotImplemented;
} }

@ -72,6 +72,10 @@ ResultStatus AppLoader_NAX::ReadRomFS(FileSys::VirtualFile& dir) {
return nca_loader->ReadRomFS(dir); return nca_loader->ReadRomFS(dir);
} }
u64 AppLoader_NAX::ReadRomFSIVFCOffset() const {
return nca_loader->ReadRomFSIVFCOffset();
}
ResultStatus AppLoader_NAX::ReadProgramId(u64& out_program_id) { ResultStatus AppLoader_NAX::ReadProgramId(u64& out_program_id) {
return nca_loader->ReadProgramId(out_program_id); return nca_loader->ReadProgramId(out_program_id);
} }

@ -36,6 +36,7 @@ public:
ResultStatus Load(Kernel::Process& process) override; ResultStatus Load(Kernel::Process& process) override;
ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
u64 ReadRomFSIVFCOffset() const override;
ResultStatus ReadProgramId(u64& out_program_id) override; ResultStatus ReadProgramId(u64& out_program_id) override;
private: private:

@ -10,8 +10,10 @@
#include "core/file_sys/control_metadata.h" #include "core/file_sys/control_metadata.h"
#include "core/file_sys/nca_metadata.h" #include "core/file_sys/nca_metadata.h"
#include "core/file_sys/patch_manager.h" #include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/submission_package.h" #include "core/file_sys/submission_package.h"
#include "core/hle/kernel/process.h" #include "core/hle/kernel/process.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/deconstructed_rom_directory.h" #include "core/loader/deconstructed_rom_directory.h"
#include "core/loader/nca.h" #include "core/loader/nca.h"
#include "core/loader/nsp.h" #include "core/loader/nsp.h"
@ -91,13 +93,39 @@ ResultStatus AppLoader_NSP::Load(Kernel::Process& process) {
if (result != ResultStatus::Success) if (result != ResultStatus::Success)
return result; return result;
FileSys::VirtualFile update_raw;
if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr)
Service::FileSystem::SetPackedUpdate(std::move(update_raw));
is_loaded = true; is_loaded = true;
return ResultStatus::Success; return ResultStatus::Success;
} }
ResultStatus AppLoader_NSP::ReadRomFS(FileSys::VirtualFile& dir) { ResultStatus AppLoader_NSP::ReadRomFS(FileSys::VirtualFile& file) {
return secondary_loader->ReadRomFS(dir); return secondary_loader->ReadRomFS(file);
}
u64 AppLoader_NSP::ReadRomFSIVFCOffset() const {
return secondary_loader->ReadRomFSIVFCOffset();
}
ResultStatus AppLoader_NSP::ReadUpdateRaw(FileSys::VirtualFile& file) {
if (nsp->IsExtractedType())
return ResultStatus::ErrorNoPackedUpdate;
const auto read =
nsp->GetNCAFile(FileSys::GetUpdateTitleID(title_id), FileSys::ContentRecordType::Program);
if (read == nullptr)
return ResultStatus::ErrorNoPackedUpdate;
const auto nca_test = std::make_shared<FileSys::NCA>(read);
if (nca_test->GetStatus() != ResultStatus::ErrorMissingBKTRBaseRomFS)
return nca_test->GetStatus();
file = read;
return ResultStatus::Success;
} }
ResultStatus AppLoader_NSP::ReadProgramId(u64& out_program_id) { ResultStatus AppLoader_NSP::ReadProgramId(u64& out_program_id) {

@ -37,7 +37,9 @@ public:
ResultStatus Load(Kernel::Process& process) override; ResultStatus Load(Kernel::Process& process) override;
ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; ResultStatus ReadRomFS(FileSys::VirtualFile& file) override;
u64 ReadRomFSIVFCOffset() const override;
ResultStatus ReadUpdateRaw(FileSys::VirtualFile& file) override;
ResultStatus ReadProgramId(u64& out_program_id) override; ResultStatus ReadProgramId(u64& out_program_id) override;
ResultStatus ReadIcon(std::vector<u8>& buffer) override; ResultStatus ReadIcon(std::vector<u8>& buffer) override;
ResultStatus ReadTitle(std::string& title) override; ResultStatus ReadTitle(std::string& title) override;

@ -9,7 +9,11 @@
#include "core/file_sys/content_archive.h" #include "core/file_sys/content_archive.h"
#include "core/file_sys/control_metadata.h" #include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h" #include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs.h"
#include "core/file_sys/submission_package.h"
#include "core/hle/kernel/process.h" #include "core/hle/kernel/process.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/nca.h" #include "core/loader/nca.h"
#include "core/loader/xci.h" #include "core/loader/xci.h"
@ -63,13 +67,41 @@ ResultStatus AppLoader_XCI::Load(Kernel::Process& process) {
if (result != ResultStatus::Success) if (result != ResultStatus::Success)
return result; return result;
FileSys::VirtualFile update_raw;
if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr)
Service::FileSystem::SetPackedUpdate(std::move(update_raw));
is_loaded = true; is_loaded = true;
return ResultStatus::Success; return ResultStatus::Success;
} }
ResultStatus AppLoader_XCI::ReadRomFS(FileSys::VirtualFile& dir) { ResultStatus AppLoader_XCI::ReadRomFS(FileSys::VirtualFile& file) {
return nca_loader->ReadRomFS(dir); return nca_loader->ReadRomFS(file);
}
u64 AppLoader_XCI::ReadRomFSIVFCOffset() const {
return nca_loader->ReadRomFSIVFCOffset();
}
ResultStatus AppLoader_XCI::ReadUpdateRaw(FileSys::VirtualFile& file) {
u64 program_id{};
nca_loader->ReadProgramId(program_id);
if (program_id == 0)
return ResultStatus::ErrorXCIMissingProgramNCA;
const auto read = xci->GetSecurePartitionNSP()->GetNCAFile(
FileSys::GetUpdateTitleID(program_id), FileSys::ContentRecordType::Program);
if (read == nullptr)
return ResultStatus::ErrorNoPackedUpdate;
const auto nca_test = std::make_shared<FileSys::NCA>(read);
if (nca_test->GetStatus() != ResultStatus::ErrorMissingBKTRBaseRomFS)
return nca_test->GetStatus();
file = read;
return ResultStatus::Success;
} }
ResultStatus AppLoader_XCI::ReadProgramId(u64& out_program_id) { ResultStatus AppLoader_XCI::ReadProgramId(u64& out_program_id) {

@ -37,7 +37,9 @@ public:
ResultStatus Load(Kernel::Process& process) override; ResultStatus Load(Kernel::Process& process) override;
ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; ResultStatus ReadRomFS(FileSys::VirtualFile& file) override;
u64 ReadRomFSIVFCOffset() const override;
ResultStatus ReadUpdateRaw(FileSys::VirtualFile& file) override;
ResultStatus ReadProgramId(u64& out_program_id) override; ResultStatus ReadProgramId(u64& out_program_id) override;
ResultStatus ReadIcon(std::vector<u8>& buffer) override; ResultStatus ReadIcon(std::vector<u8>& buffer) override;
ResultStatus ReadTitle(std::string& title) override; ResultStatus ReadTitle(std::string& title) override;

@ -57,16 +57,25 @@ QString FormatGameName(const std::string& physical_name) {
return physical_name_as_qstring; return physical_name_as_qstring;
} }
QString FormatPatchNameVersions(const FileSys::PatchManager& patch_manager, bool updatable = true) { QString FormatPatchNameVersions(const FileSys::PatchManager& patch_manager,
Loader::AppLoader& loader, bool updatable = true) {
QString out; QString out;
for (const auto& kv : patch_manager.GetPatchVersionNames()) { FileSys::VirtualFile update_raw;
loader.ReadUpdateRaw(update_raw);
for (const auto& kv : patch_manager.GetPatchVersionNames(update_raw)) {
if (!updatable && kv.first == "Update") if (!updatable && kv.first == "Update")
continue; continue;
if (kv.second.empty()) { if (kv.second.empty()) {
out.append(fmt::format("{}\n", kv.first).c_str()); out.append(fmt::format("{}\n", kv.first).c_str());
} else { } else {
out.append(fmt::format("{} ({})\n", kv.first, kv.second).c_str()); auto ver = kv.second;
// Display container name for packed updates
if (ver == "PACKED" && kv.first == "Update")
ver = Loader::GetFileTypeString(loader.GetFileType());
out.append(fmt::format("{} ({})\n", kv.first, ver).c_str());
} }
} }
@ -116,7 +125,7 @@ void GameListWorker::AddInstalledTitlesToGameList() {
QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())), QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())),
program_id), program_id),
new GameListItemCompat(compatibility), new GameListItemCompat(compatibility),
new GameListItem(FormatPatchNameVersions(patch)), new GameListItem(FormatPatchNameVersions(patch, *loader)),
new GameListItem( new GameListItem(
QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
new GameListItemSize(file->GetSize()), new GameListItemSize(file->GetSize()),
@ -206,7 +215,8 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign
QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())), QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType())),
program_id), program_id),
new GameListItemCompat(compatibility), new GameListItemCompat(compatibility),
new GameListItem(FormatPatchNameVersions(patch, loader->IsRomFSUpdatable())), new GameListItem(
FormatPatchNameVersions(patch, *loader, loader->IsRomFSUpdatable())),
new GameListItem( new GameListItem(
QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))), QString::fromStdString(Loader::GetFileTypeString(loader->GetFileType()))),
new GameListItemSize(FileUtil::GetSize(physical_name)), new GameListItemSize(FileUtil::GetSize(physical_name)),