diff --git a/programs/nstool/source/UserSettings.cpp b/programs/nstool/source/UserSettings.cpp index f6229ee..00216de 100644 --- a/programs/nstool/source/UserSettings.cpp +++ b/programs/nstool/source/UserSettings.cpp @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include #include #include @@ -49,8 +51,8 @@ void UserSettings::showHelp() printf(" nstool [--listfs] [--update --normal --secure ] <.xci file>\n"); printf(" --listfs Print file system in embedded partitions\n"); printf(" --update Extract \"update\" partition to directory\n"); - printf(" --normal Extract \"normal\" partition to directory\n"); printf(" --logo Extract \"logo\" partition to directory\n"); + printf(" --normal Extract \"normal\" partition to directory\n"); printf(" --secure Extract \"secure\" partition to directory\n"); printf("\n PFS0/HFS0 (PartitionFs), RomFs, NSP (Ninendo Submission Package)\n"); printf(" nstool [--listfs] [--fsdir ] \n"); @@ -128,6 +130,11 @@ const sOptional& UserSettings::getXciUpdatePath() const return mXciUpdatePath; } +const sOptional& UserSettings::getXciLogoPath() const +{ + return mXciLogoPath; +} + const sOptional& UserSettings::getXciNormalPath() const { return mXciNormalPath; @@ -138,11 +145,6 @@ const sOptional& UserSettings::getXciSecurePath() const return mXciSecurePath; } -const sOptional& UserSettings::getXciLogoPath() const -{ - return mXciLogoPath; -} - const sOptional& UserSettings::getFsPath() const { return mFsPath; @@ -688,7 +690,7 @@ FileType UserSettings::getFileTypeFromString(const std::string& type_str) FileType UserSettings::determineFileTypeFromFile(const std::string& path) { - static const size_t kMaxReadSize = 0x1000; + static const size_t kMaxReadSize = 0x4000; FileType file_type = FILE_INVALID; fnd::SimpleFile file; fnd::MemoryBlob scratch; @@ -702,16 +704,7 @@ FileType UserSettings::determineFileTypeFromFile(const std::string& path) // close file file.close(); - //fnd::SimpleTextOutput::hxdStyleDump(scratch.getBytes(), scratch.getSize()); - - // prepare decrypted NCA data - byte_t nca_raw[nx::nca::kHeaderSize]; - nx::sNcaHeader* nca_header = (nx::sNcaHeader*)(nca_raw + nx::NcaUtils::sectorToOffset(1)); - if (scratch.getSize() >= nx::nca::kHeaderSize) - { - nx::NcaUtils::decryptNcaHeader(scratch.getBytes(), nca_raw, mKeyset.nca.header_key); - } // _QUICK_CAST resolves to a pointer of type 'st' located at scratch.getBytes() + 'oft' #define _QUICK_CAST(st, oft) ((st*)(scratch.getBytes() + (oft))) @@ -729,15 +722,18 @@ FileType UserSettings::determineFileTypeFromFile(const std::string& path) // test romfs else if (_ASSERT_SIZE(sizeof(nx::sRomfsHeader)) && _QUICK_CAST(nx::sRomfsHeader, 0)->header_size.get() == sizeof(nx::sRomfsHeader) && _QUICK_CAST(nx::sRomfsHeader, 0)->sections[1].offset.get() == (_QUICK_CAST(nx::sRomfsHeader, 0)->sections[0].offset.get() + _QUICK_CAST(nx::sRomfsHeader, 0)->sections[0].size.get())) file_type = FILE_ROMFS; - // test nca2 - else if (_ASSERT_SIZE(nx::nca::kHeaderSize) && nca_header->signature.get() == nx::nca::kNca2Sig) - file_type = FILE_NCA; - // test nca3 - else if (_ASSERT_SIZE(nx::nca::kHeaderSize) && nca_header->signature.get() == nx::nca::kNca3Sig) - file_type = FILE_NCA; // test npdm else if (_ASSERT_SIZE(sizeof(nx::sNpdmHeader)) && _QUICK_CAST(nx::sNpdmHeader, 0)->signature.get() == nx::npdm::kNpdmStructSig) file_type = FILE_NPDM; + // test nca + else if (determineValidNcaFromSample(scratch)) + file_type = FILE_NCA; + // test cnmt + else if (determineValidCnmtFromSample(scratch)) + file_type = FILE_CNMT; + // test nacp + else if (determineValidNacpFromSample(scratch)) + file_type = FILE_NACP; // test nso else if (_ASSERT_SIZE(sizeof(nx::sNsoHeader)) && _QUICK_CAST(nx::sNsoHeader, 0)->signature.get() == nx::nso::kNsoSig) file_type = FILE_NSO; @@ -757,6 +753,95 @@ FileType UserSettings::determineFileTypeFromFile(const std::string& path) return file_type; } +bool UserSettings::determineValidNcaFromSample(const fnd::MemoryBlob& sample) const +{ + // prepare decrypted NCA data + byte_t nca_raw[nx::nca::kHeaderSize]; + nx::sNcaHeader* nca_header = (nx::sNcaHeader*)(nca_raw + nx::NcaUtils::sectorToOffset(1)); + + if (sample.getSize() < nx::nca::kHeaderSize) + return false; + + nx::NcaUtils::decryptNcaHeader(sample.getBytes(), nca_raw, mKeyset.nca.header_key); + + if (nca_header->signature.get() != nx::nca::kNca2Sig && nca_header->signature.get() != nx::nca::kNca3Sig) + return false; + + return true; +} + +bool UserSettings::determineValidCnmtFromSample(const fnd::MemoryBlob& sample) const +{ + if (sample.getSize() < sizeof(nx::sContentMetaHeader)) + return false; + + const nx::sContentMetaHeader* data = (const nx::sContentMetaHeader*)sample.getBytes(); + + size_t minimum_size = sizeof(nx::sContentMetaHeader) + data->exhdr_size.get() + data->content_count.get() * sizeof(nx::sContentInfo) + data->content_meta_count.get() * sizeof(nx::sContentMetaInfo) + nx::cnmt::kDigestLen; + + if (sample.getSize() < minimum_size) + return false; + + if (data->type == nx::cnmt::METATYPE_APPLICATION) + { + const nx::sApplicationMetaExtendedHeader* meta = (const nx::sApplicationMetaExtendedHeader*)(sample.getBytes() + sizeof(nx::sContentMetaHeader)); + if ((meta->patch_id.get() & data->id.get()) != data->id.get()) + return false; + } + else if (data->type == nx::cnmt::METATYPE_PATCH) + { + const nx::sPatchMetaExtendedHeader* meta = (const nx::sPatchMetaExtendedHeader*)(sample.getBytes() + sizeof(nx::sContentMetaHeader)); + if ((meta->application_id.get() & data->id.get()) != meta->application_id.get()) + return false; + + minimum_size += meta->extended_data_size.get(); + } + else if (data->type == nx::cnmt::METATYPE_ADD_ON_CONTENT) + { + const nx::sAddOnContentMetaExtendedHeader* meta = (const nx::sAddOnContentMetaExtendedHeader*)(sample.getBytes() + sizeof(nx::sContentMetaHeader)); + if ((meta->application_id.get() & data->id.get()) != meta->application_id.get()) + return false; + } + else if (data->type == nx::cnmt::METATYPE_DELTA) + { + const nx::sDeltaMetaExtendedHeader* meta = (const nx::sDeltaMetaExtendedHeader*)(sample.getBytes() + sizeof(nx::sContentMetaHeader)); + if ((meta->application_id.get() & data->id.get()) != meta->application_id.get()) + return false; + + minimum_size += meta->extended_data_size.get(); + } + + if (sample.getSize() != minimum_size) + return false; + + return true; +} + +bool UserSettings::determineValidNacpFromSample(const fnd::MemoryBlob& sample) const +{ + if (sample.getSize() != sizeof(nx::sApplicationControlProperty)) + return false; + + const nx::sApplicationControlProperty* data = (const nx::sApplicationControlProperty*)sample.getBytes(); + + if (data->logo_type > nx::nacp::LOGO_Nintendo) + return false; + + if (data->display_version[0] == 0) + return false; + + if (data->user_account_save_data_size.get() == 0 && data->user_account_save_data_journal_size.get() != 0) + return false; + + if (data->user_account_save_data_journal_size.get() == 0 && data->user_account_save_data_size.get() != 0) + return false; + + if (data->supported_language_flag.get() == 0) + return false; + + return true; +} + nx::npdm::InstructionType UserSettings::getInstructionTypeFromString(const std::string & type_str) { std::string str = type_str; diff --git a/programs/nstool/source/UserSettings.h b/programs/nstool/source/UserSettings.h index b876ff8..cb82b5e 100644 --- a/programs/nstool/source/UserSettings.h +++ b/programs/nstool/source/UserSettings.h @@ -1,6 +1,7 @@ #pragma once #include #include +#include #include #include "nstool.h" @@ -27,9 +28,9 @@ public: // specialised paths const sOptional& getXciUpdatePath() const; + const sOptional& getXciLogoPath() const; const sOptional& getXciNormalPath() const; const sOptional& getXciSecurePath() const; - const sOptional& getXciLogoPath() const; const sOptional& getFsPath() const; const sOptional& getNcaPart0Path() const; const sOptional& getNcaPart1Path() const; @@ -53,9 +54,9 @@ private: sOptional minimal_output; sOptional list_fs; sOptional update_path; + sOptional logo_path; sOptional normal_path; sOptional secure_path; - sOptional logo_path; sOptional fs_path; sOptional nca_titlekey; sOptional nca_bodykey; @@ -78,9 +79,9 @@ private: bool mListFs; sOptional mXciUpdatePath; + sOptional mXciLogoPath; sOptional mXciNormalPath; sOptional mXciSecurePath; - sOptional mXciLogoPath; sOptional mFsPath; sOptional mNcaPart0Path; @@ -101,5 +102,8 @@ private: void decodeHexStringToBytes(const std::string& name, const std::string& str, byte_t* out, size_t out_len); FileType getFileTypeFromString(const std::string& type_str); FileType determineFileTypeFromFile(const std::string& path); + bool determineValidNcaFromSample(const fnd::MemoryBlob& sample) const; + bool determineValidCnmtFromSample(const fnd::MemoryBlob& sample) const; + bool determineValidNacpFromSample(const fnd::MemoryBlob& sample) const; nx::npdm::InstructionType getInstructionTypeFromString(const std::string& type_str); }; \ No newline at end of file diff --git a/programs/nstool/source/main.cpp b/programs/nstool/source/main.cpp index 4821fa4..0edeffd 100644 --- a/programs/nstool/source/main.cpp +++ b/programs/nstool/source/main.cpp @@ -30,12 +30,12 @@ int main(int argc, char** argv) if (user_set.getXciUpdatePath().isSet) xci.setPartitionForExtract(nx::xci::kUpdatePartitionStr, user_set.getXciUpdatePath().var); + if (user_set.getXciLogoPath().isSet) + xci.setPartitionForExtract(nx::xci::kLogoPartitionStr, user_set.getXciLogoPath().var); if (user_set.getXciNormalPath().isSet) xci.setPartitionForExtract(nx::xci::kNormalPartitionStr, user_set.getXciNormalPath().var); if (user_set.getXciSecurePath().isSet) xci.setPartitionForExtract(nx::xci::kSecurePartitionStr, user_set.getXciSecurePath().var); - if (user_set.getXciLogoPath().isSet) - xci.setPartitionForExtract(nx::xci::kLogoPartitionStr, user_set.getXciLogoPath().var); xci.setListFs(user_set.isListFs()); xci.process();