mirror of
https://github.com/jakcron/nstool
synced 2024-11-15 02:06:40 +00:00
final commit
This commit is contained in:
parent
98c933305d
commit
d730c1e3b8
22 changed files with 1050 additions and 74 deletions
34
NXTools.sln
34
NXTools.sln
|
@ -13,6 +13,21 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ncatool", "programs\ncatool
|
||||||
EndProject
|
EndProject
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{170B4A09-1B67-4A62-93AB-116EBCFF4A8C}"
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{170B4A09-1B67-4A62-93AB-116EBCFF4A8C}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "npdmtool", "programs\npdmtool\npdmtool.vcxproj", "{550C6AC3-EBE0-46CA-AE6C-EEEB59DDF35C}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Programs", "Programs", "{E0863FCC-8E72-490D-BE1B-458F12CA8298}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
programs\makefile = programs\makefile
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{8F6C846D-35E2-47FD-AF42-7A3FD036346E}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
.gitignore = .gitignore
|
||||||
|
makefile = makefile
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pfstool", "programs\pfstool\pfstool.vcxproj", "{BC2F2D07-BAB3-469C-9C25-8CC54F96F7AB}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|x64 = Debug|x64
|
Debug|x64 = Debug|x64
|
||||||
|
@ -53,6 +68,22 @@ Global
|
||||||
{7DA88C6F-4470-495D-995A-4F633F3370C1}.Release|x64.Build.0 = Release|x64
|
{7DA88C6F-4470-495D-995A-4F633F3370C1}.Release|x64.Build.0 = Release|x64
|
||||||
{7DA88C6F-4470-495D-995A-4F633F3370C1}.Release|x86.ActiveCfg = Release|Win32
|
{7DA88C6F-4470-495D-995A-4F633F3370C1}.Release|x86.ActiveCfg = Release|Win32
|
||||||
{7DA88C6F-4470-495D-995A-4F633F3370C1}.Release|x86.Build.0 = Release|Win32
|
{7DA88C6F-4470-495D-995A-4F633F3370C1}.Release|x86.Build.0 = Release|Win32
|
||||||
|
{550C6AC3-EBE0-46CA-AE6C-EEEB59DDF35C}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{550C6AC3-EBE0-46CA-AE6C-EEEB59DDF35C}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{550C6AC3-EBE0-46CA-AE6C-EEEB59DDF35C}.Debug|x86.ActiveCfg = Debug|Win32
|
||||||
|
{550C6AC3-EBE0-46CA-AE6C-EEEB59DDF35C}.Debug|x86.Build.0 = Debug|Win32
|
||||||
|
{550C6AC3-EBE0-46CA-AE6C-EEEB59DDF35C}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{550C6AC3-EBE0-46CA-AE6C-EEEB59DDF35C}.Release|x64.Build.0 = Release|x64
|
||||||
|
{550C6AC3-EBE0-46CA-AE6C-EEEB59DDF35C}.Release|x86.ActiveCfg = Release|Win32
|
||||||
|
{550C6AC3-EBE0-46CA-AE6C-EEEB59DDF35C}.Release|x86.Build.0 = Release|Win32
|
||||||
|
{BC2F2D07-BAB3-469C-9C25-8CC54F96F7AB}.Debug|x64.ActiveCfg = Debug|x64
|
||||||
|
{BC2F2D07-BAB3-469C-9C25-8CC54F96F7AB}.Debug|x64.Build.0 = Debug|x64
|
||||||
|
{BC2F2D07-BAB3-469C-9C25-8CC54F96F7AB}.Debug|x86.ActiveCfg = Debug|Win32
|
||||||
|
{BC2F2D07-BAB3-469C-9C25-8CC54F96F7AB}.Debug|x86.Build.0 = Debug|Win32
|
||||||
|
{BC2F2D07-BAB3-469C-9C25-8CC54F96F7AB}.Release|x64.ActiveCfg = Release|x64
|
||||||
|
{BC2F2D07-BAB3-469C-9C25-8CC54F96F7AB}.Release|x64.Build.0 = Release|x64
|
||||||
|
{BC2F2D07-BAB3-469C-9C25-8CC54F96F7AB}.Release|x86.ActiveCfg = Release|Win32
|
||||||
|
{BC2F2D07-BAB3-469C-9C25-8CC54F96F7AB}.Release|x86.Build.0 = Release|Win32
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
@ -61,5 +92,8 @@ Global
|
||||||
{4D27EDB9-5110-44FE-8CE2-D46C5AD3C55B} = {170B4A09-1B67-4A62-93AB-116EBCFF4A8C}
|
{4D27EDB9-5110-44FE-8CE2-D46C5AD3C55B} = {170B4A09-1B67-4A62-93AB-116EBCFF4A8C}
|
||||||
{6ADBB60D-DBA0-411D-BD2D-A355EF8E0FE1} = {170B4A09-1B67-4A62-93AB-116EBCFF4A8C}
|
{6ADBB60D-DBA0-411D-BD2D-A355EF8E0FE1} = {170B4A09-1B67-4A62-93AB-116EBCFF4A8C}
|
||||||
{91BA9E79-8242-4F7D-B997-0DFEC95EA22B} = {170B4A09-1B67-4A62-93AB-116EBCFF4A8C}
|
{91BA9E79-8242-4F7D-B997-0DFEC95EA22B} = {170B4A09-1B67-4A62-93AB-116EBCFF4A8C}
|
||||||
|
{7DA88C6F-4470-495D-995A-4F633F3370C1} = {E0863FCC-8E72-490D-BE1B-458F12CA8298}
|
||||||
|
{550C6AC3-EBE0-46CA-AE6C-EEEB59DDF35C} = {E0863FCC-8E72-490D-BE1B-458F12CA8298}
|
||||||
|
{BC2F2D07-BAB3-469C-9C25-8CC54F96F7AB} = {E0863FCC-8E72-490D-BE1B-458F12CA8298}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|
|
@ -73,20 +73,45 @@ namespace fnd
|
||||||
|
|
||||||
// functions
|
// functions
|
||||||
void addElement(const T& element) { mElements.push_back(element); }
|
void addElement(const T& element) { mElements.push_back(element); }
|
||||||
size_t getIndexOf(const T& element) const
|
size_t getIndexOf(const T& key) const
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < getSize(); i++)
|
for (size_t i = 0; i < getSize(); i++)
|
||||||
{
|
{
|
||||||
if (getElement(i) == element) return i;
|
if (getElement(i) == key) return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw Exception("LIST", "Element does not exist");
|
throw Exception("LIST", "Element does not exist");
|
||||||
}
|
}
|
||||||
bool hasElement(const T& element) const
|
bool hasElement(const T& key) const
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
getIndexOf(element);
|
getIndexOf(key);
|
||||||
|
} catch (const Exception&)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// special
|
||||||
|
template <class X>
|
||||||
|
size_t getIndexOf(const X& key) const
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < getSize(); i++)
|
||||||
|
{
|
||||||
|
if (getElement(i) == key) return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Exception("LIST", "Element does not exist");
|
||||||
|
}
|
||||||
|
template <class X>
|
||||||
|
bool hasElement(const X& key) const
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
getIndexOf(key);
|
||||||
} catch (const Exception&)
|
} catch (const Exception&)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -23,12 +23,12 @@ void nx::AcidBinary::clear()
|
||||||
mEmbeddedPublicKey = crypto::rsa::sRsa2048Key();
|
mEmbeddedPublicKey = crypto::rsa::sRsa2048Key();
|
||||||
}
|
}
|
||||||
|
|
||||||
const crypto::rsa::sRsa2048Key & nx::AcidBinary::getPublicKey() const
|
const crypto::rsa::sRsa2048Key & nx::AcidBinary::getNcaHeader2RsaKey() const
|
||||||
{
|
{
|
||||||
return mEmbeddedPublicKey;
|
return mEmbeddedPublicKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nx::AcidBinary::setPublicKey(const crypto::rsa::sRsa2048Key & key)
|
void nx::AcidBinary::setNcaHeader2RsaKey(const crypto::rsa::sRsa2048Key & key)
|
||||||
{
|
{
|
||||||
mEmbeddedPublicKey = key;
|
mEmbeddedPublicKey = key;
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,8 +32,8 @@ namespace nx
|
||||||
// variables
|
// variables
|
||||||
virtual void clear();
|
virtual void clear();
|
||||||
|
|
||||||
const crypto::rsa::sRsa2048Key& getPublicKey() const;
|
const crypto::rsa::sRsa2048Key& getNcaHeader2RsaKey() const;
|
||||||
void setPublicKey(const crypto::rsa::sRsa2048Key& key);
|
void setNcaHeader2RsaKey(const crypto::rsa::sRsa2048Key& key);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const std::string kModuleName = "ACID_BINARY";
|
const std::string kModuleName = "ACID_BINARY";
|
||||||
|
|
|
@ -46,6 +46,8 @@ namespace crypto
|
||||||
TitleKeyGenarateKey
|
TitleKeyGenarateKey
|
||||||
};
|
};
|
||||||
|
|
||||||
|
u8 titlekey_generate_key[0x20] = { 39, 111, 56, 188, 68, 106, 241, 86, 31, 44, 90, 111, 116, 32, 93, 197, 25, 181, 59, 188, 178, 159, 211, 175, 212, 178, 162, 4, 28, 152, 117, 126 };
|
||||||
|
|
||||||
// aes128-cbc keys
|
// aes128-cbc keys
|
||||||
u8 xci_header_key[16] = { 0x01, 0xc5, 0x8f, 0xe7, 0x2d, 0x13, 0x5a, 0xb2, 0x9a, 0x3f, 0x69, 0x33, 0x95, 0x74, 0xb1 };
|
u8 xci_header_key[16] = { 0x01, 0xc5, 0x8f, 0xe7, 0x2d, 0x13, 0x5a, 0xb2, 0x9a, 0x3f, 0x69, 0x33, 0x95, 0x74, 0xb1 };
|
||||||
u8 eticket_common_key[16] = { 0x55, 0xA3, 0xF8, 0x72, 0xBD, 0xC8, 0x0C, 0x55, 0x5A, 0x65, 0x43, 0x81, 0x13, 0x9E, 0x15, 0x3B }; // lol this 3ds dev common key
|
u8 eticket_common_key[16] = { 0x55, 0xA3, 0xF8, 0x72, 0xBD, 0xC8, 0x0C, 0x55, 0x5A, 0x65, 0x43, 0x81, 0x13, 0x9E, 0x15, 0x3B }; // lol this 3ds dev common key
|
||||||
|
@ -69,7 +71,7 @@ namespace crypto
|
||||||
{ 0x1C, 0x9B, 0x7B, 0xFA, 0xF6, 0x28, 0x18, 0x3D, 0x71, 0xF6, 0x4D, 0x73, 0xF1, 0x50, 0xB9, 0xD2 }
|
{ 0x1C, 0x9B, 0x7B, 0xFA, 0xF6, 0x28, 0x18, 0x3D, 0x71, 0xF6, 0x4D, 0x73, 0xF1, 0x50, 0xB9, 0xD2 }
|
||||||
};
|
};
|
||||||
// aeskey, related to m_KeyAreaEncryptionKeyList (first in list?)
|
// aeskey, related to m_KeyAreaEncryptionKeyList (first in list?)
|
||||||
u8 unk_aes_key[0x10] = { 0x3A, 0x7C, 0x3E, 0x38, 0x4A, 0x8F, 0x22, 0xFF, 0x4B, 0x21, 0x57, 0x19, 0xB7, 0x81, 0xAD, 0x0C };
|
u8 key_area_encryption_key_0[0x10] = { 0x3A, 0x7C, 0x3E, 0x38, 0x4A, 0x8F, 0x22, 0xFF, 0x4B, 0x21, 0x57, 0x19, 0xB7, 0x81, 0xAD, 0x0C };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,10 +10,14 @@ void NcaHeader::exportBinary()
|
||||||
sNcaHeader* hdr = (sNcaHeader*)mBinaryBlob.getBytes();
|
sNcaHeader* hdr = (sNcaHeader*)mBinaryBlob.getBytes();
|
||||||
|
|
||||||
hdr->set_signature(kNcaSig.c_str());
|
hdr->set_signature(kNcaSig.c_str());
|
||||||
hdr->set_block_size(kDefaultBlockSize);
|
hdr->set_distribution_type(mDistributionType);
|
||||||
|
hdr->set_content_type(mContentType);
|
||||||
|
hdr->set_key_generation(mEncryptionType);
|
||||||
|
hdr->set_key_area_encryption_key_index(mKeyIndex);
|
||||||
hdr->set_nca_size(mNcaSize);
|
hdr->set_nca_size(mNcaSize);
|
||||||
hdr->set_program_id(mProgramId);
|
hdr->set_program_id(mProgramId);
|
||||||
hdr->set_unk0(mUnk0);
|
hdr->set_content_index(mContentIndex);
|
||||||
|
hdr->set_sdk_addon_version(mSdkAddonVersion);
|
||||||
|
|
||||||
// TODO: properly reconstruct NCA layout? atm in hands of user
|
// TODO: properly reconstruct NCA layout? atm in hands of user
|
||||||
|
|
||||||
|
@ -24,13 +28,13 @@ void NcaHeader::exportBinary()
|
||||||
|
|
||||||
hdr->section(section).set_start(sizeToBlockNum(mSections[i].offset));
|
hdr->section(section).set_start(sizeToBlockNum(mSections[i].offset));
|
||||||
hdr->section(section).set_end(sizeToBlockNum(mSections[i].offset) + sizeToBlockNum(mSections[i].size));
|
hdr->section(section).set_end(sizeToBlockNum(mSections[i].offset) + sizeToBlockNum(mSections[i].size));
|
||||||
hdr->section(section).set_key_type(mSections[i].key_type);
|
hdr->section(section).set_enabled(1);
|
||||||
hdr->section_hash(section) = mSections[i].hash;
|
hdr->section_hash(section) = mSections[i].hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < kAesKeyNum; i++)
|
for (size_t i = 0; i < kAesKeyNum; i++)
|
||||||
{
|
{
|
||||||
hdr->aes_key(i) = mAesKeys[i];
|
hdr->enc_aes_key(i) = mEncAesKeys[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,10 +57,14 @@ void NcaHeader::importBinary(const u8 * bytes, size_t len)
|
||||||
throw fnd::Exception(kModuleName, "NCA header corrupt");
|
throw fnd::Exception(kModuleName, "NCA header corrupt");
|
||||||
}
|
}
|
||||||
|
|
||||||
mBlockSize = hdr->block_size();
|
mDistributionType = (DistributionType)hdr->distribution_type();
|
||||||
|
mContentType = (ContentType)hdr->content_type();
|
||||||
|
mEncryptionType = (EncryptionType)hdr->key_generation();
|
||||||
|
mKeyIndex = (EncryptionKeyIndex)hdr->key_area_encryption_key_index();
|
||||||
mNcaSize = hdr->nca_size();
|
mNcaSize = hdr->nca_size();
|
||||||
mProgramId = hdr->program_id();
|
mProgramId = hdr->program_id();
|
||||||
mUnk0 = hdr->unk0();
|
mContentIndex = hdr->content_index();
|
||||||
|
mSdkAddonVersion = hdr->sdk_addon_version();
|
||||||
|
|
||||||
for (size_t i = 0; i < kSectionNum; i++)
|
for (size_t i = 0; i < kSectionNum; i++)
|
||||||
{
|
{
|
||||||
|
@ -66,24 +74,81 @@ void NcaHeader::importBinary(const u8 * bytes, size_t len)
|
||||||
// skip sections that don't exist
|
// skip sections that don't exist
|
||||||
if (hdr->section(section).start() == 0 && hdr->section(section).end() == 0) continue;
|
if (hdr->section(section).start() == 0 && hdr->section(section).end() == 0) continue;
|
||||||
|
|
||||||
|
EncryptionType encType = mEncryptionType;
|
||||||
|
if (encType == CRYPT_AUTO)
|
||||||
|
{
|
||||||
|
if (mContentType == TYPE_PROGRAM && section == SECTION_LOGO)
|
||||||
|
{
|
||||||
|
encType = CRYPT_NONE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
encType = CRYPT_AESCTR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// add high level struct
|
// add high level struct
|
||||||
mSections.addElement({ blockNumToSize(hdr->section(section).start()), blockNumToSize(hdr->section(section).end() - hdr->section(section).start()), hdr->section(section).key_type(), hdr->section_hash(section) });
|
mSections.addElement({ blockNumToSize(hdr->section(section).start()), blockNumToSize(hdr->section(section).end() - hdr->section(section).start()), encType, hdr->section_hash(section) });
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < kAesKeyNum; i++)
|
for (size_t i = 0; i < kAesKeyNum; i++)
|
||||||
{
|
{
|
||||||
mAesKeys.addElement(hdr->aes_key(i));
|
mEncAesKeys.addElement(hdr->enc_aes_key(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void nx::NcaHeader::clear()
|
void nx::NcaHeader::clear()
|
||||||
{
|
{
|
||||||
mBlockSize = 0;
|
mDistributionType = DIST_DOWNLOAD;
|
||||||
|
mContentType = TYPE_PROGRAM;
|
||||||
|
mEncryptionType = CRYPT_AUTO;
|
||||||
|
mKeyIndex = KEY_DEFAULT;
|
||||||
mNcaSize = 0;
|
mNcaSize = 0;
|
||||||
mProgramId = 0;
|
mProgramId = 0;
|
||||||
mUnk0 = 0;
|
mContentIndex = 0;
|
||||||
|
mSdkAddonVersion = 0;
|
||||||
mSections.clear();
|
mSections.clear();
|
||||||
mAesKeys.clear();
|
mEncAesKeys.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
nx::NcaHeader::DistributionType nx::NcaHeader::getDistributionType() const
|
||||||
|
{
|
||||||
|
return mDistributionType;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nx::NcaHeader::setDistributionType(DistributionType type)
|
||||||
|
{
|
||||||
|
mDistributionType = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
nx::NcaHeader::ContentType nx::NcaHeader::getContentType() const
|
||||||
|
{
|
||||||
|
return mContentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nx::NcaHeader::setContentType(ContentType type)
|
||||||
|
{
|
||||||
|
mContentType = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
nx::NcaHeader::EncryptionType nx::NcaHeader::getEncryptionType() const
|
||||||
|
{
|
||||||
|
return mEncryptionType;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nx::NcaHeader::setEncryptionType(EncryptionType type)
|
||||||
|
{
|
||||||
|
mEncryptionType = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
nx::NcaHeader::EncryptionKeyIndex nx::NcaHeader::getKeyIndex() const
|
||||||
|
{
|
||||||
|
return mKeyIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nx::NcaHeader::setKeyIndex(EncryptionKeyIndex index)
|
||||||
|
{
|
||||||
|
mKeyIndex = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 NcaHeader::getNcaSize() const
|
u64 NcaHeader::getNcaSize() const
|
||||||
|
@ -106,9 +171,24 @@ void NcaHeader::setProgramId(u64 program_id)
|
||||||
mProgramId = program_id;
|
mProgramId = program_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 NcaHeader::getUnk() const
|
u32 nx::NcaHeader::getContentIndex() const
|
||||||
{
|
{
|
||||||
return mUnk0;
|
return mContentIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nx::NcaHeader::setContentIndex(u32 index)
|
||||||
|
{
|
||||||
|
mContentIndex = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 nx::NcaHeader::getSdkAddonVersion() const
|
||||||
|
{
|
||||||
|
return mSdkAddonVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nx::NcaHeader::setSdkAddonVersion(u32 version)
|
||||||
|
{
|
||||||
|
mSdkAddonVersion = version;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fnd::List<NcaHeader::sSection>& NcaHeader::getSections() const
|
const fnd::List<NcaHeader::sSection>& NcaHeader::getSections() const
|
||||||
|
@ -125,39 +205,43 @@ void NcaHeader::addSection(const sSection & section)
|
||||||
mSections.addElement(section);
|
mSections.addElement(section);
|
||||||
}
|
}
|
||||||
|
|
||||||
const fnd::List<crypto::aes::sAes128Key>& NcaHeader::getAesKeys() const
|
const fnd::List<crypto::aes::sAes128Key>& NcaHeader::getEncAesKeys() const
|
||||||
{
|
{
|
||||||
return mAesKeys;
|
return mEncAesKeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NcaHeader::addKey(const crypto::aes::sAes128Key & key)
|
void NcaHeader::addEncAesKey(const crypto::aes::sAes128Key & key)
|
||||||
{
|
{
|
||||||
if (mAesKeys.getSize() >= kAesKeyNum)
|
if (mEncAesKeys.getSize() >= kAesKeyNum)
|
||||||
{
|
{
|
||||||
throw fnd::Exception(kModuleName, "Too many NCA aes keys");
|
throw fnd::Exception(kModuleName, "Too many NCA aes keys");
|
||||||
}
|
}
|
||||||
|
|
||||||
mAesKeys.addElement(key);
|
mEncAesKeys.addElement(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
u64 NcaHeader::blockNumToSize(u32 block_num) const
|
u64 NcaHeader::blockNumToSize(u32 block_num) const
|
||||||
{
|
{
|
||||||
return block_num*mBlockSize;
|
return block_num*kBlockSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 NcaHeader::sizeToBlockNum(u64 real_size) const
|
u32 NcaHeader::sizeToBlockNum(u64 real_size) const
|
||||||
{
|
{
|
||||||
return align(real_size, mBlockSize)/mBlockSize;
|
return align(real_size, kBlockSize)/kBlockSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NcaHeader::isEqual(const NcaHeader & other) const
|
bool NcaHeader::isEqual(const NcaHeader & other) const
|
||||||
{
|
{
|
||||||
return (mBlockSize == other.mBlockSize) \
|
return (mDistributionType == other.mDistributionType) \
|
||||||
|
&& (mContentType == other.mContentType) \
|
||||||
|
&& (mEncryptionType == other.mEncryptionType) \
|
||||||
|
&& (mKeyIndex == other.mKeyIndex) \
|
||||||
&& (mNcaSize == other.mNcaSize) \
|
&& (mNcaSize == other.mNcaSize) \
|
||||||
&& (mProgramId == other.mProgramId) \
|
&& (mProgramId == other.mProgramId) \
|
||||||
&& (mUnk0 == other.mUnk0) \
|
&& (mContentIndex == other.mContentIndex) \
|
||||||
|
&& (mSdkAddonVersion == other.mSdkAddonVersion) \
|
||||||
&& (mSections == other.mSections) \
|
&& (mSections == other.mSections) \
|
||||||
&& (mAesKeys == other.mAesKeys);
|
&& (mEncAesKeys == other.mEncAesKeys);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NcaHeader::copyFrom(const NcaHeader & other)
|
void NcaHeader::copyFrom(const NcaHeader & other)
|
||||||
|
@ -168,13 +252,17 @@ void NcaHeader::copyFrom(const NcaHeader & other)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
this->mBinaryBlob.clear();
|
mBinaryBlob.clear();
|
||||||
mBlockSize = other.mBlockSize;
|
mDistributionType = other.mDistributionType;
|
||||||
|
mContentType = other.mContentType;
|
||||||
|
mEncryptionType = other.mEncryptionType;
|
||||||
|
mKeyIndex = other.mKeyIndex;
|
||||||
mNcaSize = other.mNcaSize;
|
mNcaSize = other.mNcaSize;
|
||||||
mProgramId = other.mProgramId;
|
mProgramId = other.mProgramId;
|
||||||
mUnk0 = other.mUnk0;
|
mContentIndex = other.mContentIndex;
|
||||||
|
mSdkAddonVersion = other.mSdkAddonVersion;
|
||||||
mSections = other.mSections;
|
mSections = other.mSections;
|
||||||
mAesKeys = other.mAesKeys;
|
mEncAesKeys = other.mEncAesKeys;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,18 +12,48 @@ namespace nx
|
||||||
class NcaHeader : public ISerialiseableBinary
|
class NcaHeader : public ISerialiseableBinary
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
enum DistributionType
|
||||||
|
{
|
||||||
|
DIST_DOWNLOAD,
|
||||||
|
DIST_GAME_CARD
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ContentType
|
||||||
|
{
|
||||||
|
TYPE_PROGRAM,
|
||||||
|
TYPE_META,
|
||||||
|
TYPE_CONTROL,
|
||||||
|
TYPE_MANUAL,
|
||||||
|
TYPE_DATA,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum EncryptionType
|
||||||
|
{
|
||||||
|
CRYPT_AUTO,
|
||||||
|
CRYPT_NONE,
|
||||||
|
CRYPT_AESCTR = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
enum EncryptionKeyIndex
|
||||||
|
{
|
||||||
|
KEY_UNUSED_0,
|
||||||
|
KEY_UNUSED_1,
|
||||||
|
KEY_DEFAULT,
|
||||||
|
KEY_UNUSED_3,
|
||||||
|
};
|
||||||
|
|
||||||
struct sSection
|
struct sSection
|
||||||
{
|
{
|
||||||
u64 offset;
|
u64 offset;
|
||||||
u64 size;
|
u64 size;
|
||||||
u8 key_type;
|
EncryptionType enc_type;
|
||||||
crypto::sha::sSha256Hash hash;
|
crypto::sha::sSha256Hash hash;
|
||||||
|
|
||||||
const sSection& operator=(const sSection& other)
|
const sSection& operator=(const sSection& other)
|
||||||
{
|
{
|
||||||
offset = other.offset;
|
offset = other.offset;
|
||||||
size = other.size;
|
size = other.size;
|
||||||
key_type = other.key_type;
|
enc_type = other.enc_type;
|
||||||
hash = other.hash;
|
hash = other.hash;
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
|
@ -33,7 +63,7 @@ namespace nx
|
||||||
{
|
{
|
||||||
return (offset == other.offset) \
|
return (offset == other.offset) \
|
||||||
&& (size == other.size) \
|
&& (size == other.size) \
|
||||||
&& (key_type == other.key_type) \
|
&& (enc_type == other.enc_type) \
|
||||||
&& (hash == other.hash);
|
&& (hash == other.hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,7 +73,7 @@ namespace nx
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static const size_t kDefaultBlockSize = 0x200;
|
static const size_t kBlockSize = 0x200;
|
||||||
|
|
||||||
NcaHeader();
|
NcaHeader();
|
||||||
NcaHeader(const NcaHeader& other);
|
NcaHeader(const NcaHeader& other);
|
||||||
|
@ -63,21 +93,40 @@ namespace nx
|
||||||
|
|
||||||
// variables
|
// variables
|
||||||
void clear();
|
void clear();
|
||||||
|
DistributionType getDistributionType() const;
|
||||||
|
void setDistributionType(DistributionType type);
|
||||||
|
ContentType getContentType() const;
|
||||||
|
void setContentType(ContentType type);
|
||||||
|
EncryptionType getEncryptionType() const;
|
||||||
|
void setEncryptionType(EncryptionType type);
|
||||||
|
EncryptionKeyIndex getKeyIndex() const;
|
||||||
|
void setKeyIndex(EncryptionKeyIndex index);
|
||||||
u64 getNcaSize() const;
|
u64 getNcaSize() const;
|
||||||
void setNcaSize(u64 size);
|
void setNcaSize(u64 size);
|
||||||
u64 getProgramId() const;
|
u64 getProgramId() const;
|
||||||
void setProgramId(u64 program_id);
|
void setProgramId(u64 program_id);
|
||||||
u32 getUnk() const;
|
u32 getContentIndex() const;
|
||||||
|
void setContentIndex(u32 index);
|
||||||
|
u32 getSdkAddonVersion() const;
|
||||||
|
void setSdkAddonVersion(u32 version);
|
||||||
const fnd::List<sSection>& getSections() const;
|
const fnd::List<sSection>& getSections() const;
|
||||||
void addSection(const sSection& section);
|
void addSection(const sSection& section);
|
||||||
const fnd::List<crypto::aes::sAes128Key>& getAesKeys() const;
|
const fnd::List<crypto::aes::sAes128Key>& getEncAesKeys() const;
|
||||||
void addKey(const crypto::aes::sAes128Key& key);
|
void addEncAesKey(const crypto::aes::sAes128Key& key);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const std::string kModuleName = "NCA_HEADER";
|
const std::string kModuleName = "NCA_HEADER";
|
||||||
const std::string kNcaSig = "NCA2";
|
const std::string kNcaSig = "NCA2";
|
||||||
static const size_t kSectionNum = 4;
|
static const size_t kSectionNum = 4;
|
||||||
static const size_t kAesKeyNum = 4;
|
static const size_t kAesKeyNum = 4;
|
||||||
|
static const u32 kDefaultSdkAddonVersion = 721920;
|
||||||
|
|
||||||
|
enum ProgramPartitionId
|
||||||
|
{
|
||||||
|
SECTION_CODE,
|
||||||
|
SECTION_DATA,
|
||||||
|
SECTION_LOGO,
|
||||||
|
};
|
||||||
|
|
||||||
#pragma pack (push, 1)
|
#pragma pack (push, 1)
|
||||||
|
|
||||||
|
@ -85,19 +134,21 @@ namespace nx
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
u8 signature_[4];
|
u8 signature_[4];
|
||||||
u8 reserved_0[2];
|
u8 distribution_type_;
|
||||||
u16 block_size_;
|
u8 content_type_;
|
||||||
|
u8 key_generation_;
|
||||||
|
u8 key_area_encryption_key_index_;
|
||||||
u64 nca_size_;
|
u64 nca_size_;
|
||||||
u64 program_id_;
|
u64 program_id_;
|
||||||
u8 reserved_1[4];
|
u32 content_index_;
|
||||||
u32 unk_0_;
|
u32 sdk_addon_version_;
|
||||||
u8 reserved_2[0x20];
|
u8 reserved_2[0x20];
|
||||||
struct sNcaSection
|
struct sNcaSection
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
u32 start_; // block units
|
u32 start_; // block units
|
||||||
u32 end_; // block units
|
u32 end_; // block units
|
||||||
u8 key_type_;
|
u8 enabled_;
|
||||||
u8 reserved[7];
|
u8 reserved[7];
|
||||||
public:
|
public:
|
||||||
u32 start() const { return le_word(start_); }
|
u32 start() const { return le_word(start_); }
|
||||||
|
@ -106,17 +157,26 @@ namespace nx
|
||||||
u32 end() const { return le_word(end_); }
|
u32 end() const { return le_word(end_); }
|
||||||
void set_end(u32 offset) { end_ = le_word(offset); }
|
void set_end(u32 offset) { end_ = le_word(offset); }
|
||||||
|
|
||||||
u8 key_type() const { return key_type_; }
|
u8 enabled() const { return enabled_; }
|
||||||
void set_key_type(u8 key_type) { key_type_ = key_type; }
|
void set_enabled(u8 is_enabled) { enabled_ = is_enabled; }
|
||||||
} section_[kSectionNum];
|
} section_[kSectionNum];
|
||||||
crypto::sha::sSha256Hash section_hash_[kSectionNum];
|
crypto::sha::sSha256Hash section_hash_[kSectionNum];
|
||||||
crypto::aes::sAes128Key aes_keys_[kAesKeyNum];
|
crypto::aes::sAes128Key enc_aes_keys_[kAesKeyNum];
|
||||||
public:
|
public:
|
||||||
const char* signature() const { return (const char*)signature_; }
|
const char* signature() const { return (const char*)signature_; }
|
||||||
void set_signature(const char* signature) { memcpy(signature_, signature, 4); }
|
void set_signature(const char* signature) { memcpy(signature_, signature, 4); }
|
||||||
|
|
||||||
u16 block_size() const { return le_hword(block_size_); }
|
u8 distribution_type() const { return distribution_type_; }
|
||||||
void set_block_size(u16 block_size) { block_size_ = le_hword(block_size); }
|
void set_distribution_type(u8 type) { distribution_type_ = type; }
|
||||||
|
|
||||||
|
u8 content_type() const { return content_type_; }
|
||||||
|
void set_content_type(u8 type) { content_type_ = type; }
|
||||||
|
|
||||||
|
u8 key_generation() const { return key_generation_; }
|
||||||
|
void set_key_generation(u8 type) { key_generation_ = type; }
|
||||||
|
|
||||||
|
u8 key_area_encryption_key_index() const { return key_area_encryption_key_index_; }
|
||||||
|
void set_key_area_encryption_key_index(u8 index) { key_area_encryption_key_index_ = index; }
|
||||||
|
|
||||||
u64 nca_size() const { return le_dword(nca_size_); }
|
u64 nca_size() const { return le_dword(nca_size_); }
|
||||||
void set_nca_size(u64 nca_size) { nca_size_ = le_dword(nca_size); }
|
void set_nca_size(u64 nca_size) { nca_size_ = le_dword(nca_size); }
|
||||||
|
@ -124,8 +184,11 @@ namespace nx
|
||||||
u64 program_id() const { return le_dword(program_id_); }
|
u64 program_id() const { return le_dword(program_id_); }
|
||||||
void set_program_id(u64 program_id) { program_id_ = le_dword(program_id); }
|
void set_program_id(u64 program_id) { program_id_ = le_dword(program_id); }
|
||||||
|
|
||||||
u32 unk0() const { return le_word(unk_0_); }
|
u32 content_index() const { return le_word(content_index_); }
|
||||||
void set_unk0(u32 val) { unk_0_ = le_word(val); }
|
void set_content_index(u32 index) { content_index_ = le_word(index); }
|
||||||
|
|
||||||
|
u32 sdk_addon_version() const { return le_word(sdk_addon_version_); }
|
||||||
|
void set_sdk_addon_version(u32 version) { sdk_addon_version_ = le_word(version); }
|
||||||
|
|
||||||
const sNcaSection& section(u8 index) const { return section_[index%kSectionNum]; }
|
const sNcaSection& section(u8 index) const { return section_[index%kSectionNum]; }
|
||||||
sNcaSection& section(u8 index) { return section_[index%kSectionNum]; }
|
sNcaSection& section(u8 index) { return section_[index%kSectionNum]; }
|
||||||
|
@ -133,8 +196,8 @@ namespace nx
|
||||||
const crypto::sha::sSha256Hash& section_hash(u8 index) const { return section_hash_[index%kSectionNum]; }
|
const crypto::sha::sSha256Hash& section_hash(u8 index) const { return section_hash_[index%kSectionNum]; }
|
||||||
crypto::sha::sSha256Hash& section_hash(u8 index) { return section_hash_[index%kSectionNum]; }
|
crypto::sha::sSha256Hash& section_hash(u8 index) { return section_hash_[index%kSectionNum]; }
|
||||||
|
|
||||||
const crypto::aes::sAes128Key& aes_key(u8 index) const { return aes_keys_[index%kAesKeyNum]; }
|
const crypto::aes::sAes128Key& enc_aes_key(u8 index) const { return enc_aes_keys_[index%kAesKeyNum]; }
|
||||||
crypto::aes::sAes128Key& aes_key(u8 index) { return aes_keys_[index%kAesKeyNum]; }
|
crypto::aes::sAes128Key& enc_aes_key(u8 index) { return enc_aes_keys_[index%kAesKeyNum]; }
|
||||||
};
|
};
|
||||||
#pragma pack (pop)
|
#pragma pack (pop)
|
||||||
|
|
||||||
|
@ -142,12 +205,16 @@ namespace nx
|
||||||
fnd::MemoryBlob mBinaryBlob;
|
fnd::MemoryBlob mBinaryBlob;
|
||||||
|
|
||||||
// data
|
// data
|
||||||
u16 mBlockSize;
|
DistributionType mDistributionType;
|
||||||
|
ContentType mContentType;
|
||||||
|
EncryptionType mEncryptionType;
|
||||||
|
EncryptionKeyIndex mKeyIndex;
|
||||||
u64 mNcaSize;
|
u64 mNcaSize;
|
||||||
u64 mProgramId;
|
u64 mProgramId;
|
||||||
u32 mUnk0;
|
u32 mContentIndex;
|
||||||
|
u32 mSdkAddonVersion;
|
||||||
fnd::List<sSection> mSections;
|
fnd::List<sSection> mSections;
|
||||||
fnd::List<crypto::aes::sAes128Key> mAesKeys;
|
fnd::List<crypto::aes::sAes128Key> mEncAesKeys;
|
||||||
|
|
||||||
u64 blockNumToSize(u32 block_num) const;
|
u64 blockNumToSize(u32 block_num) const;
|
||||||
u32 sizeToBlockNum(u64 real_size) const;
|
u32 sizeToBlockNum(u64 real_size) const;
|
||||||
|
|
|
@ -10,7 +10,6 @@ namespace nx
|
||||||
public nx::ISerialiseableBinary
|
public nx::ISerialiseableBinary
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// move these enums to NpdmBinary?
|
|
||||||
enum InstructionType
|
enum InstructionType
|
||||||
{
|
{
|
||||||
INSTR_32BIT,
|
INSTR_32BIT,
|
||||||
|
|
170
lib/nx/PfsHeader.cpp
Normal file
170
lib/nx/PfsHeader.cpp
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
#include "PfsHeader.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
nx::PfsHeader::PfsHeader()
|
||||||
|
{}
|
||||||
|
|
||||||
|
nx::PfsHeader::PfsHeader(const PfsHeader & other)
|
||||||
|
{
|
||||||
|
copyFrom(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
nx::PfsHeader::PfsHeader(const u8 * bytes, size_t len)
|
||||||
|
{
|
||||||
|
importBinary(bytes, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nx::PfsHeader::exportBinary()
|
||||||
|
{
|
||||||
|
// calculate name table size
|
||||||
|
size_t name_table_size = 0;
|
||||||
|
for (size_t i = 0; i < mFileList.getSize(); i++)
|
||||||
|
{
|
||||||
|
name_table_size += mFileList[i].name.length() + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t pfs_header_size = align(sizeof(sPfsHeader) + sizeof(sPfsFile) * mFileList.getSize() + name_table_size, kPfsAlign);
|
||||||
|
|
||||||
|
// align name_table_size
|
||||||
|
name_table_size = pfs_header_size - (sizeof(sPfsHeader) + sizeof(sPfsFile) * mFileList.getSize());
|
||||||
|
|
||||||
|
// allocate pfs header binary
|
||||||
|
mBinaryBlob.alloc(pfs_header_size);
|
||||||
|
sPfsHeader* hdr = (sPfsHeader*)mBinaryBlob.getBytes();
|
||||||
|
|
||||||
|
// set header fields
|
||||||
|
hdr->set_signature(kPfsStructSig.c_str());
|
||||||
|
hdr->set_file_num(mFileList.getSize());
|
||||||
|
hdr->set_name_table_size(name_table_size);
|
||||||
|
|
||||||
|
// set file entries
|
||||||
|
sPfsFile* raw_files = (sPfsFile*)(mBinaryBlob.getBytes() + sizeof(sPfsHeader));
|
||||||
|
char* raw_name_table = (char*)(mBinaryBlob.getBytes() + sizeof(sPfsHeader) + sizeof(sPfsFile) * mFileList.getSize());
|
||||||
|
size_t raw_name_table_pos = 0;
|
||||||
|
|
||||||
|
calculateOffsets(pfs_header_size);
|
||||||
|
for (size_t i = 0; i < mFileList.getSize(); i++)
|
||||||
|
{
|
||||||
|
raw_files[i].set_offset(mFileList[i].offset - pfs_header_size);
|
||||||
|
raw_files[i].set_size(mFileList[i].size);
|
||||||
|
raw_files[i].set_name_offset(raw_name_table_pos);
|
||||||
|
|
||||||
|
strcpy(raw_name_table + raw_name_table_pos, mFileList[i].name.c_str());
|
||||||
|
raw_name_table_pos += mFileList[i].name.length() + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void nx::PfsHeader::importBinary(const u8 * bytes, size_t len)
|
||||||
|
{
|
||||||
|
// check input length meets minimum size
|
||||||
|
if (len < sizeof(sPfsHeader))
|
||||||
|
{
|
||||||
|
throw fnd::Exception(kModuleName, "PFS header too small");
|
||||||
|
}
|
||||||
|
|
||||||
|
// import minimum header
|
||||||
|
mBinaryBlob.alloc(sizeof(sPfsHeader));
|
||||||
|
memcpy(mBinaryBlob.getBytes(), bytes, mBinaryBlob.getSize());
|
||||||
|
const sPfsHeader* hdr = (const sPfsHeader*)mBinaryBlob.getBytes();
|
||||||
|
|
||||||
|
// check struct signature
|
||||||
|
if (memcmp(hdr->signature(), kPfsStructSig.c_str(), 4) != 0)
|
||||||
|
{
|
||||||
|
throw fnd::Exception(kModuleName, "PFS header corrupt");
|
||||||
|
}
|
||||||
|
|
||||||
|
// determine complete header size
|
||||||
|
size_t pfs_full_header_size = sizeof(sPfsHeader) + sizeof(sPfsFile) * hdr->file_num() + hdr->name_table_size();
|
||||||
|
|
||||||
|
// check input length meets complete size
|
||||||
|
if (len < pfs_full_header_size)
|
||||||
|
{
|
||||||
|
throw fnd::Exception(kModuleName, "PFS header too small");
|
||||||
|
}
|
||||||
|
|
||||||
|
// import full header
|
||||||
|
mBinaryBlob.alloc(pfs_full_header_size);
|
||||||
|
memcpy(mBinaryBlob.getBytes(), bytes, mBinaryBlob.getSize());
|
||||||
|
hdr = (const sPfsHeader*)mBinaryBlob.getBytes();
|
||||||
|
|
||||||
|
// clear variables
|
||||||
|
clear();
|
||||||
|
|
||||||
|
// get pointers to raw data
|
||||||
|
const sPfsFile* raw_files = (const sPfsFile*)(mBinaryBlob.getBytes() + sizeof(sPfsHeader));
|
||||||
|
const char* raw_name_table = (const char*)(mBinaryBlob.getBytes() + sizeof(sPfsHeader) + sizeof(sPfsFile) * hdr->file_num());
|
||||||
|
|
||||||
|
// process file entries
|
||||||
|
for (size_t i = 0; i < hdr->file_num(); i++)
|
||||||
|
{
|
||||||
|
mFileList.addElement({ std::string(raw_name_table + raw_files[i].name_offset()), raw_files[i].offset() + pfs_full_header_size, raw_files[i].size() });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void nx::PfsHeader::clear()
|
||||||
|
{
|
||||||
|
mBinaryBlob.clear();
|
||||||
|
mFileList.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
const fnd::List<nx::PfsHeader::sFile>& nx::PfsHeader::getFileList() const
|
||||||
|
{
|
||||||
|
return mFileList;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nx::PfsHeader::addFile(const std::string & name, size_t size)
|
||||||
|
{
|
||||||
|
mFileList.addElement({ name, 0, size });
|
||||||
|
}
|
||||||
|
|
||||||
|
void nx::PfsHeader::calculateOffsets(size_t data_offset)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < mFileList.getSize(); i++)
|
||||||
|
{
|
||||||
|
mFileList[i].offset = (i == 0) ? data_offset : mFileList[i - 1].offset + mFileList[i - 1].size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nx::PfsHeader::isEqual(const PfsHeader & other) const
|
||||||
|
{
|
||||||
|
return mFileList == other.mFileList;
|
||||||
|
}
|
||||||
|
|
||||||
|
void nx::PfsHeader::copyFrom(const PfsHeader & other)
|
||||||
|
{
|
||||||
|
if (other.getSize())
|
||||||
|
{
|
||||||
|
importBinary(other.getBytes(), other.getSize());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
mFileList = other.mFileList;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nx::PfsHeader::operator==(const PfsHeader & other) const
|
||||||
|
{
|
||||||
|
return isEqual(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nx::PfsHeader::operator!=(const PfsHeader & other) const
|
||||||
|
{
|
||||||
|
return !isEqual(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
void nx::PfsHeader::operator=(const PfsHeader & other)
|
||||||
|
{
|
||||||
|
copyFrom(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
const u8 * nx::PfsHeader::getBytes() const
|
||||||
|
{
|
||||||
|
return mBinaryBlob.getBytes();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t nx::PfsHeader::getSize() const
|
||||||
|
{
|
||||||
|
return mBinaryBlob.getSize();
|
||||||
|
}
|
126
lib/nx/PfsHeader.h
Normal file
126
lib/nx/PfsHeader.h
Normal file
|
@ -0,0 +1,126 @@
|
||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <fnd/types.h>
|
||||||
|
#include <fnd/memory_blob.h>
|
||||||
|
#include <fnd/List.h>
|
||||||
|
#include <nx/ISerialiseableBinary.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace nx
|
||||||
|
{
|
||||||
|
class PfsHeader :
|
||||||
|
public ISerialiseableBinary
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct sFile
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
size_t offset;
|
||||||
|
size_t size;
|
||||||
|
|
||||||
|
sFile& operator=(const sFile& other)
|
||||||
|
{
|
||||||
|
name = other.name;
|
||||||
|
offset = other.offset;
|
||||||
|
size = other.size;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const sFile& other) const
|
||||||
|
{
|
||||||
|
return (name == other.name) \
|
||||||
|
&& (offset == other.offset) \
|
||||||
|
&& (size == other.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const sFile& other) const
|
||||||
|
{
|
||||||
|
return !operator==(other);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const std::string& other) const
|
||||||
|
{
|
||||||
|
return (name == other);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const std::string& other) const
|
||||||
|
{
|
||||||
|
return !operator==(other);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
PfsHeader();
|
||||||
|
PfsHeader(const PfsHeader& other);
|
||||||
|
PfsHeader(const u8* bytes, size_t len);
|
||||||
|
|
||||||
|
bool operator==(const PfsHeader& other) const;
|
||||||
|
bool operator!=(const PfsHeader& other) const;
|
||||||
|
void operator=(const PfsHeader& other);
|
||||||
|
|
||||||
|
// to be used after export
|
||||||
|
const u8* getBytes() const;
|
||||||
|
size_t getSize() const;
|
||||||
|
|
||||||
|
// export/import binary
|
||||||
|
void exportBinary();
|
||||||
|
void importBinary(const u8* bytes, size_t len);
|
||||||
|
|
||||||
|
// variables
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
const fnd::List<sFile>& getFileList() const;
|
||||||
|
void addFile(const std::string& name, size_t size);
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::string kModuleName = "PFS_HEADER";
|
||||||
|
const std::string kPfsStructSig = "PFS0";
|
||||||
|
static const size_t kPfsAlign = 0x40;
|
||||||
|
|
||||||
|
#pragma pack (push, 1)
|
||||||
|
struct sPfsFile
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
u64 data_offset_;
|
||||||
|
u64 size_;
|
||||||
|
u64 name_offset_;
|
||||||
|
public:
|
||||||
|
u64 offset() const { return le_dword(data_offset_); }
|
||||||
|
void set_offset(u64 offset) { data_offset_ = le_dword(offset); }
|
||||||
|
|
||||||
|
u64 size() const { return le_dword(size_); }
|
||||||
|
void set_size(u64 size) { size_ = le_dword(size); }
|
||||||
|
|
||||||
|
u64 name_offset() const { return le_dword(name_offset_); }
|
||||||
|
void set_name_offset(u64 offset) { name_offset_ = le_dword(offset); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sPfsHeader
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
u8 signature_[4];
|
||||||
|
u32 file_num_;
|
||||||
|
u64 name_table_size_;
|
||||||
|
public:
|
||||||
|
const char* signature() const { return (const char*)signature_; }
|
||||||
|
void set_signature(const char* signature) { memcpy(signature_, signature, 4); }
|
||||||
|
|
||||||
|
u32 file_num() const { return le_word(file_num_); }
|
||||||
|
void set_file_num(u32 file_num) { file_num_ = le_word(file_num); }
|
||||||
|
|
||||||
|
u64 name_table_size() const { return le_dword(name_table_size_); }
|
||||||
|
void set_name_table_size(u64 size) { name_table_size_ = le_dword(size); }
|
||||||
|
};
|
||||||
|
#pragma pack (pop)
|
||||||
|
|
||||||
|
// binary blob
|
||||||
|
fnd::MemoryBlob mBinaryBlob;
|
||||||
|
|
||||||
|
// variables
|
||||||
|
fnd::List<sFile> mFileList;
|
||||||
|
|
||||||
|
void calculateOffsets(size_t data_offset);
|
||||||
|
bool isEqual(const PfsHeader& other) const;
|
||||||
|
void copyFrom(const PfsHeader& other);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
6
lib/nx/XciHeader.cpp
Normal file
6
lib/nx/XciHeader.cpp
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#include "XciHeader.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
nx::XciHeader::XciHeader()
|
||||||
|
{}
|
92
lib/nx/XciHeader.h
Normal file
92
lib/nx/XciHeader.h
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
#pragma once
|
||||||
|
#include <string>
|
||||||
|
#include <fnd/types.h>
|
||||||
|
#include <fnd/memory_blob.h>
|
||||||
|
#include <fnd/List.h>
|
||||||
|
#include <nx/ISerialiseableBinary.h>
|
||||||
|
|
||||||
|
namespace nx
|
||||||
|
{
|
||||||
|
class XciHeader// :
|
||||||
|
//public ISerialiseableBinary
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
XciHeader();
|
||||||
|
|
||||||
|
private:
|
||||||
|
#pragma pack (push, 1)
|
||||||
|
enum ContentMetaType
|
||||||
|
{
|
||||||
|
SYSTEM_PROGRAM = 1,
|
||||||
|
SYSTEM_DATA = 2,
|
||||||
|
SYSTEM_UPDATE = 3,
|
||||||
|
BOOT_IMAGE_PACKAGE = 4,
|
||||||
|
BOOT_IMAGE_PACKAGE_SAFE = 5,
|
||||||
|
APPLICATION = 128,
|
||||||
|
PATCH = 129,
|
||||||
|
ADD_ON_CONTENT = 130
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ContentType
|
||||||
|
{
|
||||||
|
META,
|
||||||
|
PROGRAM,
|
||||||
|
DATA,
|
||||||
|
CONTROL,
|
||||||
|
HTML_DOCUMENT,
|
||||||
|
LEGAL_INFORMATION
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ContentMetaAttribute
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
IncludesExFatDriver = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sContentMetaInfo
|
||||||
|
{
|
||||||
|
u64 id;
|
||||||
|
u32 version;
|
||||||
|
u8 type; // ContentMetaType
|
||||||
|
u8 attributes;
|
||||||
|
u8 reserved[2];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sContentInfo
|
||||||
|
{
|
||||||
|
u8 id[16];
|
||||||
|
u32 size_low;
|
||||||
|
u16 size_high;
|
||||||
|
u8 type;
|
||||||
|
u8 reserved;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sXciHeader
|
||||||
|
{
|
||||||
|
u8 signature[4];
|
||||||
|
u32 rom_area_start_page;
|
||||||
|
u32 backup_area_start_page;
|
||||||
|
u8 key_flag; // bit0-3 = KekIndex, bit4-7 = TitleKeyDecIndex
|
||||||
|
u8 rom_size; // this is an enum
|
||||||
|
u8 flags;
|
||||||
|
u8 package_id[8]; // stylised as 0x{0:x2}{1:x2}{2:x2}{3:x2}_{4:x2}{5:x2}{6:x2}{7:x2}
|
||||||
|
u32 valid_data_end_page;
|
||||||
|
u8 reserved_0[100];
|
||||||
|
u32 sel_sec;
|
||||||
|
u32 sel_t1_key;
|
||||||
|
u32 sel_key;
|
||||||
|
u32 lim_area;
|
||||||
|
u32 fw_version[2]; // [0]=minor, [1]=major
|
||||||
|
u32 acc_ctrl_1;
|
||||||
|
u8 reserved_1[0x10];
|
||||||
|
u32 fw_mode;
|
||||||
|
u32 cup_version;
|
||||||
|
u8 reserved_2[0x4];
|
||||||
|
u8 upp_hash[8]; // stylised as 0x{0:x2}{1:x2}{2:x2}{3:x2}_{4:x2}{5:x2}{6:x2}{7:x2}
|
||||||
|
u64 cup_id; // cup programID?
|
||||||
|
|
||||||
|
};
|
||||||
|
#pragma pack (pop)
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -44,12 +44,14 @@
|
||||||
<ClInclude Include="NpdmHeader.h" />
|
<ClInclude Include="NpdmHeader.h" />
|
||||||
<ClInclude Include="NcaHeader.h" />
|
<ClInclude Include="NcaHeader.h" />
|
||||||
<ClInclude Include="NXCrypto.h" />
|
<ClInclude Include="NXCrypto.h" />
|
||||||
|
<ClInclude Include="PfsHeader.h" />
|
||||||
<ClInclude Include="SacBinary.h" />
|
<ClInclude Include="SacBinary.h" />
|
||||||
<ClInclude Include="SacEntry.h" />
|
<ClInclude Include="SacEntry.h" />
|
||||||
<ClInclude Include="SystemCallEntry.h" />
|
<ClInclude Include="SystemCallEntry.h" />
|
||||||
<ClInclude Include="SystemCallHandler.h" />
|
<ClInclude Include="SystemCallHandler.h" />
|
||||||
<ClInclude Include="ThreadInfoEntry.h" />
|
<ClInclude Include="ThreadInfoEntry.h" />
|
||||||
<ClInclude Include="ThreadInfoHandler.h" />
|
<ClInclude Include="ThreadInfoHandler.h" />
|
||||||
|
<ClInclude Include="XciHeader.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="AciBinary.cpp" />
|
<ClCompile Include="AciBinary.cpp" />
|
||||||
|
@ -74,12 +76,14 @@
|
||||||
<ClCompile Include="NpdmBinary.cpp" />
|
<ClCompile Include="NpdmBinary.cpp" />
|
||||||
<ClCompile Include="NpdmHeader.cpp" />
|
<ClCompile Include="NpdmHeader.cpp" />
|
||||||
<ClCompile Include="NcaHeader.cpp" />
|
<ClCompile Include="NcaHeader.cpp" />
|
||||||
|
<ClCompile Include="PfsHeader.cpp" />
|
||||||
<ClCompile Include="SacBinary.cpp" />
|
<ClCompile Include="SacBinary.cpp" />
|
||||||
<ClCompile Include="SacEntry.cpp" />
|
<ClCompile Include="SacEntry.cpp" />
|
||||||
<ClCompile Include="SystemCallEntry.cpp" />
|
<ClCompile Include="SystemCallEntry.cpp" />
|
||||||
<ClCompile Include="SystemCallHandler.cpp" />
|
<ClCompile Include="SystemCallHandler.cpp" />
|
||||||
<ClCompile Include="ThreadInfoEntry.cpp" />
|
<ClCompile Include="ThreadInfoEntry.cpp" />
|
||||||
<ClCompile Include="ThreadInfoHandler.cpp" />
|
<ClCompile Include="ThreadInfoHandler.cpp" />
|
||||||
|
<ClCompile Include="XciHeader.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<PropertyGroup Label="Globals">
|
<PropertyGroup Label="Globals">
|
||||||
<VCProjectVersion>15.0</VCProjectVersion>
|
<VCProjectVersion>15.0</VCProjectVersion>
|
||||||
|
|
|
@ -108,6 +108,12 @@
|
||||||
<ClInclude Include="NpdmBinary.h">
|
<ClInclude Include="NpdmBinary.h">
|
||||||
<Filter>Header Files</Filter>
|
<Filter>Header Files</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="PfsHeader.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="XciHeader.h">
|
||||||
|
<Filter>Header Files</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="NcaHeader.cpp">
|
<ClCompile Include="NcaHeader.cpp">
|
||||||
|
@ -194,5 +200,11 @@
|
||||||
<ClCompile Include="NpdmBinary.cpp">
|
<ClCompile Include="NpdmBinary.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="PfsHeader.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="XciHeader.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
|
@ -1,4 +1,4 @@
|
||||||
PROGS = ncatool npdmtool
|
PROGS = ncatool npdmtool pfstool
|
||||||
|
|
||||||
main: build
|
main: build
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,14 @@
|
||||||
#include <nx/NXCrypto.h>
|
#include <nx/NXCrypto.h>
|
||||||
#include <nx/NcaHeader.h>
|
#include <nx/NcaHeader.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <direct.h>
|
||||||
|
#else
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const size_t kNcaSectorSize = nx::NcaHeader::kBlockSize;
|
||||||
|
|
||||||
const size_t kNcaSectorSize = nx::NcaHeader::kDefaultBlockSize;
|
|
||||||
|
|
||||||
void initNcaCtr(u8 ctr[crypto::aes::kAesBlockSize], u32 generation)
|
void initNcaCtr(u8 ctr[crypto::aes::kAesBlockSize], u32 generation)
|
||||||
{
|
{
|
||||||
|
@ -25,6 +31,14 @@ void hexDump(const u8* data, size_t len)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void xorData(const u8* a, const u8* b, u8* out, size_t len)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
out[i] = a[i] ^ b[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void decryptNcaSectorXts(const fnd::MemoryBlob& nca, u8 out[kNcaSectorSize], size_t sector, const u8* key1, const u8* key2)
|
void decryptNcaSectorXts(const fnd::MemoryBlob& nca, u8 out[kNcaSectorSize], size_t sector, const u8* key1, const u8* key2)
|
||||||
{
|
{
|
||||||
u8 tweak[crypto::aes::kAesBlockSize];
|
u8 tweak[crypto::aes::kAesBlockSize];
|
||||||
|
@ -49,6 +63,63 @@ void dumpNcaSector(u8 out[kNcaSectorSize])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void dumpHxdStyleSector(u8* out, size_t len)
|
||||||
|
{
|
||||||
|
// iterate over 0x10 blocks
|
||||||
|
for (size_t i = 0; i < (len / crypto::aes::kAesBlockSize); i++)
|
||||||
|
{
|
||||||
|
// for block i print each byte
|
||||||
|
for (size_t j = 0; j < crypto::aes::kAesBlockSize; j++)
|
||||||
|
{
|
||||||
|
printf("%02X ", out[i*crypto::aes::kAesBlockSize + j]);
|
||||||
|
}
|
||||||
|
printf(" ");
|
||||||
|
for (size_t j = 0; j < crypto::aes::kAesBlockSize; j++)
|
||||||
|
{
|
||||||
|
printf("%c", isalnum(out[i*crypto::aes::kAesBlockSize + j]) ? out[i*crypto::aes::kAesBlockSize + j] : '.');
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
for (size_t i = 0; i < len % crypto::aes::kAesBlockSize; i++)
|
||||||
|
{
|
||||||
|
printf("%02X ", out[(len / crypto::aes::kAesBlockSize)*crypto::aes::kAesBlockSize + i]);
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < crypto::aes::kAesBlockSize - (len % crypto::aes::kAesBlockSize); i++)
|
||||||
|
{
|
||||||
|
printf(" ");
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < len % crypto::aes::kAesBlockSize; i++)
|
||||||
|
{
|
||||||
|
printf("%c", out[(len / crypto::aes::kAesBlockSize)*crypto::aes::kAesBlockSize + i]);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string kDistributionTypeStr[]
|
||||||
|
{
|
||||||
|
"Download",
|
||||||
|
"Game Card"
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string kContentTypeStr[]
|
||||||
|
{
|
||||||
|
"Program",
|
||||||
|
"Meta",
|
||||||
|
"Control",
|
||||||
|
"Manual",
|
||||||
|
"Data"
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string kEncryptionTypeStr[]
|
||||||
|
{
|
||||||
|
"Auto",
|
||||||
|
"None",
|
||||||
|
"UNKNOWN_2",
|
||||||
|
"AesCtr"
|
||||||
|
};
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
if (argc < 2)
|
if (argc < 2)
|
||||||
|
@ -65,7 +136,7 @@ int main(int argc, char** argv)
|
||||||
u8 sector[kNcaSectorSize];
|
u8 sector[kNcaSectorSize];
|
||||||
|
|
||||||
// nca test
|
// nca test
|
||||||
if (argc == 2)
|
if (argc == 2 || argc == 3)
|
||||||
{
|
{
|
||||||
decryptNcaSectorXts(nca, sector, 1, crypto::aes::nx::nca_header_key[0], crypto::aes::nx::nca_header_key[1]);
|
decryptNcaSectorXts(nca, sector, 1, crypto::aes::nx::nca_header_key[0], crypto::aes::nx::nca_header_key[1]);
|
||||||
|
|
||||||
|
@ -73,9 +144,14 @@ int main(int argc, char** argv)
|
||||||
hdr.importBinary(sector, kNcaSectorSize);
|
hdr.importBinary(sector, kNcaSectorSize);
|
||||||
|
|
||||||
printf("[NCA Header]\n");
|
printf("[NCA Header]\n");
|
||||||
printf(" Size: 0x%" PRIx64 "\n", hdr.getNcaSize());
|
printf(" Dist. Type: %s\n", kDistributionTypeStr[hdr.getDistributionType()].c_str());
|
||||||
printf(" ProgID: 0x%016" PRIx64 "\n", hdr.getProgramId());
|
printf(" Type: %s\n", kContentTypeStr[hdr.getContentType()].c_str());
|
||||||
printf(" Unk0: 0x%" PRIx32 "\n", hdr.getUnk());
|
printf(" Enc. Type: %s\n", kEncryptionTypeStr[hdr.getEncryptionType()].c_str());
|
||||||
|
printf(" KeyIndex: %d\n", hdr.getKeyIndex());
|
||||||
|
printf(" Size: 0x%" PRIx64 "\n", hdr.getNcaSize());
|
||||||
|
printf(" ProgID: 0x%016" PRIx64 "\n", hdr.getProgramId());
|
||||||
|
printf(" Content. Idx: %" PRIu32 "\n", hdr.getContentIndex());
|
||||||
|
printf(" SdkAddon Ver.: v%" PRIu32 "\n", hdr.getSdkAddonVersion());
|
||||||
printf(" Sections:\n");
|
printf(" Sections:\n");
|
||||||
for (size_t i = 0; i < hdr.getSections().getSize(); i++)
|
for (size_t i = 0; i < hdr.getSections().getSize(); i++)
|
||||||
{
|
{
|
||||||
|
@ -83,23 +159,41 @@ int main(int argc, char** argv)
|
||||||
printf(" %lu:\n", i);
|
printf(" %lu:\n", i);
|
||||||
//printf(" Start Blk: %" PRId32 "\n", section.start_blk);
|
//printf(" Start Blk: %" PRId32 "\n", section.start_blk);
|
||||||
//printf(" End Blk: %" PRId32 "\n", section.end_blk);
|
//printf(" End Blk: %" PRId32 "\n", section.end_blk);
|
||||||
printf(" Offset: 0x%" PRIx64 "\n", section.offset);
|
printf(" Offset: 0x%" PRIx64 "\n", section.offset);
|
||||||
printf(" Size: 0x%" PRIx64 "\n", section.size);
|
printf(" Size: 0x%" PRIx64 "\n", section.size);
|
||||||
printf(" KeyType: 0x%02x\n", section.key_type);
|
printf(" Enc. Type: %s\n", kEncryptionTypeStr[section.enc_type].c_str());
|
||||||
printf(" Hash: ");
|
printf(" Hash: ");
|
||||||
hexDump(section.hash.bytes, crypto::sha::kSha256HashLen);
|
hexDump(section.hash.bytes, crypto::sha::kSha256HashLen);
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
printf(" AES Keys:\n");
|
printf(" Encrypted Body Keys:\n");
|
||||||
for (size_t i = 0; i < hdr.getAesKeys().getSize(); i++)
|
for (size_t i = 0; i < hdr.getEncAesKeys().getSize(); i++)
|
||||||
{
|
{
|
||||||
printf(" %lu: ", i);
|
printf(" %lu: ", i);
|
||||||
hexDump(hdr.getAesKeys()[i].key, crypto::aes::kAes128KeySize);
|
hexDump(hdr.getEncAesKeys()[i].key, crypto::aes::kAes128KeySize);
|
||||||
printf("\n");
|
printf("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (argc == 3)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
_mkdir(argv[2]);
|
||||||
|
#else
|
||||||
|
mkdir(argv[2], S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (size_t i = 0; i < hdr.getSections().getSize(); i++)
|
||||||
|
{
|
||||||
|
const nx::NcaHeader::sSection& section = hdr.getSections()[i];
|
||||||
|
#ifdef _WIN32
|
||||||
|
fnd::io::writeFile(std::string(argv[2]) + "\\" + std::to_string(i) + ".bin" , nca.getBytes() + section.offset, section.size);
|
||||||
|
#else
|
||||||
|
fnd::io::writeFile(std::string(argv[2]) + "/" + std::to_string(i) + ".bin", nca.getBytes() + section.offset, section.size);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (const fnd::Exception& e)
|
} catch (const fnd::Exception& e)
|
||||||
{
|
{
|
||||||
printf("%s\n",e.what());
|
printf("%s\n",e.what());
|
||||||
|
|
|
@ -116,6 +116,9 @@
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="main.cpp" />
|
<ClCompile Include="main.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="makefile" />
|
||||||
|
</ItemGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<ImportGroup Label="ExtensionTargets">
|
<ImportGroup Label="ExtensionTargets">
|
||||||
</ImportGroup>
|
</ImportGroup>
|
||||||
|
|
|
@ -19,4 +19,7 @@
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="makefile" />
|
||||||
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
62
programs/pfstool/main.cpp
Normal file
62
programs/pfstool/main.cpp
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
#include <cstdio>
|
||||||
|
#include <crypto/aes.h>
|
||||||
|
#include <fnd/io.h>
|
||||||
|
#include <fnd/memory_blob.h>
|
||||||
|
#include <nx/NXCrypto.h>
|
||||||
|
#include <nx/PfsHeader.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <direct.h>
|
||||||
|
#else
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
if (argc < 2)
|
||||||
|
{
|
||||||
|
printf("usage: pfstool <file> [<output dir>]\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
fnd::MemoryBlob file;
|
||||||
|
fnd::io::readFile(argv[1], file);
|
||||||
|
|
||||||
|
// import
|
||||||
|
nx::PfsHeader pfs;
|
||||||
|
pfs.importBinary(file.getBytes(), file.getSize());
|
||||||
|
|
||||||
|
if (argc == 3)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
_mkdir(argv[2]);
|
||||||
|
#else
|
||||||
|
mkdir(argv[2], S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("[PFS]\n");
|
||||||
|
for (size_t i = 0; i < pfs.getFileList().getSize(); i++)
|
||||||
|
{
|
||||||
|
printf(" %s (offset=0x%" PRIx64 ", size=0x%" PRIx64 ")\n", pfs.getFileList()[i].name.c_str(), pfs.getFileList()[i].offset, pfs.getFileList()[i].size);
|
||||||
|
if (argc == 3)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
fnd::io::writeFile(std::string(argv[2]) + "\\" + pfs.getFileList()[i].name, file.getBytes() + pfs.getFileList()[i].offset, pfs.getFileList()[i].size);
|
||||||
|
#else
|
||||||
|
fnd::io::writeFile(std::string(argv[2]) + "/" + pfs.getFileList()[i].name, file.getBytes() + pfs.getFileList()[i].offset, pfs.getFileList()[i].size);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} catch (const fnd::Exception& e)
|
||||||
|
{
|
||||||
|
printf("%s\n", e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
39
programs/pfstool/makefile
Normal file
39
programs/pfstool/makefile
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
# Sources
|
||||||
|
SRC_DIR = .
|
||||||
|
OBJS = $(foreach dir,$(SRC_DIR),$(subst .cpp,.o,$(wildcard $(dir)/*.cpp))) $(foreach dir,$(SRC_DIR),$(subst .c,.o,$(wildcard $(dir)/*.c)))
|
||||||
|
|
||||||
|
#local dependencies
|
||||||
|
DEPENDS = nx crypto fnd
|
||||||
|
|
||||||
|
LIB_DIR = ../../lib
|
||||||
|
|
||||||
|
LIBS = -L"$(LIB_DIR)" $(foreach dep,$(DEPENDS), -l"$(dep)")
|
||||||
|
INCS = -I"$(LIB_DIR)/"
|
||||||
|
|
||||||
|
OUTPUT = ../../bin/$(shell basename $(CURDIR))
|
||||||
|
|
||||||
|
# Compiler Settings
|
||||||
|
CXXFLAGS = -std=c++11 $(INCS) -D__STDC_FORMAT_MACROS -Wall -Wno-unused-but-set-variable -Wno-unused-value
|
||||||
|
ifeq ($(OS),Windows_NT)
|
||||||
|
# Windows Only Flags/Libs
|
||||||
|
CC = x86_64-w64-mingw32-gcc
|
||||||
|
CXX = x86_64-w64-mingw32-g++
|
||||||
|
CFLAGS +=
|
||||||
|
CXXFLAGS +=
|
||||||
|
LIBS += -static
|
||||||
|
else
|
||||||
|
# *nix Only Flags/Libs
|
||||||
|
CFLAGS +=
|
||||||
|
CXXFLAGS +=
|
||||||
|
LIBS +=
|
||||||
|
endif
|
||||||
|
|
||||||
|
all: build
|
||||||
|
|
||||||
|
rebuild: clean build
|
||||||
|
|
||||||
|
build: $(OBJS)
|
||||||
|
$(CXX) $(OBJS) $(LIBS) -o $(OUTPUT)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf $(OBJS) $(OUTPUT)
|
125
programs/pfstool/pfstool.vcxproj
Normal file
125
programs/pfstool/pfstool.vcxproj
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup Label="ProjectConfigurations">
|
||||||
|
<ProjectConfiguration Include="Debug|Win32">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|Win32">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Debug|x64">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|x64">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>x64</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
</ItemGroup>
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<ProjectGuid>{BC2F2D07-BAB3-469C-9C25-8CC54F96F7AB}</ProjectGuid>
|
||||||
|
<RootNamespace>pfstool</RootNamespace>
|
||||||
|
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v140</PlatformToolset>
|
||||||
|
<CharacterSet>MultiByte</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v140</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>MultiByte</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v140</PlatformToolset>
|
||||||
|
<CharacterSet>MultiByte</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v140</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>MultiByte</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
|
<ImportGroup Label="ExtensionSettings">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="Shared">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<PropertyGroup Label="UserMacros" />
|
||||||
|
<PropertyGroup />
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<Optimization>Disabled</Optimization>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<AdditionalIncludeDirectories>..\..\lib</AdditionalIncludeDirectories>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<Optimization>Disabled</Optimization>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<AdditionalIncludeDirectories>..\..\lib</AdditionalIncludeDirectories>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<Optimization>MaxSpeed</Optimization>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<AdditionalIncludeDirectories>..\..\lib</AdditionalIncludeDirectories>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<Optimization>MaxSpeed</Optimization>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<SDLCheck>true</SDLCheck>
|
||||||
|
<AdditionalIncludeDirectories>..\..\lib</AdditionalIncludeDirectories>
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="main.cpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="makefile" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
</ImportGroup>
|
||||||
|
</Project>
|
25
programs/pfstool/pfstool.vcxproj.filters
Normal file
25
programs/pfstool/pfstool.vcxproj.filters
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup>
|
||||||
|
<Filter Include="Source Files">
|
||||||
|
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||||
|
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Header Files">
|
||||||
|
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||||
|
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
|
||||||
|
</Filter>
|
||||||
|
<Filter Include="Resource Files">
|
||||||
|
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||||
|
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||||
|
</Filter>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="main.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="makefile" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
Loading…
Reference in a new issue