diff --git a/lib/nx/NcaHeader.cpp b/lib/nx/NcaHeader.cpp index 8ed9a47..9c6f7cc 100644 --- a/lib/nx/NcaHeader.cpp +++ b/lib/nx/NcaHeader.cpp @@ -14,13 +14,13 @@ void NcaHeader::exportBinary() // TODO: properly reconstruct NCA layout? atm in hands of user - for (size_t i = 0; i < mSections.size(); i++) + for (size_t i = 0; i < mSections.getSize(); i++) { // determine section index - u8 section = mSections.size() - 1 - i; + u8 section = mSections.getSize() - 1 - i; - hdr->section(section).set_start(mSections[i].start_blk); - hdr->section(section).set_end(mSections[i].end_blk); + 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_key_type(mSections[i].key_type); hdr->section_hash(section) = mSections[i].hash; } @@ -59,12 +59,12 @@ void NcaHeader::importBinary(const u8 * bytes) if (hdr->section(section).start() == 0 && hdr->section(section).end() == 0) continue; // add high level struct - mSections.push_back({ hdr->section(section).start(), hdr->section(section).end(), 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()), hdr->section(section).key_type(), hdr->section_hash(section) }); } for (size_t i = 0; i < kAesKeyNum; i++) { - mAesKeys.push_back(hdr->aes_key(i)); + mAesKeys.addElement(hdr->aes_key(i)); } } @@ -102,33 +102,33 @@ u32 NcaHeader::getUnk() const return mUnk0; } -const std::vector& NcaHeader::getSections() const +const fnd::List& NcaHeader::getSections() const { return mSections; } void NcaHeader::addSection(const sSection & section) { - if (mSections.size() >= kSectionNum) + if (mSections.getSize() >= kSectionNum) { throw fnd::Exception(kModuleName, "Too many NCA sections"); } - mSections.push_back(section); + mSections.addElement(section); } -const std::vector& NcaHeader::getAesKeys() const +const fnd::List& NcaHeader::getAesKeys() const { return mAesKeys; } void NcaHeader::addKey(const crypto::aes::sAes128Key & key) { - if (mAesKeys.size() >= kAesKeyNum) + if (mAesKeys.getSize() >= kAesKeyNum) { throw fnd::Exception(kModuleName, "Too many NCA aes keys"); } - mAesKeys.push_back(key); + mAesKeys.addElement(key); } void NcaHeader::clearVariables() @@ -151,6 +151,34 @@ u32 NcaHeader::sizeToBlockNum(u64 real_size) const return align(real_size, mBlockSize)/mBlockSize; } +bool NcaHeader::isEqual(const NcaHeader & other) const +{ + return (mBlockSize == other.mBlockSize) \ + && (mNcaSize == other.mNcaSize) \ + && (mProgramId == other.mProgramId) \ + && (mUnk0 == other.mUnk0) \ + && (mSections == other.mSections) \ + && (mAesKeys == other.mAesKeys); +} + +void NcaHeader::copyFrom(const NcaHeader & other) +{ + if (other.getSize()) + { + importBinary(other.getBytes(), other.getSize()); + } + else + { + this->mBinaryBlob.clear(); + mBlockSize = other.mBlockSize; + mNcaSize = other.mNcaSize; + mProgramId = other.mProgramId; + mUnk0 = other.mUnk0; + mSections = other.mSections; + mAesKeys = other.mAesKeys; + } +} + NcaHeader::NcaHeader() { clearVariables(); @@ -158,7 +186,7 @@ NcaHeader::NcaHeader() NcaHeader::NcaHeader(const NcaHeader & other) { - importBinary(other.getBytes()); + copyFrom(other); } NcaHeader::NcaHeader(const u8 * bytes) @@ -166,9 +194,14 @@ NcaHeader::NcaHeader(const u8 * bytes) importBinary(bytes); } -bool NcaHeader::operator==(const NcaHeader & other) +bool NcaHeader::operator==(const NcaHeader & other) const { - return memcmp(this->getBytes(), other.getBytes(), this->getSize()) == 0; + return isEqual(other); +} + +bool NcaHeader::operator!=(const NcaHeader & other) const +{ + return !isEqual(other); } void NcaHeader::operator=(const NcaHeader & other) diff --git a/lib/nx/NcaHeader.h b/lib/nx/NcaHeader.h index 35addec..1901cd4 100644 --- a/lib/nx/NcaHeader.h +++ b/lib/nx/NcaHeader.h @@ -1,8 +1,8 @@ #pragma once -#include #include #include #include +#include #include #include #include @@ -12,12 +12,33 @@ class NcaHeader : public ISerialiseableBinary public: struct sSection { - u32 start_blk; - u32 end_blk; u64 offset; u64 size; u8 key_type; crypto::sha::sSha256Hash hash; + + const sSection& operator=(const sSection& other) + { + offset = other.offset; + size = other.size; + key_type = other.key_type; + hash = other.hash; + + return *this; + } + + bool operator==(const sSection& other) const + { + return (offset == other.offset) \ + && (size == other.size) \ + && (key_type == other.key_type) \ + && (hash == other.hash); + } + + bool operator!=(const sSection& other) const + { + return operator==(other); + } }; static const size_t kDefaultBlockSize = 0x200; @@ -26,7 +47,8 @@ public: NcaHeader(const NcaHeader& other); NcaHeader(const u8* bytes); - bool operator==(const NcaHeader& other); + bool operator==(const NcaHeader& other) const; + bool operator!=(const NcaHeader& other) const; void operator=(const NcaHeader& other); // to be used after export @@ -44,9 +66,9 @@ public: u64 getProgramId() const; void setProgramId(u64 program_id); u32 getUnk() const; - const std::vector& getSections() const; + const fnd::List& getSections() const; void addSection(const sSection& section); - const std::vector& getAesKeys() const; + const fnd::List& getAesKeys() const; void addKey(const crypto::aes::sAes128Key& key); private: @@ -122,10 +144,12 @@ private: u64 mNcaSize; u64 mProgramId; u32 mUnk0; - std::vector mSections; - std::vector mAesKeys; + fnd::List mSections; + fnd::List mAesKeys; void clearVariables(); u64 blockNumToSize(u32 block_num) const; u32 sizeToBlockNum(u64 real_size) const; + bool isEqual(const NcaHeader& other) const; + void copyFrom(const NcaHeader& other); }; diff --git a/lib/nx/SacEntry.cpp b/lib/nx/SacEntry.cpp index edfa335..dcf748e 100644 --- a/lib/nx/SacEntry.cpp +++ b/lib/nx/SacEntry.cpp @@ -6,14 +6,31 @@ SacEntry::SacEntry() : { } -SacEntry::SacEntry(const SacEntry & other) +SacEntry::SacEntry(const std::string & name, bool isServer) : + mIsServer(isServer), + mName(name) { - importBinary(other.getBytes(), other.getSize()); + exportBinary(); } -SacEntry::SacEntry(const u8 * bytes) +SacEntry::SacEntry(const SacEntry & other) { - importBinary(bytes); + copyFrom(other); +} + +bool SacEntry::operator==(const SacEntry & other) const +{ + return isEqual(other); +} + +bool SacEntry::operator!=(const SacEntry & other) const +{ + return !isEqual(other); +} + +void SacEntry::operator=(const SacEntry & other) +{ + copyFrom(other); } const u8 * SacEntry::getBytes() const @@ -47,9 +64,20 @@ void SacEntry::exportBinary() } void SacEntry::importBinary(const u8 * bytes) +{ + throw fnd::Exception(kModuleName, "Unsupported operation. importBinary(const u8* bytes) is not supported for variable size structures."); +} + +void SacEntry::importBinary(const u8 * bytes, size_t len) { bool isServer = (bytes[0] & SAC_IS_SERVER) == SAC_IS_SERVER; size_t nameLen = (bytes[0] & SAC_NAME_LEN_MASK); + + if (nameLen+1 > len) + { + throw fnd::Exception(kModuleName, "SAC entry is too small"); + } + if (nameLen == 0) { throw fnd::Exception(kModuleName, "SAC entry has no service name"); @@ -66,15 +94,6 @@ void SacEntry::importBinary(const u8 * bytes) mName = std::string((const char*)(mBinaryBlob.getBytes() + 1), nameLen); } -void SacEntry::importBinary(const u8 * bytes, size_t len) -{ - importBinary(bytes); - if (getSize() != len) - { - throw fnd::Exception(kModuleName, "SAC Entry has unexpected size"); - } -} - bool SacEntry::isServer() const { return mIsServer; @@ -99,3 +118,22 @@ void SacEntry::setName(const std::string & name) mName = name; } + +bool SacEntry::isEqual(const SacEntry & other) const +{ + return (mIsServer == other.mIsServer) \ + && (mName == other.mName); +} + +void SacEntry::copyFrom(const SacEntry & other) +{ + if (other.getSize()) + { + importBinary(other.getBytes(), other.getSize()); + } + else + { + this->mIsServer = other.mIsServer; + this->mName = other.mName; + } +} diff --git a/lib/nx/SacEntry.h b/lib/nx/SacEntry.h index 644a02d..14ca515 100644 --- a/lib/nx/SacEntry.h +++ b/lib/nx/SacEntry.h @@ -6,9 +6,14 @@ class SacEntry : public ISerialiseableBinary { +public: SacEntry(); + SacEntry(const std::string& name, bool isServer); SacEntry(const SacEntry& other); - SacEntry(const u8* bytes); + + bool operator==(const SacEntry& other) const; + bool operator!=(const SacEntry& other) const; + void operator=(const SacEntry& other); // to be used after export const u8* getBytes() const; @@ -40,4 +45,7 @@ private: // variables bool mIsServer; std::string mName; + + bool isEqual(const SacEntry& other) const; + void copyFrom(const SacEntry& other); }; \ No newline at end of file diff --git a/programs/ncatool/main.cpp b/programs/ncatool/main.cpp index 5236ae3..5e5177a 100644 --- a/programs/ncatool/main.cpp +++ b/programs/ncatool/main.cpp @@ -57,46 +57,52 @@ int main(int argc, char** argv) return 1; } - fnd::MemoryBlob nca; - fnd::io::readFile(argv[1], nca); - - u8 sector[kNcaSectorSize]; - - // nca test - if (argc == 2) + try { - decryptNcaSectorXts(nca, sector, 1, nx::crypto::aes::nca_header_key[0], nx::crypto::aes::nca_header_key[1]); + fnd::MemoryBlob nca; + fnd::io::readFile(argv[1], nca); - NcaHeader hdr; - hdr.importBinary(sector); + u8 sector[kNcaSectorSize]; - printf("NCA Header\n"); - printf(" Size: 0x%" PRIx64 "\n", hdr.getNcaSize()); - printf(" ProgID: 0x%016" PRIx64 "\n", hdr.getProgramId()); - printf(" Unk0: 0x%" PRIx32 "\n", hdr.getUnk()); - printf(" Sections:\n"); - for (size_t i = 0; i < hdr.getSections().size(); i++) + // nca test + if (argc == 2) { - const NcaHeader::sSection& section = hdr.getSections()[i]; - printf(" %lu:\n", i); - printf(" Start Blk: %" PRId32 "\n", section.start_blk); - printf(" End Blk: %" PRId32 "\n", section.end_blk); - printf(" Offset: 0x%" PRIx64 "\n", section.offset); - printf(" Size: 0x%" PRIx64 "\n", section.size); - printf(" KeyType: 0x%02x\n", section.key_type); - printf(" Hash: "); - hexDump(section.hash.bytes, crypto::sha::kSha256HashLen); - printf("\n"); - } - printf(" AES Keys:\n"); - for (size_t i = 0; i < hdr.getAesKeys().size(); i++) - { - printf(" %lu: ", i); - hexDump(hdr.getAesKeys()[i].key, crypto::aes::kAes128KeySize); - printf("\n"); - } + decryptNcaSectorXts(nca, sector, 1, nx::crypto::aes::nca_header_key[0], nx::crypto::aes::nca_header_key[1]); - + NcaHeader hdr; + hdr.importBinary(sector); + + printf("[NCA Header]\n"); + printf(" Size: 0x%" PRIx64 "\n", hdr.getNcaSize()); + printf(" ProgID: 0x%016" PRIx64 "\n", hdr.getProgramId()); + printf(" Unk0: 0x%" PRIx32 "\n", hdr.getUnk()); + printf(" Sections:\n"); + for (size_t i = 0; i < hdr.getSections().getSize(); i++) + { + const NcaHeader::sSection& section = hdr.getSections()[i]; + printf(" %lu:\n", i); + //printf(" Start Blk: %" PRId32 "\n", section.start_blk); + //printf(" End Blk: %" PRId32 "\n", section.end_blk); + printf(" Offset: 0x%" PRIx64 "\n", section.offset); + printf(" Size: 0x%" PRIx64 "\n", section.size); + printf(" KeyType: 0x%02x\n", section.key_type); + printf(" Hash: "); + hexDump(section.hash.bytes, crypto::sha::kSha256HashLen); + printf("\n"); + } + printf(" AES Keys:\n"); + for (size_t i = 0; i < hdr.getAesKeys().getSize(); i++) + { + printf(" %lu: ", i); + hexDump(hdr.getAesKeys()[i].key, crypto::aes::kAes128KeySize); + printf("\n"); + } + + + } + } catch (const fnd::Exception& e) + { + printf("[%s%sERROR] %s\n", e.module(), strlen(e.module()) > 0 ? " " : "", e.what()); } return 0;