2017-08-05 13:09:50 +00:00
|
|
|
#include <nx/NcaHeader.h>
|
2017-07-05 08:57:14 +00:00
|
|
|
|
2017-07-06 11:17:21 +00:00
|
|
|
using namespace nx;
|
|
|
|
|
|
|
|
|
2017-07-05 08:57:14 +00:00
|
|
|
void NcaHeader::exportBinary()
|
|
|
|
{
|
2017-07-05 15:38:14 +00:00
|
|
|
mBinaryBlob.alloc(sizeof(sNcaHeader));
|
2017-07-06 03:30:27 +00:00
|
|
|
sNcaHeader* hdr = (sNcaHeader*)mBinaryBlob.getBytes();
|
2017-07-05 15:38:14 +00:00
|
|
|
|
2018-03-18 09:08:42 +00:00
|
|
|
|
|
|
|
switch(mFormatVersion)
|
|
|
|
{
|
|
|
|
case (NCA2_FORMAT):
|
2018-04-07 08:01:11 +00:00
|
|
|
strncpy(hdr->signature, nca::kNca2Sig.c_str(), 4);
|
2018-03-18 09:08:42 +00:00
|
|
|
break;
|
|
|
|
case (NCA3_FORMAT):
|
2018-04-07 08:01:11 +00:00
|
|
|
strncpy(hdr->signature, nca::kNca3Sig.c_str(), 4);
|
2018-03-18 09:08:42 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw fnd::Exception(kModuleName, "Unsupported format version");
|
|
|
|
}
|
2018-03-17 12:01:19 +00:00
|
|
|
hdr->distribution_type = mDistributionType;
|
|
|
|
hdr->content_type = mContentType;
|
2018-04-07 08:01:11 +00:00
|
|
|
if (mKeyGeneration > 2)
|
|
|
|
{
|
|
|
|
hdr->key_generation = 2;
|
|
|
|
hdr->key_generation_2 = mKeyGeneration;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
hdr->key_generation = mKeyGeneration;
|
|
|
|
hdr->key_generation_2 = 0;
|
|
|
|
}
|
|
|
|
|
2018-03-18 09:08:42 +00:00
|
|
|
hdr->key_area_encryption_key_index = mKaekIndex;
|
2018-04-07 08:01:11 +00:00
|
|
|
hdr->content_size = mContentSize;
|
2018-03-17 12:01:19 +00:00
|
|
|
hdr->program_id = mProgramId;
|
|
|
|
hdr->content_index = mContentIndex;
|
|
|
|
hdr->sdk_addon_version = mSdkAddonVersion;
|
2018-04-07 08:01:11 +00:00
|
|
|
memcpy(hdr->rights_id, mRightsId, nca::kRightsIdLen);
|
2017-07-05 15:38:14 +00:00
|
|
|
|
|
|
|
// TODO: properly reconstruct NCA layout? atm in hands of user
|
2018-04-07 08:01:11 +00:00
|
|
|
for (size_t i = 0; i < mPartitions.getSize(); i++)
|
2017-07-05 15:38:14 +00:00
|
|
|
{
|
2018-04-07 08:01:11 +00:00
|
|
|
// determine partition index
|
|
|
|
byte_t idx = mPartitions[i].index;
|
|
|
|
|
|
|
|
if (mPartitions[i].index >= nca::kPartitionNum || hdr->partition[idx].enabled) continue;
|
2017-07-05 15:38:14 +00:00
|
|
|
|
2018-04-07 08:01:11 +00:00
|
|
|
hdr->partition[idx].start = sizeToBlockNum(mPartitions[i].offset);
|
|
|
|
hdr->partition[idx].end = (sizeToBlockNum(mPartitions[i].offset) + sizeToBlockNum(mPartitions[i].size));
|
|
|
|
hdr->partition[idx].enabled = true;
|
|
|
|
hdr->partition_hash[idx] = mPartitions[i].hash;
|
2017-07-05 15:38:14 +00:00
|
|
|
}
|
|
|
|
|
2018-04-07 08:01:11 +00:00
|
|
|
for (size_t i = 0; i < nca::kAesKeyNum; i++)
|
2017-07-05 15:38:14 +00:00
|
|
|
{
|
2018-03-17 12:01:19 +00:00
|
|
|
hdr->enc_aes_key[i] = mEncAesKeys[i];
|
2017-07-05 15:38:14 +00:00
|
|
|
}
|
2017-07-05 08:57:14 +00:00
|
|
|
}
|
|
|
|
|
2018-03-22 05:26:22 +00:00
|
|
|
void NcaHeader::importBinary(const byte_t * bytes, size_t len)
|
2017-07-05 08:57:14 +00:00
|
|
|
{
|
2017-07-15 08:28:01 +00:00
|
|
|
if (len < sizeof(sNcaHeader))
|
|
|
|
{
|
|
|
|
throw fnd::Exception(kModuleName, "NCA header size is too small");
|
|
|
|
}
|
|
|
|
|
2017-07-17 08:21:39 +00:00
|
|
|
clear();
|
2017-07-15 08:28:01 +00:00
|
|
|
|
2017-07-05 15:38:14 +00:00
|
|
|
mBinaryBlob.alloc(sizeof(sNcaHeader));
|
2017-07-06 03:30:27 +00:00
|
|
|
memcpy(mBinaryBlob.getBytes(), bytes, sizeof(sNcaHeader));
|
2017-07-05 08:57:14 +00:00
|
|
|
|
2017-07-06 03:30:27 +00:00
|
|
|
sNcaHeader* hdr = (sNcaHeader*)mBinaryBlob.getBytes();
|
2017-07-05 08:57:14 +00:00
|
|
|
|
2018-04-21 09:27:29 +00:00
|
|
|
std::string sig = std::string(hdr->signature, 4);
|
|
|
|
if (sig == nca::kNca2Sig)
|
2018-03-18 09:08:42 +00:00
|
|
|
{
|
|
|
|
mFormatVersion = NCA2_FORMAT;
|
|
|
|
}
|
2018-04-21 09:27:29 +00:00
|
|
|
else if (sig == nca::kNca3Sig)
|
2018-03-18 09:08:42 +00:00
|
|
|
{
|
|
|
|
mFormatVersion = NCA3_FORMAT;
|
|
|
|
}
|
|
|
|
else
|
2017-07-05 08:57:14 +00:00
|
|
|
{
|
|
|
|
throw fnd::Exception(kModuleName, "NCA header corrupt");
|
|
|
|
}
|
2017-07-15 08:28:01 +00:00
|
|
|
|
2018-04-07 08:01:11 +00:00
|
|
|
mDistributionType = (nca::DistributionType)hdr->distribution_type;
|
|
|
|
mContentType = (nca::ContentType)hdr->content_type;
|
|
|
|
mKeyGeneration = MAX(hdr->key_generation, hdr->key_generation_2);
|
2018-03-18 09:08:42 +00:00
|
|
|
mKaekIndex = hdr->key_area_encryption_key_index;
|
2018-04-07 08:01:11 +00:00
|
|
|
mContentSize = *hdr->content_size;
|
2018-03-17 12:01:19 +00:00
|
|
|
mProgramId = *hdr->program_id;
|
|
|
|
mContentIndex = *hdr->content_index;
|
|
|
|
mSdkAddonVersion = *hdr->sdk_addon_version;
|
2018-04-07 08:01:11 +00:00
|
|
|
memcpy(mRightsId, hdr->rights_id, nca::kRightsIdLen);
|
2017-07-05 08:57:14 +00:00
|
|
|
|
2018-04-07 08:01:11 +00:00
|
|
|
for (size_t i = 0; i < nca::kPartitionNum; i++)
|
2017-07-05 08:57:14 +00:00
|
|
|
{
|
|
|
|
// skip sections that don't exist
|
2018-04-07 08:01:11 +00:00
|
|
|
if (hdr->partition[i].enabled == 0) continue;
|
2017-07-15 08:28:01 +00:00
|
|
|
|
2017-07-05 08:57:14 +00:00
|
|
|
// add high level struct
|
2018-04-07 08:01:11 +00:00
|
|
|
mPartitions.addElement({(byte_t)i, blockNumToSize(hdr->partition[i].start.get()), blockNumToSize(hdr->partition[i].end.get() - hdr->partition[i].start.get()), hdr->partition_hash[i] });
|
2017-07-05 08:57:14 +00:00
|
|
|
}
|
|
|
|
|
2018-04-07 08:01:11 +00:00
|
|
|
for (size_t i = 0; i < nca::kAesKeyNum; i++)
|
2017-07-05 08:57:14 +00:00
|
|
|
{
|
2018-03-17 12:01:19 +00:00
|
|
|
mEncAesKeys.addElement(hdr->enc_aes_key[i]);
|
2017-07-05 08:57:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-15 08:28:01 +00:00
|
|
|
void nx::NcaHeader::clear()
|
2017-07-06 03:30:27 +00:00
|
|
|
{
|
2018-03-18 09:08:42 +00:00
|
|
|
mFormatVersion = NCA3_FORMAT;
|
2018-04-07 08:01:11 +00:00
|
|
|
mDistributionType = nca::DIST_DOWNLOAD;
|
|
|
|
mContentType = nca::TYPE_PROGRAM;
|
|
|
|
mKeyGeneration = 0;
|
2018-03-18 09:08:42 +00:00
|
|
|
mKaekIndex = 0;
|
2018-04-07 08:01:11 +00:00
|
|
|
mContentSize = 0;
|
2017-07-17 08:21:39 +00:00
|
|
|
mProgramId = 0;
|
2017-07-18 14:17:32 +00:00
|
|
|
mContentIndex = 0;
|
|
|
|
mSdkAddonVersion = 0;
|
2018-04-07 08:01:11 +00:00
|
|
|
|
|
|
|
mPartitions.clear();
|
2017-07-18 14:17:32 +00:00
|
|
|
mEncAesKeys.clear();
|
|
|
|
}
|
|
|
|
|
2018-03-18 09:08:42 +00:00
|
|
|
nx::NcaHeader::FormatVersion nx::NcaHeader::getFormatVersion() const
|
|
|
|
{
|
|
|
|
return mFormatVersion;
|
|
|
|
}
|
|
|
|
|
|
|
|
void nx::NcaHeader::setFormatVersion(FormatVersion version)
|
|
|
|
{
|
|
|
|
mFormatVersion = version;
|
|
|
|
}
|
|
|
|
|
2018-04-07 08:01:11 +00:00
|
|
|
nx::nca::DistributionType nx::NcaHeader::getDistributionType() const
|
2017-07-18 14:17:32 +00:00
|
|
|
{
|
|
|
|
return mDistributionType;
|
|
|
|
}
|
|
|
|
|
2018-04-07 08:01:11 +00:00
|
|
|
void nx::NcaHeader::setDistributionType(nca::DistributionType type)
|
2017-07-18 14:17:32 +00:00
|
|
|
{
|
|
|
|
mDistributionType = type;
|
|
|
|
}
|
|
|
|
|
2018-04-07 08:01:11 +00:00
|
|
|
nx::nca::ContentType nx::NcaHeader::getContentType() const
|
2017-07-18 14:17:32 +00:00
|
|
|
{
|
|
|
|
return mContentType;
|
|
|
|
}
|
|
|
|
|
2018-04-07 08:01:11 +00:00
|
|
|
void nx::NcaHeader::setContentType(nca::ContentType type)
|
2017-07-18 14:17:32 +00:00
|
|
|
{
|
|
|
|
mContentType = type;
|
|
|
|
}
|
|
|
|
|
2018-04-07 08:01:11 +00:00
|
|
|
byte_t nx::NcaHeader::getKeyGeneration() const
|
2017-07-18 14:17:32 +00:00
|
|
|
{
|
2018-04-07 08:01:11 +00:00
|
|
|
return mKeyGeneration;
|
2017-07-18 14:17:32 +00:00
|
|
|
}
|
|
|
|
|
2018-04-07 08:01:11 +00:00
|
|
|
void nx::NcaHeader::setKeyGeneration(byte_t gen)
|
2017-07-18 14:17:32 +00:00
|
|
|
{
|
2018-04-07 08:01:11 +00:00
|
|
|
mKeyGeneration = gen;
|
2017-07-18 14:17:32 +00:00
|
|
|
}
|
|
|
|
|
2018-03-18 09:08:42 +00:00
|
|
|
byte_t nx::NcaHeader::getKaekIndex() const
|
2017-07-18 14:17:32 +00:00
|
|
|
{
|
2018-03-18 09:08:42 +00:00
|
|
|
return mKaekIndex;
|
2017-07-18 14:17:32 +00:00
|
|
|
}
|
|
|
|
|
2018-03-18 09:08:42 +00:00
|
|
|
void nx::NcaHeader::setKaekIndex(byte_t index)
|
2017-07-18 14:17:32 +00:00
|
|
|
{
|
2018-03-18 09:08:42 +00:00
|
|
|
mKaekIndex = index;
|
2017-07-06 03:30:27 +00:00
|
|
|
}
|
|
|
|
|
2018-04-07 08:01:11 +00:00
|
|
|
uint64_t NcaHeader::getContentSize() const
|
2017-07-05 08:57:14 +00:00
|
|
|
{
|
2018-04-07 08:01:11 +00:00
|
|
|
return mContentSize;
|
2017-07-05 08:57:14 +00:00
|
|
|
}
|
|
|
|
|
2018-04-07 08:01:11 +00:00
|
|
|
void NcaHeader::setContentSize(uint64_t size)
|
2017-07-05 08:57:14 +00:00
|
|
|
{
|
2018-04-07 08:01:11 +00:00
|
|
|
mContentSize = size;
|
2017-07-05 08:57:14 +00:00
|
|
|
}
|
|
|
|
|
2018-03-22 05:26:22 +00:00
|
|
|
uint64_t NcaHeader::getProgramId() const
|
2017-07-05 08:57:14 +00:00
|
|
|
{
|
2017-07-05 15:38:14 +00:00
|
|
|
return mProgramId;
|
2017-07-05 08:57:14 +00:00
|
|
|
}
|
|
|
|
|
2018-03-22 05:26:22 +00:00
|
|
|
void NcaHeader::setProgramId(uint64_t program_id)
|
2017-07-05 08:57:14 +00:00
|
|
|
{
|
2017-07-05 15:38:14 +00:00
|
|
|
mProgramId = program_id;
|
2017-07-05 08:57:14 +00:00
|
|
|
}
|
|
|
|
|
2018-03-22 05:26:22 +00:00
|
|
|
uint32_t nx::NcaHeader::getContentIndex() const
|
2017-07-18 14:17:32 +00:00
|
|
|
{
|
|
|
|
return mContentIndex;
|
|
|
|
}
|
|
|
|
|
2018-03-22 05:26:22 +00:00
|
|
|
void nx::NcaHeader::setContentIndex(uint32_t index)
|
2017-07-18 14:17:32 +00:00
|
|
|
{
|
|
|
|
mContentIndex = index;
|
|
|
|
}
|
|
|
|
|
2018-03-22 05:26:22 +00:00
|
|
|
uint32_t nx::NcaHeader::getSdkAddonVersion() const
|
2017-07-18 14:17:32 +00:00
|
|
|
{
|
|
|
|
return mSdkAddonVersion;
|
|
|
|
}
|
|
|
|
|
2018-03-22 05:26:22 +00:00
|
|
|
void nx::NcaHeader::setSdkAddonVersion(uint32_t version)
|
2017-07-05 08:57:14 +00:00
|
|
|
{
|
2017-07-18 14:17:32 +00:00
|
|
|
mSdkAddonVersion = version;
|
2017-07-05 08:57:14 +00:00
|
|
|
}
|
|
|
|
|
2018-05-01 05:17:25 +00:00
|
|
|
bool nx::NcaHeader::hasRightsId() const
|
|
|
|
{
|
|
|
|
bool rightsIdIsSet = false;
|
|
|
|
|
|
|
|
for (size_t i = 0; i < nca::kRightsIdLen; i++)
|
|
|
|
{
|
|
|
|
if (mRightsId[i] != 0)
|
|
|
|
rightsIdIsSet = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rightsIdIsSet;
|
|
|
|
}
|
|
|
|
|
2018-04-07 08:01:11 +00:00
|
|
|
const byte_t* nx::NcaHeader::getRightsId() const
|
2017-07-05 08:57:14 +00:00
|
|
|
{
|
2018-04-07 08:01:11 +00:00
|
|
|
return mRightsId;
|
2017-07-05 08:57:14 +00:00
|
|
|
}
|
|
|
|
|
2018-04-07 08:01:11 +00:00
|
|
|
void nx::NcaHeader::setRightsId(const byte_t* rights_id)
|
2017-07-05 08:57:14 +00:00
|
|
|
{
|
2018-04-07 08:01:11 +00:00
|
|
|
memcpy(mRightsId, rights_id, nca::kRightsIdLen);
|
|
|
|
}
|
|
|
|
|
|
|
|
const fnd::List<NcaHeader::sPartition>& NcaHeader::getPartitions() const
|
|
|
|
{
|
|
|
|
return mPartitions;
|
|
|
|
}
|
|
|
|
|
|
|
|
void NcaHeader::setPartitions(const fnd::List<NcaHeader::sPartition>& partitions)
|
|
|
|
{
|
|
|
|
mPartitions = partitions;
|
|
|
|
if (mPartitions.getSize() >= nca::kPartitionNum)
|
2017-07-05 08:57:14 +00:00
|
|
|
{
|
2018-04-07 08:01:11 +00:00
|
|
|
throw fnd::Exception(kModuleName, "Too many NCA partitions");
|
2017-07-05 08:57:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-18 14:17:32 +00:00
|
|
|
const fnd::List<crypto::aes::sAes128Key>& NcaHeader::getEncAesKeys() const
|
2017-07-05 08:57:14 +00:00
|
|
|
{
|
2017-07-18 14:17:32 +00:00
|
|
|
return mEncAesKeys;
|
2017-07-05 08:57:14 +00:00
|
|
|
}
|
|
|
|
|
2018-04-07 08:01:11 +00:00
|
|
|
void NcaHeader::setEncAesKeys(const fnd::List<crypto::aes::sAes128Key>& keys)
|
2017-07-05 08:57:14 +00:00
|
|
|
{
|
2018-04-07 08:01:11 +00:00
|
|
|
mEncAesKeys = keys;
|
2017-07-05 08:57:14 +00:00
|
|
|
}
|
|
|
|
|
2018-03-22 05:26:22 +00:00
|
|
|
uint64_t NcaHeader::blockNumToSize(uint32_t block_num) const
|
2017-07-05 08:57:14 +00:00
|
|
|
{
|
2018-04-07 08:01:11 +00:00
|
|
|
return block_num*nca::kSectorSize;
|
2017-07-05 08:57:14 +00:00
|
|
|
}
|
|
|
|
|
2018-03-22 05:26:22 +00:00
|
|
|
uint32_t NcaHeader::sizeToBlockNum(uint64_t real_size) const
|
2017-07-05 08:57:14 +00:00
|
|
|
{
|
2018-05-26 04:07:42 +00:00
|
|
|
return (uint32_t)(align(real_size, nca::kSectorSize) / nca::kSectorSize);
|
2017-07-05 08:57:14 +00:00
|
|
|
}
|
|
|
|
|
2017-07-06 10:55:58 +00:00
|
|
|
bool NcaHeader::isEqual(const NcaHeader & other) const
|
|
|
|
{
|
2017-07-18 14:17:32 +00:00
|
|
|
return (mDistributionType == other.mDistributionType) \
|
|
|
|
&& (mContentType == other.mContentType) \
|
2018-04-07 08:01:11 +00:00
|
|
|
&& (mKeyGeneration == other.mKeyGeneration) \
|
2018-03-18 09:08:42 +00:00
|
|
|
&& (mKaekIndex == other.mKaekIndex) \
|
2018-04-07 08:01:11 +00:00
|
|
|
&& (mContentSize == other.mContentSize) \
|
2017-07-06 10:55:58 +00:00
|
|
|
&& (mProgramId == other.mProgramId) \
|
2017-07-18 14:17:32 +00:00
|
|
|
&& (mContentIndex == other.mContentIndex) \
|
|
|
|
&& (mSdkAddonVersion == other.mSdkAddonVersion) \
|
2018-04-07 08:01:11 +00:00
|
|
|
&& (mPartitions == other.mPartitions) \
|
2017-07-18 14:17:32 +00:00
|
|
|
&& (mEncAesKeys == other.mEncAesKeys);
|
2017-07-06 10:55:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void NcaHeader::copyFrom(const NcaHeader & other)
|
|
|
|
{
|
|
|
|
if (other.getSize())
|
|
|
|
{
|
|
|
|
importBinary(other.getBytes(), other.getSize());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2017-07-18 14:17:32 +00:00
|
|
|
mBinaryBlob.clear();
|
|
|
|
mDistributionType = other.mDistributionType;
|
|
|
|
mContentType = other.mContentType;
|
2018-04-07 08:01:11 +00:00
|
|
|
mKeyGeneration = other.mKeyGeneration;
|
2018-03-18 09:08:42 +00:00
|
|
|
mKaekIndex = other.mKaekIndex;
|
2018-04-07 08:01:11 +00:00
|
|
|
mContentSize = other.mContentSize;
|
2017-07-06 10:55:58 +00:00
|
|
|
mProgramId = other.mProgramId;
|
2017-07-18 14:17:32 +00:00
|
|
|
mContentIndex = other.mContentIndex;
|
|
|
|
mSdkAddonVersion = other.mSdkAddonVersion;
|
2018-04-07 08:01:11 +00:00
|
|
|
mPartitions = other.mPartitions;
|
2017-07-18 14:17:32 +00:00
|
|
|
mEncAesKeys = other.mEncAesKeys;
|
2017-07-06 10:55:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-05 08:57:14 +00:00
|
|
|
NcaHeader::NcaHeader()
|
|
|
|
{
|
2017-07-17 08:21:39 +00:00
|
|
|
clear();
|
2017-07-05 08:57:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
NcaHeader::NcaHeader(const NcaHeader & other)
|
|
|
|
{
|
2017-07-06 10:55:58 +00:00
|
|
|
copyFrom(other);
|
2017-07-05 08:57:14 +00:00
|
|
|
}
|
|
|
|
|
2018-03-22 05:26:22 +00:00
|
|
|
NcaHeader::NcaHeader(const byte_t * bytes, size_t len)
|
2017-07-05 08:57:14 +00:00
|
|
|
{
|
2017-07-15 08:28:01 +00:00
|
|
|
importBinary(bytes, len);
|
2017-07-05 08:57:14 +00:00
|
|
|
}
|
|
|
|
|
2017-07-06 10:55:58 +00:00
|
|
|
bool NcaHeader::operator==(const NcaHeader & other) const
|
|
|
|
{
|
|
|
|
return isEqual(other);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool NcaHeader::operator!=(const NcaHeader & other) const
|
2017-07-06 03:30:27 +00:00
|
|
|
{
|
2017-07-06 10:55:58 +00:00
|
|
|
return !isEqual(other);
|
2017-07-06 03:30:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void NcaHeader::operator=(const NcaHeader & other)
|
|
|
|
{
|
|
|
|
this->importBinary(other.getBytes(), other.getSize());
|
|
|
|
}
|
|
|
|
|
2018-03-22 05:26:22 +00:00
|
|
|
const byte_t * NcaHeader::getBytes() const
|
2017-07-05 08:57:14 +00:00
|
|
|
{
|
2017-07-06 03:30:27 +00:00
|
|
|
return mBinaryBlob.getBytes();
|
2017-07-05 08:57:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t NcaHeader::getSize() const
|
|
|
|
{
|
2017-07-06 03:30:27 +00:00
|
|
|
return mBinaryBlob.getSize();
|
2017-07-05 08:57:14 +00:00
|
|
|
}
|