diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/file_sys/submission_package.cpp | 169 | ||||
| -rw-r--r-- | src/core/file_sys/submission_package.h | 5 |
2 files changed, 101 insertions, 73 deletions
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp index 11264878d..09bf077cd 100644 --- a/src/core/file_sys/submission_package.cpp +++ b/src/core/file_sys/submission_package.cpp | |||
| @@ -18,6 +18,39 @@ | |||
| 18 | #include "core/loader/loader.h" | 18 | #include "core/loader/loader.h" |
| 19 | 19 | ||
| 20 | namespace FileSys { | 20 | namespace FileSys { |
| 21 | namespace { | ||
| 22 | void SetTicketKeys(const std::vector<VirtualFile>& files) { | ||
| 23 | Core::Crypto::KeyManager keys; | ||
| 24 | |||
| 25 | for (const auto& ticket_file : files) { | ||
| 26 | if (ticket_file == nullptr) { | ||
| 27 | continue; | ||
| 28 | } | ||
| 29 | |||
| 30 | if (ticket_file->GetExtension() != "tik") { | ||
| 31 | continue; | ||
| 32 | } | ||
| 33 | |||
| 34 | if (ticket_file->GetSize() < | ||
| 35 | Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET + sizeof(Core::Crypto::Key128)) { | ||
| 36 | continue; | ||
| 37 | } | ||
| 38 | |||
| 39 | Core::Crypto::Key128 key{}; | ||
| 40 | ticket_file->Read(key.data(), key.size(), Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET); | ||
| 41 | |||
| 42 | // We get the name without the extension in order to create the rights ID. | ||
| 43 | std::string name_only(ticket_file->GetName()); | ||
| 44 | name_only.erase(name_only.size() - 4); | ||
| 45 | |||
| 46 | const auto rights_id_raw = Common::HexStringToArray<16>(name_only); | ||
| 47 | u128 rights_id; | ||
| 48 | std::memcpy(rights_id.data(), rights_id_raw.data(), sizeof(u128)); | ||
| 49 | keys.SetKey(Core::Crypto::S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); | ||
| 50 | } | ||
| 51 | } | ||
| 52 | } // Anonymous namespace | ||
| 53 | |||
| 21 | NSP::NSP(VirtualFile file_) | 54 | NSP::NSP(VirtualFile file_) |
| 22 | : file(std::move(file_)), status{Loader::ResultStatus::Success}, | 55 | : file(std::move(file_)), status{Loader::ResultStatus::Success}, |
| 23 | pfs(std::make_shared<PartitionFilesystem>(file)) { | 56 | pfs(std::make_shared<PartitionFilesystem>(file)) { |
| @@ -26,83 +59,16 @@ NSP::NSP(VirtualFile file_) | |||
| 26 | return; | 59 | return; |
| 27 | } | 60 | } |
| 28 | 61 | ||
| 62 | const auto files = pfs->GetFiles(); | ||
| 63 | |||
| 29 | if (IsDirectoryExeFS(pfs)) { | 64 | if (IsDirectoryExeFS(pfs)) { |
| 30 | extracted = true; | 65 | extracted = true; |
| 31 | exefs = pfs; | 66 | InitializeExeFSAndRomFS(files); |
| 32 | |||
| 33 | const auto& files = pfs->GetFiles(); | ||
| 34 | const auto romfs_iter = | ||
| 35 | std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& file) { | ||
| 36 | return file->GetName().find(".romfs") != std::string::npos; | ||
| 37 | }); | ||
| 38 | if (romfs_iter != files.end()) | ||
| 39 | romfs = *romfs_iter; | ||
| 40 | return; | 67 | return; |
| 41 | } | 68 | } |
| 42 | 69 | ||
| 43 | extracted = false; | 70 | SetTicketKeys(files); |
| 44 | const auto files = pfs->GetFiles(); | 71 | ReadNCAs(files); |
| 45 | |||
| 46 | Core::Crypto::KeyManager keys; | ||
| 47 | for (const auto& ticket_file : files) { | ||
| 48 | if (ticket_file->GetExtension() == "tik") { | ||
| 49 | if (ticket_file == nullptr || | ||
| 50 | ticket_file->GetSize() < | ||
| 51 | Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET + sizeof(Core::Crypto::Key128)) { | ||
| 52 | continue; | ||
| 53 | } | ||
| 54 | |||
| 55 | Core::Crypto::Key128 key{}; | ||
| 56 | ticket_file->Read(key.data(), key.size(), Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET); | ||
| 57 | std::string_view name_only(ticket_file->GetName()); | ||
| 58 | name_only.remove_suffix(4); | ||
| 59 | const auto rights_id_raw = Common::HexStringToArray<16>(name_only); | ||
| 60 | u128 rights_id; | ||
| 61 | std::memcpy(rights_id.data(), rights_id_raw.data(), sizeof(u128)); | ||
| 62 | keys.SetKey(Core::Crypto::S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); | ||
| 63 | } | ||
| 64 | } | ||
| 65 | |||
| 66 | for (const auto& outer_file : files) { | ||
| 67 | if (outer_file->GetName().substr(outer_file->GetName().size() - 9) == ".cnmt.nca") { | ||
| 68 | const auto nca = std::make_shared<NCA>(outer_file); | ||
| 69 | if (nca->GetStatus() != Loader::ResultStatus::Success) { | ||
| 70 | program_status[nca->GetTitleId()] = nca->GetStatus(); | ||
| 71 | continue; | ||
| 72 | } | ||
| 73 | |||
| 74 | const auto section0 = nca->GetSubdirectories()[0]; | ||
| 75 | |||
| 76 | for (const auto& inner_file : section0->GetFiles()) { | ||
| 77 | if (inner_file->GetExtension() != "cnmt") | ||
| 78 | continue; | ||
| 79 | |||
| 80 | const CNMT cnmt(inner_file); | ||
| 81 | auto& ncas_title = ncas[cnmt.GetTitleID()]; | ||
| 82 | |||
| 83 | ncas_title[ContentRecordType::Meta] = nca; | ||
| 84 | for (const auto& rec : cnmt.GetContentRecords()) { | ||
| 85 | const auto id_string = Common::HexArrayToString(rec.nca_id, false); | ||
| 86 | const auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string)); | ||
| 87 | if (next_file == nullptr) { | ||
| 88 | LOG_WARNING(Service_FS, | ||
| 89 | "NCA with ID {}.nca is listed in content metadata, but cannot " | ||
| 90 | "be found in PFS. NSP appears to be corrupted.", | ||
| 91 | id_string); | ||
| 92 | continue; | ||
| 93 | } | ||
| 94 | |||
| 95 | auto next_nca = std::make_shared<NCA>(next_file); | ||
| 96 | if (next_nca->GetType() == NCAContentType::Program) | ||
| 97 | program_status[cnmt.GetTitleID()] = next_nca->GetStatus(); | ||
| 98 | if (next_nca->GetStatus() == Loader::ResultStatus::Success) | ||
| 99 | ncas_title[rec.type] = std::move(next_nca); | ||
| 100 | } | ||
| 101 | |||
| 102 | break; | ||
| 103 | } | ||
| 104 | } | ||
| 105 | } | ||
| 106 | } | 72 | } |
| 107 | 73 | ||
| 108 | NSP::~NSP() = default; | 74 | NSP::~NSP() = default; |
| @@ -242,4 +208,63 @@ VirtualDir NSP::GetParentDirectory() const { | |||
| 242 | bool NSP::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { | 208 | bool NSP::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { |
| 243 | return false; | 209 | return false; |
| 244 | } | 210 | } |
| 211 | |||
| 212 | void NSP::InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files) { | ||
| 213 | exefs = pfs; | ||
| 214 | |||
| 215 | const auto romfs_iter = std::find_if(files.begin(), files.end(), [](const VirtualFile& file) { | ||
| 216 | return file->GetName().rfind(".romfs") != std::string::npos; | ||
| 217 | }); | ||
| 218 | |||
| 219 | if (romfs_iter == files.end()) { | ||
| 220 | return; | ||
| 221 | } | ||
| 222 | |||
| 223 | romfs = *romfs_iter; | ||
| 224 | } | ||
| 225 | |||
| 226 | void NSP::ReadNCAs(const std::vector<VirtualFile>& files) { | ||
| 227 | for (const auto& outer_file : files) { | ||
| 228 | if (outer_file->GetName().substr(outer_file->GetName().size() - 9) != ".cnmt.nca") { | ||
| 229 | continue; | ||
| 230 | } | ||
| 231 | |||
| 232 | const auto nca = std::make_shared<NCA>(outer_file); | ||
| 233 | if (nca->GetStatus() != Loader::ResultStatus::Success) { | ||
| 234 | program_status[nca->GetTitleId()] = nca->GetStatus(); | ||
| 235 | continue; | ||
| 236 | } | ||
| 237 | |||
| 238 | const auto section0 = nca->GetSubdirectories()[0]; | ||
| 239 | |||
| 240 | for (const auto& inner_file : section0->GetFiles()) { | ||
| 241 | if (inner_file->GetExtension() != "cnmt") | ||
| 242 | continue; | ||
| 243 | |||
| 244 | const CNMT cnmt(inner_file); | ||
| 245 | auto& ncas_title = ncas[cnmt.GetTitleID()]; | ||
| 246 | |||
| 247 | ncas_title[ContentRecordType::Meta] = nca; | ||
| 248 | for (const auto& rec : cnmt.GetContentRecords()) { | ||
| 249 | const auto id_string = Common::HexArrayToString(rec.nca_id, false); | ||
| 250 | const auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string)); | ||
| 251 | if (next_file == nullptr) { | ||
| 252 | LOG_WARNING(Service_FS, | ||
| 253 | "NCA with ID {}.nca is listed in content metadata, but cannot " | ||
| 254 | "be found in PFS. NSP appears to be corrupted.", | ||
| 255 | id_string); | ||
| 256 | continue; | ||
| 257 | } | ||
| 258 | |||
| 259 | auto next_nca = std::make_shared<NCA>(next_file); | ||
| 260 | if (next_nca->GetType() == NCAContentType::Program) | ||
| 261 | program_status[cnmt.GetTitleID()] = next_nca->GetStatus(); | ||
| 262 | if (next_nca->GetStatus() == Loader::ResultStatus::Success) | ||
| 263 | ncas_title[rec.type] = std::move(next_nca); | ||
| 264 | } | ||
| 265 | |||
| 266 | break; | ||
| 267 | } | ||
| 268 | } | ||
| 269 | } | ||
| 245 | } // namespace FileSys | 270 | } // namespace FileSys |
diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h index e85a2b76e..da3dc5e9f 100644 --- a/src/core/file_sys/submission_package.h +++ b/src/core/file_sys/submission_package.h | |||
| @@ -59,9 +59,12 @@ protected: | |||
| 59 | bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; | 59 | bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; |
| 60 | 60 | ||
| 61 | private: | 61 | private: |
| 62 | void InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files); | ||
| 63 | void ReadNCAs(const std::vector<VirtualFile>& files); | ||
| 64 | |||
| 62 | VirtualFile file; | 65 | VirtualFile file; |
| 63 | 66 | ||
| 64 | bool extracted; | 67 | bool extracted = false; |
| 65 | Loader::ResultStatus status; | 68 | Loader::ResultStatus status; |
| 66 | std::map<u64, Loader::ResultStatus> program_status; | 69 | std::map<u64, Loader::ResultStatus> program_status; |
| 67 | 70 | ||