From 60e1b8a32837242fc51b183d4366055a270b8864 Mon Sep 17 00:00:00 2001 From: jakcron Date: Fri, 7 Jul 2017 10:18:33 +1000 Subject: [PATCH] [nx] Add NpdmHeader. --- lib/nx/NpdmHeader.cpp | 278 ++++++++++++++++++++++++++++++++++++++ lib/nx/NpdmHeader.h | 195 ++++++++++++++++++++++++++ lib/nx/nx.vcxproj | 2 + lib/nx/nx.vcxproj.filters | 6 + 4 files changed, 481 insertions(+) create mode 100644 lib/nx/NpdmHeader.cpp create mode 100644 lib/nx/NpdmHeader.h diff --git a/lib/nx/NpdmHeader.cpp b/lib/nx/NpdmHeader.cpp new file mode 100644 index 0000000..07385b6 --- /dev/null +++ b/lib/nx/NpdmHeader.cpp @@ -0,0 +1,278 @@ +#include "NpdmHeader.h" + + + +nx::NpdmHeader::NpdmHeader() +{ + clearVariables(); +} + +nx::NpdmHeader::NpdmHeader(const NpdmHeader & other) +{ + copyFrom(other); +} + +nx::NpdmHeader::NpdmHeader(const u8 * bytes) +{ + importBinary(bytes); +} + +bool nx::NpdmHeader::operator==(const NpdmHeader & other) const +{ + return isEqual(other); +} + +bool nx::NpdmHeader::operator!=(const NpdmHeader & other) const +{ + return isEqual(other); +} + +void nx::NpdmHeader::operator=(const NpdmHeader & other) +{ + copyFrom(other); +} + +const u8 * nx::NpdmHeader::getBytes() const +{ + return mBinaryBlob.getBytes(); +} + +size_t nx::NpdmHeader::getSize() const +{ + return mBinaryBlob.getSize(); +} + +void nx::NpdmHeader::clearVariables() +{ + mBinaryBlob.clear(); + mInstructionType = INSTR_64BIT; + mProcAddressSpaceType = ADDR_SPACE_64BIT; + mMainThreadPriority = 0; + mMainThreadCoreNumber = 0; + mVersion = 0; + mMainThreadStackSize = 0; + mName.clear(); + mProductCode.clear(); + mAciPos.offset = 0; + mAciPos.size = 0; + mAcidPos.offset = 0; + mAcidPos.size = 0; +} + +void nx::NpdmHeader::calculateOffsets() +{ + mAcidPos.offset = align(sizeof(sNpdmHeader), kNpdmAlignSize); + mAciPos.offset = mAcidPos.offset + align(mAcidPos.size, kNpdmAlignSize); +} + +bool nx::NpdmHeader::isEqual(const NpdmHeader & other) const +{ + return (mInstructionType == other.mInstructionType) \ + && (mProcAddressSpaceType == other.mProcAddressSpaceType) \ + && (mMainThreadPriority == other.mMainThreadPriority) \ + && (mMainThreadCoreNumber == other.mMainThreadCoreNumber) \ + && (mVersion == other.mVersion) \ + && (mMainThreadStackSize == other.mMainThreadStackSize) \ + && (mName == other.mName) \ + && (mProductCode == other.mProductCode) \ + && (mAciPos == other.mAciPos) \ + && (mAcidPos == other.mAcidPos); +} + +void nx::NpdmHeader::copyFrom(const NpdmHeader & other) +{ + if (other.getSize()) + { + importBinary(other.getBytes(), other.getSize()); + } + else + { + mInstructionType = other.mInstructionType; + mProcAddressSpaceType = other.mProcAddressSpaceType; + mMainThreadPriority = other.mMainThreadPriority; + mMainThreadCoreNumber = other.mMainThreadCoreNumber; + mVersion = other.mVersion; + mMainThreadStackSize = other.mMainThreadStackSize; + mName = other.mName; + mProductCode = other.mProductCode; + mAciPos = other.mAciPos; + mAcidPos = other.mAcidPos; + } +} + +void nx::NpdmHeader::exportBinary() +{ + mBinaryBlob.alloc(sizeof(sNpdmHeader)); + sNpdmHeader* hdr = (sNpdmHeader*)mBinaryBlob.getBytes(); + + hdr->set_signature(kNpdmStructSig.c_str()); + u8 flag = ((u8)(mInstructionType & 1) | (u8)((mProcAddressSpaceType & 3) << 1)) & 0xf; + hdr->set_flags(flag); + hdr->set_main_thread_priority(mMainThreadPriority); + hdr->set_main_thread_core_number(mMainThreadCoreNumber); + hdr->set_version(mVersion); + hdr->set_main_thread_stack_size(mMainThreadStackSize); + hdr->set_name(mName.c_str()); + hdr->set_product_code(mProductCode.c_str()); + + calculateOffsets(); + hdr->aci().set_offset(mAciPos.offset); + hdr->aci().set_size(mAciPos.size); + hdr->acid().set_offset(mAcidPos.offset); + hdr->acid().set_size(mAcidPos.size); +} + +void nx::NpdmHeader::importBinary(const u8 * bytes) +{ + mBinaryBlob.alloc(sizeof(sNpdmHeader)); + memcpy(mBinaryBlob.getBytes(), bytes, mBinaryBlob.getSize()); + sNpdmHeader* hdr = (sNpdmHeader*)mBinaryBlob.getBytes(); + + if (memcmp(kNpdmStructSig.c_str(), hdr->signature(), 4) != 0) + { + throw fnd::Exception(kModuleName, "NPDM header corrupt"); + } + + u8 flag = hdr->flags() & 0xf; + mInstructionType = (InstructionType)(flag & 1); + mProcAddressSpaceType = (ProcAddrSpaceType)((flag >> 1) & 3); + mMainThreadPriority = hdr->main_thread_priority(); + mMainThreadCoreNumber = hdr->main_thread_core_number(); + mVersion = hdr->version(); + mMainThreadStackSize = hdr->main_thread_stack_size(); + mName = std::string(hdr->name(), kNameMaxLen); + mProductCode = std::string(hdr->product_code(), kProductCodeMaxLen); + mAciPos.offset = hdr->aci().offset(); + mAciPos.size = hdr->aci().size(); + mAcidPos.offset = hdr->acid().offset(); + mAcidPos.size = hdr->acid().size(); +} + +void nx::NpdmHeader::importBinary(const u8 * bytes, size_t len) +{ + if (len < sizeof(sNpdmHeader)) + { + throw fnd::Exception(kModuleName, "NPDM header too small"); + } + importBinary(bytes); +} + +size_t nx::NpdmHeader::getNpdmSize() const +{ + return MAX(mAcidPos.offset + mAcidPos.size, mAciPos.offset + mAciPos.size); +} + +nx::NpdmHeader::InstructionType nx::NpdmHeader::getInstructionType() const +{ + return mInstructionType; +} + +void nx::NpdmHeader::setInstructionType(InstructionType type) +{ + mInstructionType = type; +} + +nx::NpdmHeader::ProcAddrSpaceType nx::NpdmHeader::getProcAddressSpaceType() const +{ + return mProcAddressSpaceType; +} + +void nx::NpdmHeader::setProcAddressSpaceType(ProcAddrSpaceType type) +{ + mProcAddressSpaceType = type; +} + +u8 nx::NpdmHeader::getMainThreadPriority() const +{ + return mMainThreadPriority; +} + +void nx::NpdmHeader::setMainThreadPriority(u8 priority) +{ + if (priority > kMaxPriority) + { + throw fnd::Exception(kModuleName, "Illegal main thread priority (range 0-63)"); + } + + mMainThreadPriority = priority; +} + +u8 nx::NpdmHeader::getMainThreadCoreNumber() const +{ + return mMainThreadCoreNumber; +} + +void nx::NpdmHeader::setMainThreadCoreNumber(u8 core_num) +{ + mMainThreadCoreNumber = core_num; +} + +u32 nx::NpdmHeader::getVersion() const +{ + return mVersion; +} + +void nx::NpdmHeader::setVersion(u32 version) +{ + mVersion = version; +} + +u32 nx::NpdmHeader::getMainThreadStackSize() const +{ + return mMainThreadStackSize; +} + +void nx::NpdmHeader::setMainThreadStackSize(u32 size) +{ + mMainThreadStackSize = size; +} + +const std::string & nx::NpdmHeader::getName() const +{ + return mName; +} + +void nx::NpdmHeader::setName(const std::string & name) +{ + if (name.length() > kNameMaxLen) + { + throw fnd::Exception(kModuleName, "Name is too long"); + } + + mName = name; +} + +const std::string & nx::NpdmHeader::getProductCode() const +{ + return mProductCode; +} + +void nx::NpdmHeader::setProductCode(const std::string & product_code) +{ + if (product_code.length() > kProductCodeMaxLen) + { + throw fnd::Exception(kModuleName, "Product Code is too long"); + } + + mProductCode = product_code; +} + +const nx::NpdmHeader::sSection & nx::NpdmHeader::getAciPos() const +{ + return mAciPos; +} + +void nx::NpdmHeader::setSetAciSize(size_t size) +{ + mAciPos.size = size; +} + +const nx::NpdmHeader::sSection & nx::NpdmHeader::getAcidPos() const +{ + return mAcidPos; +} + +void nx::NpdmHeader::setSetAcidSize(size_t size) +{ + mAcidPos.size = size; +} diff --git a/lib/nx/NpdmHeader.h b/lib/nx/NpdmHeader.h new file mode 100644 index 0000000..bea9288 --- /dev/null +++ b/lib/nx/NpdmHeader.h @@ -0,0 +1,195 @@ +#pragma once +#include +#include +#include +#include + +namespace nx +{ + class NpdmHeader : + public nx::ISerialiseableBinary + { + public: + enum InstructionType + { + INSTR_32BIT, + INSTR_64BIT, + }; + + enum ProcAddrSpaceType + { + ADDR_SPACE_64BIT = 1, + ADDR_SPACE_32BIT, + ADDR_SPACE_32BIT_NO_RESERVED, + }; + + struct sSection + { + size_t offset; + size_t size; + + void operator=(const sSection& other) + { + offset = other.offset; + size = other.size; + } + + bool operator==(const sSection& other) const + { + return (offset == other.offset) \ + && (size == other.size); + } + + bool operator!=(const sSection& other) const + { + return !operator==(other); + } + }; + + NpdmHeader(); + NpdmHeader(const NpdmHeader& other); + NpdmHeader(const u8* bytes); + + bool operator==(const NpdmHeader& other) const; + bool operator!=(const NpdmHeader& other) const; + void operator=(const NpdmHeader& other); + + // to be used after export + const u8* getBytes() const; + size_t getSize() const; + + // export/import binary + void exportBinary(); + void importBinary(const u8* bytes); + void importBinary(const u8* bytes, size_t len); + + // variables + size_t getNpdmSize() const; + + InstructionType getInstructionType() const; + void setInstructionType(InstructionType type); + + ProcAddrSpaceType getProcAddressSpaceType() const; + void setProcAddressSpaceType(ProcAddrSpaceType type); + + u8 getMainThreadPriority() const; + void setMainThreadPriority(u8 priority); + + u8 getMainThreadCoreNumber() const; + void setMainThreadCoreNumber(u8 core_num); + + u32 getVersion() const; + void setVersion(u32 version); + + u32 getMainThreadStackSize() const; + void setMainThreadStackSize(u32 size); + + const std::string& getName() const; + void setName(const std::string& name); + + const std::string& getProductCode() const; + void setProductCode(const std::string& product_code); + + const sSection& getAciPos() const; + void setSetAciSize(size_t size); + + const sSection& getAcidPos() const; + void setSetAcidSize(size_t size); + private: + const std::string kModuleName = "NPDM_HEADER"; + const std::string kNpdmStructSig = "META"; + static const size_t kNameMaxLen = 0x10; + static const size_t kProductCodeMaxLen = 0x10; + static const u8 kMaxPriority = 63; + static const size_t kNpdmAlignSize = 0x10; + + + enum FlagBits + { + IS_64BIT_INSTRUCTION_SET = BIT(0) + }; + +#pragma pack (push, 1) + struct sNpdmHeader + { + private: + u8 signature_[4]; // be"META" + u8 reserved_0[8]; + u8 flags_; + u8 reserved_1; + u8 main_thread_priority_; // 0-63 inclusive + u8 main_thread_core_number_; + u8 reserved_2[8]; + u32 version_; + u32 main_thread_stack_size_; // default 4096 + u8 name_[kNameMaxLen]; // important + u8 product_code_[kProductCodeMaxLen]; // can be empty + u8 reserved_3[48]; + // Access Control Info + struct sNpdmSection + { + private: + u32 offset_; + u32 size_; + public: + u32 offset() const { return le_word(offset_); } + void set_offset(u32 offset) { offset_ = le_word(offset); } + + u32 size() const { return le_word(size_); } + void set_size(u32 size) { size_ = le_word(size); } + } aci_, acid_; + public: + const char* signature() const { return (const char*)signature_; } + void set_signature(const char* signature) { memcpy(signature_, signature, 4); } + + u8 flags() const { return flags_; } + void set_flags(u8 flags) { flags_ = flags; } + + u8 main_thread_priority() const { return main_thread_priority_; } + void set_main_thread_priority(u8 priority) { main_thread_priority_ = priority; } + + u8 main_thread_core_number() const { return main_thread_core_number_; } + void set_main_thread_core_number(u8 core_number) { main_thread_core_number_ = core_number; } + + u32 version() const { return le_word(version_); } + void set_version(u32 version) { version_ = le_word(version); } + + u32 main_thread_stack_size() const { return le_word(main_thread_stack_size_); } + void set_main_thread_stack_size(u32 size) { main_thread_stack_size_ = le_word(size); } + + const char* name() const { return (const char*)name_; } + void set_name(const char* name) { strncpy((char*)name_, name, kNameMaxLen); } + + const char* product_code() const { return (const char*)product_code_; } + void set_product_code(const char* product_code) { strncpy((char*)product_code_, product_code, kProductCodeMaxLen); } + + const sNpdmSection& aci() const { return aci_; } + sNpdmSection& aci() { return aci_; } + + const sNpdmSection& acid() const { return acid_; } + sNpdmSection& acid() { return acid_; } + }; +#pragma pack (pop) + + // raw binary + fnd::MemoryBlob mBinaryBlob; + + // variables + InstructionType mInstructionType; + ProcAddrSpaceType mProcAddressSpaceType; + u8 mMainThreadPriority; + u8 mMainThreadCoreNumber; + u32 mVersion; + u32 mMainThreadStackSize; + std::string mName; + std::string mProductCode; + sSection mAciPos; + sSection mAcidPos; + + void clearVariables(); + void calculateOffsets(); + bool isEqual(const NpdmHeader& other) const; + void copyFrom(const NpdmHeader& other); + }; +} + diff --git a/lib/nx/nx.vcxproj b/lib/nx/nx.vcxproj index 49ef6ff..704c06d 100644 --- a/lib/nx/nx.vcxproj +++ b/lib/nx/nx.vcxproj @@ -23,6 +23,7 @@ + @@ -32,6 +33,7 @@ + diff --git a/lib/nx/nx.vcxproj.filters b/lib/nx/nx.vcxproj.filters index da41b74..9b39e72 100644 --- a/lib/nx/nx.vcxproj.filters +++ b/lib/nx/nx.vcxproj.filters @@ -39,6 +39,9 @@ Header Files + + Header Files + @@ -59,5 +62,8 @@ Source Files + + Source Files + \ No newline at end of file