mirror of
https://github.com/jakcron/nstool
synced 2024-11-23 05:59:29 +00:00
commit
d8800615af
3 changed files with 116 additions and 27 deletions
|
@ -17,6 +17,8 @@
|
||||||
#include <nx/nca.h>
|
#include <nx/nca.h>
|
||||||
#include <nx/npdm.h>
|
#include <nx/npdm.h>
|
||||||
#include <nx/romfs.h>
|
#include <nx/romfs.h>
|
||||||
|
#include <nx/cnmt.h>
|
||||||
|
#include <nx/nacp.h>
|
||||||
#include <nx/nso.h>
|
#include <nx/nso.h>
|
||||||
#include <nx/nro.h>
|
#include <nx/nro.h>
|
||||||
#include <nx/aset.h>
|
#include <nx/aset.h>
|
||||||
|
@ -49,8 +51,8 @@ void UserSettings::showHelp()
|
||||||
printf(" nstool [--listfs] [--update <dir> --normal <dir> --secure <dir>] <.xci file>\n");
|
printf(" nstool [--listfs] [--update <dir> --normal <dir> --secure <dir>] <.xci file>\n");
|
||||||
printf(" --listfs Print file system in embedded partitions\n");
|
printf(" --listfs Print file system in embedded partitions\n");
|
||||||
printf(" --update Extract \"update\" partition to directory\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(" --logo Extract \"logo\" partition to directory\n");
|
||||||
|
printf(" --normal Extract \"normal\" partition to directory\n");
|
||||||
printf(" --secure Extract \"secure\" partition to directory\n");
|
printf(" --secure Extract \"secure\" partition to directory\n");
|
||||||
printf("\n PFS0/HFS0 (PartitionFs), RomFs, NSP (Ninendo Submission Package)\n");
|
printf("\n PFS0/HFS0 (PartitionFs), RomFs, NSP (Ninendo Submission Package)\n");
|
||||||
printf(" nstool [--listfs] [--fsdir <dir>] <file>\n");
|
printf(" nstool [--listfs] [--fsdir <dir>] <file>\n");
|
||||||
|
@ -128,6 +130,11 @@ const sOptional<std::string>& UserSettings::getXciUpdatePath() const
|
||||||
return mXciUpdatePath;
|
return mXciUpdatePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const sOptional<std::string>& UserSettings::getXciLogoPath() const
|
||||||
|
{
|
||||||
|
return mXciLogoPath;
|
||||||
|
}
|
||||||
|
|
||||||
const sOptional<std::string>& UserSettings::getXciNormalPath() const
|
const sOptional<std::string>& UserSettings::getXciNormalPath() const
|
||||||
{
|
{
|
||||||
return mXciNormalPath;
|
return mXciNormalPath;
|
||||||
|
@ -138,11 +145,6 @@ const sOptional<std::string>& UserSettings::getXciSecurePath() const
|
||||||
return mXciSecurePath;
|
return mXciSecurePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sOptional<std::string>& UserSettings::getXciLogoPath() const
|
|
||||||
{
|
|
||||||
return mXciLogoPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
const sOptional<std::string>& UserSettings::getFsPath() const
|
const sOptional<std::string>& UserSettings::getFsPath() const
|
||||||
{
|
{
|
||||||
return mFsPath;
|
return mFsPath;
|
||||||
|
@ -688,7 +690,7 @@ FileType UserSettings::getFileTypeFromString(const std::string& type_str)
|
||||||
|
|
||||||
FileType UserSettings::determineFileTypeFromFile(const std::string& path)
|
FileType UserSettings::determineFileTypeFromFile(const std::string& path)
|
||||||
{
|
{
|
||||||
static const size_t kMaxReadSize = 0x1000;
|
static const size_t kMaxReadSize = 0x4000;
|
||||||
FileType file_type = FILE_INVALID;
|
FileType file_type = FILE_INVALID;
|
||||||
fnd::SimpleFile file;
|
fnd::SimpleFile file;
|
||||||
fnd::MemoryBlob scratch;
|
fnd::MemoryBlob scratch;
|
||||||
|
@ -702,16 +704,7 @@ FileType UserSettings::determineFileTypeFromFile(const std::string& path)
|
||||||
// close file
|
// close file
|
||||||
file.close();
|
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'
|
// _QUICK_CAST resolves to a pointer of type 'st' located at scratch.getBytes() + 'oft'
|
||||||
#define _QUICK_CAST(st, oft) ((st*)(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
|
// 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()))
|
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;
|
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
|
// test npdm
|
||||||
else if (_ASSERT_SIZE(sizeof(nx::sNpdmHeader)) && _QUICK_CAST(nx::sNpdmHeader, 0)->signature.get() == nx::npdm::kNpdmStructSig)
|
else if (_ASSERT_SIZE(sizeof(nx::sNpdmHeader)) && _QUICK_CAST(nx::sNpdmHeader, 0)->signature.get() == nx::npdm::kNpdmStructSig)
|
||||||
file_type = FILE_NPDM;
|
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
|
// test nso
|
||||||
else if (_ASSERT_SIZE(sizeof(nx::sNsoHeader)) && _QUICK_CAST(nx::sNsoHeader, 0)->signature.get() == nx::nso::kNsoSig)
|
else if (_ASSERT_SIZE(sizeof(nx::sNsoHeader)) && _QUICK_CAST(nx::sNsoHeader, 0)->signature.get() == nx::nso::kNsoSig)
|
||||||
file_type = FILE_NSO;
|
file_type = FILE_NSO;
|
||||||
|
@ -757,6 +753,95 @@ FileType UserSettings::determineFileTypeFromFile(const std::string& path)
|
||||||
return file_type;
|
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)
|
nx::npdm::InstructionType UserSettings::getInstructionTypeFromString(const std::string & type_str)
|
||||||
{
|
{
|
||||||
std::string str = type_str;
|
std::string str = type_str;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <fnd/types.h>
|
#include <fnd/types.h>
|
||||||
|
#include <fnd/MemoryBlob.h>
|
||||||
#include <nx/npdm.h>
|
#include <nx/npdm.h>
|
||||||
#include "nstool.h"
|
#include "nstool.h"
|
||||||
|
|
||||||
|
@ -27,9 +28,9 @@ public:
|
||||||
|
|
||||||
// specialised paths
|
// specialised paths
|
||||||
const sOptional<std::string>& getXciUpdatePath() const;
|
const sOptional<std::string>& getXciUpdatePath() const;
|
||||||
|
const sOptional<std::string>& getXciLogoPath() const;
|
||||||
const sOptional<std::string>& getXciNormalPath() const;
|
const sOptional<std::string>& getXciNormalPath() const;
|
||||||
const sOptional<std::string>& getXciSecurePath() const;
|
const sOptional<std::string>& getXciSecurePath() const;
|
||||||
const sOptional<std::string>& getXciLogoPath() const;
|
|
||||||
const sOptional<std::string>& getFsPath() const;
|
const sOptional<std::string>& getFsPath() const;
|
||||||
const sOptional<std::string>& getNcaPart0Path() const;
|
const sOptional<std::string>& getNcaPart0Path() const;
|
||||||
const sOptional<std::string>& getNcaPart1Path() const;
|
const sOptional<std::string>& getNcaPart1Path() const;
|
||||||
|
@ -53,9 +54,9 @@ private:
|
||||||
sOptional<bool> minimal_output;
|
sOptional<bool> minimal_output;
|
||||||
sOptional<bool> list_fs;
|
sOptional<bool> list_fs;
|
||||||
sOptional<std::string> update_path;
|
sOptional<std::string> update_path;
|
||||||
|
sOptional<std::string> logo_path;
|
||||||
sOptional<std::string> normal_path;
|
sOptional<std::string> normal_path;
|
||||||
sOptional<std::string> secure_path;
|
sOptional<std::string> secure_path;
|
||||||
sOptional<std::string> logo_path;
|
|
||||||
sOptional<std::string> fs_path;
|
sOptional<std::string> fs_path;
|
||||||
sOptional<std::string> nca_titlekey;
|
sOptional<std::string> nca_titlekey;
|
||||||
sOptional<std::string> nca_bodykey;
|
sOptional<std::string> nca_bodykey;
|
||||||
|
@ -78,9 +79,9 @@ private:
|
||||||
|
|
||||||
bool mListFs;
|
bool mListFs;
|
||||||
sOptional<std::string> mXciUpdatePath;
|
sOptional<std::string> mXciUpdatePath;
|
||||||
|
sOptional<std::string> mXciLogoPath;
|
||||||
sOptional<std::string> mXciNormalPath;
|
sOptional<std::string> mXciNormalPath;
|
||||||
sOptional<std::string> mXciSecurePath;
|
sOptional<std::string> mXciSecurePath;
|
||||||
sOptional<std::string> mXciLogoPath;
|
|
||||||
sOptional<std::string> mFsPath;
|
sOptional<std::string> mFsPath;
|
||||||
|
|
||||||
sOptional<std::string> mNcaPart0Path;
|
sOptional<std::string> mNcaPart0Path;
|
||||||
|
@ -101,5 +102,8 @@ private:
|
||||||
void decodeHexStringToBytes(const std::string& name, const std::string& str, byte_t* out, size_t out_len);
|
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 getFileTypeFromString(const std::string& type_str);
|
||||||
FileType determineFileTypeFromFile(const std::string& path);
|
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);
|
nx::npdm::InstructionType getInstructionTypeFromString(const std::string& type_str);
|
||||||
};
|
};
|
|
@ -30,12 +30,12 @@ int main(int argc, char** argv)
|
||||||
|
|
||||||
if (user_set.getXciUpdatePath().isSet)
|
if (user_set.getXciUpdatePath().isSet)
|
||||||
xci.setPartitionForExtract(nx::xci::kUpdatePartitionStr, user_set.getXciUpdatePath().var);
|
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)
|
if (user_set.getXciNormalPath().isSet)
|
||||||
xci.setPartitionForExtract(nx::xci::kNormalPartitionStr, user_set.getXciNormalPath().var);
|
xci.setPartitionForExtract(nx::xci::kNormalPartitionStr, user_set.getXciNormalPath().var);
|
||||||
if (user_set.getXciSecurePath().isSet)
|
if (user_set.getXciSecurePath().isSet)
|
||||||
xci.setPartitionForExtract(nx::xci::kSecurePartitionStr, user_set.getXciSecurePath().var);
|
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.setListFs(user_set.isListFs());
|
||||||
|
|
||||||
xci.process();
|
xci.process();
|
||||||
|
|
Loading…
Reference in a new issue