diff --git a/src/core/db/seed_db.cpp b/src/core/db/seed_db.cpp index cd177ae..3991314 100644 --- a/src/core/db/seed_db.cpp +++ b/src/core/db/seed_db.cpp @@ -2,17 +2,14 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -// Modified from Citra's implementation to allow multiple instances - -#include #include "common/file_util.h" #include "common/logging/log.h" +#include "common/swap.h" #include "core/db/seed_db.h" namespace Core { -bool SeedDB::Load(const std::string& path) { - seeds.clear(); +bool SeedDB::AddFromFile(const std::string& path) { if (!FileUtil::Exists(path)) { LOG_WARNING(Service_FS, "Seed database does not exist"); return true; @@ -32,20 +29,18 @@ bool SeedDB::Load(const std::string& path) { return false; } for (u32 i = 0; i < count; ++i) { - Seed seed; - if (!file.ReadBytes(&seed.title_id, sizeof(seed.title_id))) { + u64_le title_id; + if (!file.ReadBytes(&title_id, sizeof(title_id))) { LOG_ERROR(Service_FS, "Failed to read seed {} title ID", i); return false; } - if (!file.ReadBytes(seed.data.data(), seed.data.size())) { + Seed seed; + if (!file.ReadBytes(seed.data(), seed.size())) { LOG_ERROR(Service_FS, "Failed to read seed {} data", i); return false; } - if (!file.ReadBytes(seed.reserved.data(), seed.reserved.size())) { - LOG_ERROR(Service_FS, "Failed to read seed {} reserved data", i); - return false; - } - seeds.push_back(seed); + file.Seek(SEEDDB_ENTRY_PADDING_BYTES, SEEK_CUR); + seeds.emplace(title_id, std::move(seed)); } return true; } @@ -70,64 +65,20 @@ bool SeedDB::Save(const std::string& path) { LOG_ERROR(Service_FS, "Failed to write seed database padding fully"); return false; } - for (std::size_t i = 0; i < count; ++i) { - if (file.WriteBytes(&seeds[i].title_id, sizeof(seeds[i].title_id)) != - sizeof(seeds[i].title_id)) { - LOG_ERROR(Service_FS, "Failed to write seed {} title ID fully", i); + for (const auto& [title_id, seed] : seeds) { + const u64_le raw_title_id{title_id}; // for endianess + if (file.WriteBytes(&raw_title_id, sizeof(raw_title_id)) != sizeof(raw_title_id)) { + LOG_ERROR(Service_FS, "Failed to write seed {:016x} title ID fully", title_id); return false; } - if (file.WriteBytes(seeds[i].data.data(), seeds[i].data.size()) != seeds[i].data.size()) { - LOG_ERROR(Service_FS, "Failed to write seed {} data fully", i); - return false; - } - if (file.WriteBytes(seeds[i].reserved.data(), seeds[i].reserved.size()) != - seeds[i].reserved.size()) { - LOG_ERROR(Service_FS, "Failed to write seed {} reserved data fully", i); + + if (file.WriteBytes(seed.data(), seed.size()) != seed.size()) { + LOG_ERROR(Service_FS, "Failed to write seed {:016x} data fully", title_id); return false; } + file.Seek(SEEDDB_ENTRY_PADDING_BYTES, SEEK_CUR); } return true; } -void SeedDB::Add(const Seed& seed) { - seeds.push_back(seed); -} - -std::size_t SeedDB::Size() const { - return seeds.size(); -} - -std::optional SeedDB::Get(u64 title_id) const { - const auto found_seed_iter = - std::find_if(seeds.begin(), seeds.end(), - [title_id](const auto& seed) { return seed.title_id == title_id; }); - if (found_seed_iter != seeds.end()) { - return found_seed_iter->data; - } - return std::nullopt; -} - -namespace Seeds { - -static SeedDB g_seeddb; -static bool g_seeddb_loaded = false; - -void Load(const std::string& path) { - g_seeddb_loaded = g_seeddb.Load(path); -} - -void Clear() { - g_seeddb.Clear(); - g_seeddb_loaded = false; -} - -std::optional GetSeed(u64 title_id) { - if (!g_seeddb_loaded) { - Load(fmt::format("{}/seeddb.bin", FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir))); - } - return g_seeddb.Get(title_id); -} - -} // namespace Seeds - } // namespace Core diff --git a/src/core/db/seed_db.h b/src/core/db/seed_db.h index 20b6f87..c694eb2 100644 --- a/src/core/db/seed_db.h +++ b/src/core/db/seed_db.h @@ -2,67 +2,26 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -// Modified from Citra's implementation to allow multiple instances - #pragma once #include -#include -#include +#include #include "common/common_types.h" -#include "common/swap.h" namespace Core { constexpr std::size_t SEEDDB_PADDING_BYTES{12}; +constexpr std::size_t SEEDDB_ENTRY_PADDING_BYTES{8}; -struct Seed { - using Data = std::array; - - u64_le title_id; - Data data; - std::array reserved; -}; - +using Seed = std::array; class SeedDB { public: - bool Load(const std::string& path); + bool AddFromFile(const std::string& path); bool Save(const std::string& path); - void Add(const Seed& seed); - std::size_t Size() const; - std::optional Get(u64 title_id) const; - - void Clear() { - seeds.clear(); - } - - auto begin() { - return seeds.begin(); - } - - auto begin() const { - return seeds.begin(); - } - - auto end() { - return seeds.end(); - } - - auto end() const { - return seeds.end(); - } - -private: - std::vector seeds; + std::unordered_map seeds; }; -namespace Seeds { - -void Load(const std::string& path); -void Clear(); -std::optional GetSeed(u64 title_id); - -} // namespace Seeds +inline SeedDB g_seed_db; } // namespace Core diff --git a/src/core/db/title_db.cpp b/src/core/db/title_db.cpp index d60d45a..c042c29 100644 --- a/src/core/db/title_db.cpp +++ b/src/core/db/title_db.cpp @@ -9,25 +9,23 @@ namespace Core { -TitleDB::TitleDB(std::vector data) { - is_good = Init(std::move(data)); +bool TitleDB::AddFromData(std::vector data) { + return Init(std::move(data)); } -TitleDB::TitleDB(const std::string& path) { +bool TitleDB::AddFromFile(const std::string& path) { FileUtil::IOFile file(path, "rb"); DataContainer container(file.GetData()); std::vector> data; if (container.IsGood() && container.GetIVFCLevel4Data(data)) { - is_good = Init(std::move(data[0])); + return Init(std::move(data[0])); + } else { + return false; } } TitleDB::~TitleDB() = default; -bool TitleDB::IsGood() const { - return is_good; -} - bool TitleDB::Init(std::vector data) { if (!InnerFAT_TitleDB::Init({std::move(data)})) { return false; @@ -76,25 +74,19 @@ bool TitleDB::LoadTitleInfo(u32 index) { return true; } -TicketDB::TicketDB(std::vector data) { - is_good = Init(std::move(data)); -} - -TicketDB::TicketDB(const std::string& path) { +bool TicketDB::AddFromFile(const std::string& path) { FileUtil::IOFile file(path, "rb"); DataContainer container(file.GetData()); std::vector> data; if (container.IsGood() && container.GetIVFCLevel4Data(data)) { - is_good = Init(std::move(data[0])); + return Init(std::move(data[0])); + } else { + return false; } } TicketDB::~TicketDB() = default; -bool TicketDB::IsGood() const { - return is_good; -} - bool TicketDB::Init(std::vector data) { if (!InnerFAT_TicketDB::Init({std::move(data)})) { return false; diff --git a/src/core/db/title_db.h b/src/core/db/title_db.h index f48cda4..3b9dbab 100644 --- a/src/core/db/title_db.h +++ b/src/core/db/title_db.h @@ -69,12 +69,10 @@ using InnerFAT_TitleDB = InnerFAT data); - explicit TitleDB(const std::string& path); + bool AddFromData(std::vector data); + bool AddFromFile(const std::string& path); ~TitleDB(); - bool IsGood() const; - std::unordered_map titles; private: @@ -82,8 +80,6 @@ private: bool CheckMagic() const; bool LoadTitleInfo(u32 index); - bool is_good = false; - friend InnerFAT_TitleDB; }; @@ -98,12 +94,9 @@ using InnerFAT_TicketDB = InnerFAT; class TicketDB final : public InnerFAT_TicketDB { public: - explicit TicketDB(std::vector data); - explicit TicketDB(const std::string& path); + bool AddFromFile(const std::string& path); ~TicketDB(); - bool IsGood() const; - std::unordered_map tickets; private: @@ -111,7 +104,6 @@ private: bool CheckMagic() const; bool LoadTicket(u32 index); - bool is_good = false; friend InnerFAT_TicketDB; }; diff --git a/src/core/file_sys/ncch_container.cpp b/src/core/file_sys/ncch_container.cpp index 2aa9951..516b522 100644 --- a/src/core/file_sys/ncch_container.cpp +++ b/src/core/file_sys/ncch_container.cpp @@ -79,13 +79,8 @@ bool NCCHContainer::Load() { if (!ncch_header.seed_crypto) { key_y_secondary = key_y_primary; } else { - auto opt{Seeds::GetSeed(ncch_header.program_id)}; - if (!opt.has_value()) { - LOG_ERROR(Service_FS, "Seed for program {:016X} not found", - ncch_header.program_id); - failed_to_decrypt = true; - } else { - auto seed{*opt}; + if (g_seed_db.seeds.count(ncch_header.program_id)) { + const auto& seed = g_seed_db.seeds.at(ncch_header.program_id); std::array input; std::memcpy(input.data(), key_y_primary.data(), key_y_primary.size()); std::memcpy(input.data() + key_y_primary.size(), seed.data(), seed.size()); @@ -93,6 +88,10 @@ bool NCCHContainer::Load() { std::array hash; sha.CalculateDigest(hash.data(), input.data(), input.size()); std::memcpy(key_y_secondary.data(), hash.data(), key_y_secondary.size()); + } else { + LOG_ERROR(Service_FS, "Seed for program {:016X} not found", + ncch_header.program_id); + failed_to_decrypt = true; } }