diff --git a/NNTools.sln b/NNTools.sln index 3abe916..a1dc763 100644 --- a/NNTools.sln +++ b/NNTools.sln @@ -32,6 +32,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libhac", "lib\libhac\libhac EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libhac-hb", "lib\libhac-hb\libhac-hb.vcxproj", "{738CB4FC-CD9E-4B81-A04B-DEADBFA71C63}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libctr", "lib\libctr\libctr.vcxproj", "{96900163-D5A3-42AA-A387-D7D5DAD0A11E}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ctr-test", "programs\ctr-test\ctr-test.vcxproj", "{93C2F450-6682-41F8-9E19-35363BA15EF6}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -104,6 +108,22 @@ Global {738CB4FC-CD9E-4B81-A04B-DEADBFA71C63}.Release|x64.Build.0 = Release|x64 {738CB4FC-CD9E-4B81-A04B-DEADBFA71C63}.Release|x86.ActiveCfg = Release|Win32 {738CB4FC-CD9E-4B81-A04B-DEADBFA71C63}.Release|x86.Build.0 = Release|Win32 + {96900163-D5A3-42AA-A387-D7D5DAD0A11E}.Debug|x64.ActiveCfg = Debug|x64 + {96900163-D5A3-42AA-A387-D7D5DAD0A11E}.Debug|x64.Build.0 = Debug|x64 + {96900163-D5A3-42AA-A387-D7D5DAD0A11E}.Debug|x86.ActiveCfg = Debug|Win32 + {96900163-D5A3-42AA-A387-D7D5DAD0A11E}.Debug|x86.Build.0 = Debug|Win32 + {96900163-D5A3-42AA-A387-D7D5DAD0A11E}.Release|x64.ActiveCfg = Release|x64 + {96900163-D5A3-42AA-A387-D7D5DAD0A11E}.Release|x64.Build.0 = Release|x64 + {96900163-D5A3-42AA-A387-D7D5DAD0A11E}.Release|x86.ActiveCfg = Release|Win32 + {96900163-D5A3-42AA-A387-D7D5DAD0A11E}.Release|x86.Build.0 = Release|Win32 + {93C2F450-6682-41F8-9E19-35363BA15EF6}.Debug|x64.ActiveCfg = Debug|x64 + {93C2F450-6682-41F8-9E19-35363BA15EF6}.Debug|x64.Build.0 = Debug|x64 + {93C2F450-6682-41F8-9E19-35363BA15EF6}.Debug|x86.ActiveCfg = Debug|Win32 + {93C2F450-6682-41F8-9E19-35363BA15EF6}.Debug|x86.Build.0 = Debug|Win32 + {93C2F450-6682-41F8-9E19-35363BA15EF6}.Release|x64.ActiveCfg = Release|x64 + {93C2F450-6682-41F8-9E19-35363BA15EF6}.Release|x64.Build.0 = Release|x64 + {93C2F450-6682-41F8-9E19-35363BA15EF6}.Release|x86.ActiveCfg = Release|Win32 + {93C2F450-6682-41F8-9E19-35363BA15EF6}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -117,6 +137,8 @@ Global {7BE99936-0D40-410D-944B-4513C2EFF8DC} = {170B4A09-1B67-4A62-93AB-116EBCFF4A8C} {91BA9E79-8242-4F7D-B997-0DFEC95EA22B} = {170B4A09-1B67-4A62-93AB-116EBCFF4A8C} {738CB4FC-CD9E-4B81-A04B-DEADBFA71C63} = {170B4A09-1B67-4A62-93AB-116EBCFF4A8C} + {96900163-D5A3-42AA-A387-D7D5DAD0A11E} = {170B4A09-1B67-4A62-93AB-116EBCFF4A8C} + {93C2F450-6682-41F8-9E19-35363BA15EF6} = {E0863FCC-8E72-490D-BE1B-458F12CA8298} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {07DCCACC-D10D-47C9-85AE-FB9C54DB7D62} diff --git a/lib/libctr/include/nn/ctr/CodeBinaryHeader.h b/lib/libctr/include/nn/ctr/CodeBinaryHeader.h new file mode 100644 index 0000000..57a3c9c --- /dev/null +++ b/lib/libctr/include/nn/ctr/CodeBinaryHeader.h @@ -0,0 +1,78 @@ +/* +CodeBinaryHeader.h +(c) 2018 Jakcron + +This is a header to prepend to raw EXEFS .code binaries that provide enough data to be equivalent to an ELF. +*/ + +#pragma once +#include + +#include +#include +#include +#include + + +namespace nn +{ + namespace ctr + { + class CodeBinaryHeader : public fnd::ISerialisable + { + public: + // constructors + CodeBinaryHeader(); + CodeBinaryHeader(const CodeBinaryHeader& other); + + void operator=(const CodeBinaryHeader& other); + bool operator==(const CodeBinaryHeader& other) const; + bool operator!=(const CodeBinaryHeader& other) const; + + // export/import binary + void toBytes(); + void fromBytes(const byte_t* data, size_t len); + const fnd::Vec& getBytes() const; + + // variables + void clear(); + + // mutators + void setCodeBinOffset(uint32_t offset); + uint32_t getCodeBinOffset() const; + + void setCodeBinSize(uint32_t size); + uint32_t getCodeBinSize() const; + + void setStackSize(uint32_t size); + uint32_t getStackSize() const; + + void setBssSize(uint32_t size); + uint32_t getBssSize() const; + + void setTextSegment(const ctr::CodeSegment& segment); + const ctr::CodeSegment& getTextSegment() const; + + void setRODataSegment(const ctr::CodeSegment& segment); + const ctr::CodeSegment& getRODataSegment() const; + + void setDataSegment(const ctr::CodeSegment& segment); + const ctr::CodeSegment& getDataSegment() const; + + private: + const std::string kModuleName = "CODE_BINARY_HEADER"; + + + // serialised data + fnd::Vec mRawBinary; + + // members + uint32_t mCodeBinOffset; + uint32_t mCodeBinSize; + uint32_t mStackSize; + uint32_t mBssSize; + CodeSegment mSegmentList[code::kCodeSegmentNum]; + + }; + } +} diff --git a/lib/libctr/include/nn/ctr/CodeSegment.h b/lib/libctr/include/nn/ctr/CodeSegment.h new file mode 100644 index 0000000..9a199b0 --- /dev/null +++ b/lib/libctr/include/nn/ctr/CodeSegment.h @@ -0,0 +1,37 @@ +/* +CodeSegment.h +(c) 2018 Jakcron +*/ +#pragma once +#include + +namespace nn +{ + namespace ctr + { + class CodeSegment + { + public: + CodeSegment(); + CodeSegment(const CodeSegment& other); + CodeSegment(uint32_t address, uint32_t size, uint32_t page_num); + + void operator=(const CodeSegment& other); + bool operator==(const CodeSegment& other) const; + bool operator!=(const CodeSegment& other) const; + + void setAddress(uint32_t address); + uint32_t getAddress() const; + + void setSize(uint32_t size); + uint32_t getSize() const; + + void setPageNum(uint32_t num); + uint32_t getPageNum() const; + private: + uint32_t mAddress; + uint32_t mSize; + uint32_t mPageNum; + }; + } +} \ No newline at end of file diff --git a/lib/libctr/include/nn/ctr/NcchHeader.h b/lib/libctr/include/nn/ctr/NcchHeader.h new file mode 100644 index 0000000..4ef0570 --- /dev/null +++ b/lib/libctr/include/nn/ctr/NcchHeader.h @@ -0,0 +1,210 @@ +/* +NcchHeader.h +(c) 2018 Jakcron + +This is a 0x100 byte header used to map/hash sections in CXI/CFA files, is preceeded by a RSA-2048 signature over +these 0x100 bytes in such files. It is also used in CCI files in the CardInfo structure + +#Implementation Details + - sizes/offsets are in blocks (usually 512 bytes) + - all offsets in the raw structure are from the start of the file, not the start of the header. +*/ +#pragma once +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace nn +{ +namespace ctr +{ + class NcchHeader : public fnd::ISerialisable + { + public: + class Layout + { + public: + Layout() : + mOffset(0), + mSize(0), + mHashedSize(0), + mHash() + {} + + void operator=(const Layout& other) + { + mOffset = other.mOffset; + mSize = other.mSize; + mHashedSize = other.mHashedSize; + mHash = other.mHash; + } + + bool operator==(const Layout& other) const + { + return (mOffset == other.mOffset) \ + && (mSize == other.mSize) \ + && (mHashedSize == other.mHashedSize) \ + && (mHash == other.mHash); + } + + bool operator!=(const Layout& other) const + { + return !(*this == other); + } + + void setOffset(size_t offset) + { + mOffset = offset; + } + + size_t getOffset() const + { + return mOffset; + } + + void setSize(size_t size) + { + mSize = size; + } + + size_t getSize() const + { + return mSize; + } + + void setHashedSize(size_t hashed_size) + { + mHashedSize = hashed_size; + } + + size_t getHashedSize() const + { + return mHashedSize; + } + + void setHash(const fnd::sha::sSha256Hash& hash) + { + mHash = hash; + } + + const fnd::sha::sSha256Hash& getHash() const + { + return mHash; + } + private: + size_t mOffset; + size_t mSize; + size_t mHashedSize; + fnd::sha::sSha256Hash mHash; + }; + + // constructors + NcchHeader(); + NcchHeader(const NcchHeader& other); + + void operator=(const NcchHeader& other); + bool operator==(const NcchHeader& other) const; + bool operator!=(const NcchHeader& other) const; + + // export/import binary + void toBytes(); + void fromBytes(const byte_t* data, size_t len); + const fnd::Vec& getBytes() const; + + // variables + void clear(); + + // data layout mutators + void setNcchBinarySize(size_t size); + size_t getNcchBinarySize() const; + + void setExtendedHeaderLayout(const Layout layout); + const Layout& getExtendedHeaderLayout() const; + + void setPlainRegionLayout(const Layout layout); + const Layout& getPlainRegionLayout() const; + + void setLogoLayout(const Layout layout); + const Layout& getLogoLayout() const; + + void setExefsLayout(const Layout layout); + const Layout& getExefsLayout() const; + + void setRomfsLayout(const Layout layout); + const Layout& getRomfsLayout() const; + + // property mutators + void setNcchType(ncch::NcchType type); + ncch::NcchType getNcchType() const; + + void setNewCryptoFlag(ncch::NewCryptoFlag flag); + ncch::NewCryptoFlag getNewCryptoFlag() const; + + void setPlatform(ncch::Platform platform); + ncch::Platform getPlatform() const; + + void setFormType(ncch::FormType type); + ncch::FormType getFormType() const; + + void setContentType(ncch::ContentType type); + ncch::ContentType getContentType() const; + + void setBlockSize(ncch::BlockSizeFlag flag); + ncch::BlockSizeFlag getBlockSize() const; + + void setOtherFlagList(const fnd::List& list); + const fnd::List& getOtherFlagList() const; + + void setCompanyCode(const std::string& company_code); + const std::string& getCompanyCode() const; + + void setTitleId(const ctr::ProgramId& id); + const ctr::ProgramId& getTitleId() const; + + void setProgramId(const ctr::ProgramId& id); + const ctr::ProgramId& getProgramId() const; + + void setProductCode(const std::string& product_code); + const std::string& getProductCode() const; + + void setSeedChecksum(uint32_t checksum); + uint32_t getSeedChecksum() const; + + private: + const std::string kModuleName = "NCCH_HEADER"; + + // serialised data + fnd::Vec mRawBinary; + + // members + size_t mNcchBinarySize; + Layout mExHeaderLayout; + Layout mPlainRegionLayout; + Layout mLogoLayout; + Layout mExefsLayout; + Layout mRomfsLayout; + + std::string mCompanyCode; + std::string mProductCode; + + uint32_t mSeedChecksum; + + ncch::NcchType mNcchType; + ProgramId mTitleId; + ProgramId mProgramId; + + ncch::NewCryptoFlag mNewCryptoFlag; + ncch::Platform mPlatform; + ncch::FormType mFormType; + ncch::ContentType mContentType; + ncch::BlockSizeFlag mBlockSize; + fnd::List mOtherFlagList; + }; +} +} \ No newline at end of file diff --git a/lib/libctr/include/nn/ctr/ProgramId.h b/lib/libctr/include/nn/ctr/ProgramId.h new file mode 100644 index 0000000..b019f53 --- /dev/null +++ b/lib/libctr/include/nn/ctr/ProgramId.h @@ -0,0 +1,48 @@ +/* +ProgramId.h +(c) 2018 Jakcron + +This is a wrapper class for the 64bit unique id assigned to programs +*/ +#pragma once +#include + +namespace nn +{ +namespace ctr +{ + class ProgramId + { + public: + ProgramId(); + ProgramId(const ProgramId &other); + ProgramId(uint64_t prog_id); + ProgramId(uint16_t device_group, uint16_t category, uint32_t unique_id, uint8_t variation); + + void operator=(const ProgramId& other); + bool operator==(const ProgramId& other) const; + bool operator!=(const ProgramId& other) const; + + // mutators + void setInnerValue(uint64_t id); + uint64_t getInnerValue() const; + + void setDeviceGroup(uint16_t device_group); + uint16_t getDeviceGroup() const; + + void setCategory(uint16_t category); + uint16_t getCategory() const; + + void setUniqueId(uint32_t uid); + uint32_t getUniqueId() const; + + void setVariation(uint8_t variation); + uint8_t getVariation() const; + + private: + + + uint64_t mProgramId; + }; +} +} \ No newline at end of file diff --git a/lib/libctr/include/nn/ctr/code.h b/lib/libctr/include/nn/ctr/code.h new file mode 100644 index 0000000..e4c7874 --- /dev/null +++ b/lib/libctr/include/nn/ctr/code.h @@ -0,0 +1,44 @@ +#pragma once +#include +#include +#include + +namespace nn +{ + namespace ctr + { + namespace code + { + static const uint32_t kStructMagic = _MAKE_STRUCT_MAGIC_U32("CODE"); + static const uint32_t kCtrFormatIdent = _MAKE_STRUCT_MAGIC_U32("CTR0"); + static const size_t kCodeSegmentNum = 3; + + enum CodeSegmentIndex + { + TEXT_SEGMENT, + RODATA_SEGMENT, + DATA_SEGMENT, + }; + } +#pragma pack(push,1) + struct sCodeBinaryHeader + { + le_uint32_t st_magic; + le_uint32_t format_ident; + le_uint32_t flags; + byte_t reserved_00[4]; + le_uint32_t code_bin_offset; + le_uint32_t code_bin_size; + le_uint32_t stack_size; + le_uint32_t bss_size; + struct sCodeSegment + { + le_uint32_t v_addr; + le_uint32_t size; + le_uint32_t page_num; + le_uint32_t padding; + } segments[code::kCodeSegmentNum]; + }; +#pragma pack(pop) + } +} \ No newline at end of file diff --git a/lib/libctr/include/nn/ctr/macro.h b/lib/libctr/include/nn/ctr/macro.h new file mode 100644 index 0000000..fec3270 --- /dev/null +++ b/lib/libctr/include/nn/ctr/macro.h @@ -0,0 +1,5 @@ +#pragma once +#include + +#define _MAKE_STRUCT_MAGIC_U32(x) ((uint32_t)(x[3]) << 24 | (uint32_t)(x[2]) << 16 | (uint32_t)(x[1]) << 8 | (uint32_t)(x[0])) +#define _MAKE_STRUCT_MAGIC_U64(x) ((uint64_t)(x[7]) << 56 | (uint64_t)(x[6]) << 48 | (uint64_t)(x[5]) << 40 | (uint64_t)(x[4]) << 32 | (uint64_t)(x[3]) << 24 | (uint64_t)(x[2]) << 16 | (uint64_t)(x[1]) << 8 | (uint64_t)(x[0])) \ No newline at end of file diff --git a/lib/libctr/include/nn/ctr/ncch.h b/lib/libctr/include/nn/ctr/ncch.h new file mode 100644 index 0000000..3805f15 --- /dev/null +++ b/lib/libctr/include/nn/ctr/ncch.h @@ -0,0 +1,127 @@ +#pragma once +#include +#include +#include +#include + +namespace nn +{ +namespace ctr +{ + namespace ncch + { + static const uint32_t kNcchStructMagic = _MAKE_STRUCT_MAGIC_U32("NCCH"); + static const size_t kCompanyCodeLen = 2; + static const size_t kProductCodeLen = 0x10; + static const byte_t kFormTypeBitWidth = 2; + static const byte_t kContentTypeBitWidth = 6; + + enum NcchType + { + TYPE_CFA = 0, + TYPE_CXI = 2 + }; + + enum NewCryptoFlag + { + CRYPTO_DEFAULT = 0x00, + CRYPTO_25 = 0x01, + CRYPTO_18 = 0x0A, + CRYPTO_1B = 0x0B + }; + + enum Platform + { + PLATFORM_UNDEFINED, + PLATFORM_CTR, + PLATFORM_SNAKE, + }; + + enum FormType + { + UNASSIGNED, + SIMPLE_CONTENT, + EXECUTABLE_WITHOUT_ROMFS, + EXECUTABLE + }; + + enum ContentType + { + APPLICATION, + SYSTEM_UPDATE, + MANUAL, + CHILD, + TRIAL, + EXTENDED_SYSTEM_UPDATE + }; + + enum BlockSizeFlag + { + BLOCK_SIZE_512 + }; + + enum OtherFlag + { + FIXED_AES_KEY = 0, + NO_MOUNT_ROMFS = 1, + NO_AES = 2, + SEED_KEY = 5, + MANUAL_DISCLOSURE = 6, + }; + + enum FlagIndex + { + CRYPTO_FLAG_INDEX = 3, + PLATFORM_INDEX = 4, + CONTENT_TYPE_INDEX = 5, + BLOCK_SIZE_INDEX = 6, + OTHER_FLAG_INDEX = 7 + }; + } + +#pragma pack(push, 1) + struct sLayout + { + le_uint32_t block_offset; + le_uint32_t block_num; + }; + + struct sLayoutHashed : public sLayout + { + le_uint32_t block_num_hash_protected; + }; + + struct sNcchHeader + { + le_uint32_t st_magic; + le_uint32_t size; + le_uint64_t title_id; + char company_code[ncch::kCompanyCodeLen]; + le_uint16_t ncch_type; + le_uint32_t seed_checksum; + le_uint64_t program_id; + byte_t reserved1[0x10]; + fnd::sha::sSha256Hash logo_hash; + char product_code[ncch::kProductCodeLen]; + fnd::sha::sSha256Hash exheader_hash; + le_uint32_t exheader_size; + byte_t reserved2[0x4]; + byte_t flags[8]; + sLayout plain_region; + sLayout logo; + sLayoutHashed exefs; + byte_t reserved3[4]; + sLayoutHashed romfs; + byte_t reserved4[4]; + fnd::sha::sSha256Hash exefs_hash; + fnd::sha::sSha256Hash romfs_hash; + }; + + struct sNcchHeaderBlock + { + byte_t rsa_signature[fnd::rsa::kRsa2048Size]; + sNcchHeader header; + }; +#pragma pack(pop) +} +} \ No newline at end of file diff --git a/lib/libctr/include/nn/ctr/prog_id.h b/lib/libctr/include/nn/ctr/prog_id.h new file mode 100644 index 0000000..f98ce85 --- /dev/null +++ b/lib/libctr/include/nn/ctr/prog_id.h @@ -0,0 +1,87 @@ +#pragma once +#include + +namespace nn +{ + namespace ctr + { + namespace prog_id + { + static const uint64_t kDeviceGroupMask = 0xffff000000000000; + static const uint64_t kDeviceGroupShift = 48; + static const uint64_t kCategoryMask = 0x0000ffff00000000; + static const uint64_t kCategoryShift = 32; + static const uint64_t kUniqueIdMask = 0x00000000ffffff00; + static const uint64_t kUniqueIdShift = 8; + static const uint64_t kVariationMask = 0x00000000000000ff; + static const uint64_t kVariationShift = 0; + + enum DeviceGroup + { + DEVICE_TWL = 0x0003, + DEVICE_CTR = 0x0004, + DEVICE_CAFE = 0x0005, + }; + + enum CategoryFlags + { + CATEGORY_FLAG_NORMAL = 0, + CATEGORY_FLAG_DLP_CHILD = _BIT(0), + CATEGORY_FLAG_DEMO = _BIT(1), + CATEGORY_FLAG_CONTENTS = _BIT(0) | _BIT(1), + CATEGORY_FLAG_ADD_ON_CONTENTS = _BIT(2), + CATEGORY_FLAG_PATCH = _BIT(1) | _BIT(2), + CATEGORY_FLAG_NOT_EXECUTABLE = _BIT(3), + CATEGORY_FLAG_SYSTEM = _BIT(4), + CATEGORY_FLAG_REQUIRE_BATCH_UPDATE = _BIT(5), + CATEGORY_FLAG_NOT_REQUIRE_USER_APPROVAL = _BIT(6), + CATEGORY_FLAG_NOT_REQUIRE_RIGHT_FOR_MOUNT = _BIT(7), + CATEGORY_FLAG_CAN_SKIP_CONVERT_JUMP_ID = _BIT(8), + CATEGORY_FLAG_TWL_LEGACY = _BIT(15), + }; + + enum CategoryType + { + CATEGORY_APPLICATION = (CATEGORY_FLAG_NORMAL), + CATEGORY_DLP_CHILD = (CATEGORY_FLAG_DLP_CHILD), + CATEGORY_DEMO = (CATEGORY_FLAG_DEMO), + CATEGORY_CONTENTS = (CATEGORY_FLAG_CONTENTS), + CATEGORY_PATCH = (CATEGORY_FLAG_PATCH | + CATEGORY_FLAG_NOT_EXECUTABLE), + CATEGORY_ADD_ON_CONTENTS = (CATEGORY_FLAG_ADD_ON_CONTENTS | + CATEGORY_FLAG_NOT_EXECUTABLE | + CATEGORY_FLAG_NOT_REQUIRE_RIGHT_FOR_MOUNT), + CATEGORY_FIRMWARE = (CATEGORY_FLAG_SYSTEM | + CATEGORY_FLAG_NOT_EXECUTABLE | + CATEGORY_FLAG_REQUIRE_BATCH_UPDATE | + CATEGORY_FLAG_CAN_SKIP_CONVERT_JUMP_ID), + CATEGORY_MODULE = (CATEGORY_FLAG_SYSTEM | + CATEGORY_FLAG_REQUIRE_BATCH_UPDATE | + CATEGORY_FLAG_CAN_SKIP_CONVERT_JUMP_ID), + CATEGORY_APPLET = (CATEGORY_FLAG_SYSTEM | + CATEGORY_FLAG_REQUIRE_BATCH_UPDATE), + CATEGORY_SYSTEM_APPLICATION = (CATEGORY_FLAG_SYSTEM), + CATEGORY_SYSTEM_CONTENT = (CATEGORY_FLAG_SYSTEM | + CATEGORY_FLAG_CONTENTS | + CATEGORY_FLAG_NOT_EXECUTABLE), + CATEGORY_SHARED_CONTENT = (CATEGORY_FLAG_SYSTEM | + CATEGORY_FLAG_CONTENTS | + CATEGORY_FLAG_NOT_EXECUTABLE | + CATEGORY_FLAG_NOT_REQUIRE_RIGHT_FOR_MOUNT), + CATEGORY_AUTO_UPDATE_CONTENT = (CATEGORY_FLAG_SYSTEM | + CATEGORY_FLAG_CONTENTS | + CATEGORY_FLAG_NOT_EXECUTABLE | + CATEGORY_FLAG_NOT_REQUIRE_RIGHT_FOR_MOUNT | + CATEGORY_FLAG_NOT_REQUIRE_USER_APPROVAL), + }; + + enum DeviceMask + { + ID_MASK = _BIT(28) - 1, + ID_MASK_CTR = 0, // 3DS + ID_MASK_CAFE = _BIT(28), // WiiU + ID_MASK_SNAKE = _BIT(29) // n3DS + }; + } + } +} diff --git a/lib/libctr/libctr.vcxproj b/lib/libctr/libctr.vcxproj new file mode 100644 index 0000000..b14f645 --- /dev/null +++ b/lib/libctr/libctr.vcxproj @@ -0,0 +1,150 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {96900163-D5A3-42AA-A387-D7D5DAD0A11E} + libctr + 10.0.16299.0 + + + + StaticLibrary + true + v141 + MultiByte + + + StaticLibrary + false + v141 + true + MultiByte + + + StaticLibrary + true + v141 + MultiByte + + + StaticLibrary + false + v141 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + Level3 + Disabled + true + true + ..\libfnd\include;..\libctr\include;..\libes\include;..\libpki\include; + _MBCS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + + + Level3 + Disabled + true + true + ..\libfnd\include;..\libctr\include;..\libes\include;..\libpki\include; + _MBCS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + + + Level3 + MaxSpeed + true + true + true + true + ..\libfnd\include;..\libctr\include;..\libes\include;..\libpki\include; + _MBCS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + true + true + + + + + Level3 + MaxSpeed + true + true + true + true + ..\libfnd\include;..\libctr\include;..\libes\include;..\libpki\include; + _MBCS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lib/libctr/libctr.vcxproj.filters b/lib/libctr/libctr.vcxproj.filters new file mode 100644 index 0000000..d035a0d --- /dev/null +++ b/lib/libctr/libctr.vcxproj.filters @@ -0,0 +1,63 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + + + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/lib/libctr/libctr.vcxproj.user b/lib/libctr/libctr.vcxproj.user new file mode 100644 index 0000000..be25078 --- /dev/null +++ b/lib/libctr/libctr.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/lib/libctr/makefile b/lib/libctr/makefile new file mode 100644 index 0000000..1dec5ec --- /dev/null +++ b/lib/libctr/makefile @@ -0,0 +1,47 @@ +# Sources +SRC_DIR = source +OBJS = $(foreach dir,$(SRC_DIR),$(subst .cpp,.o,$(wildcard $(dir)/*.cpp))) $(foreach dir,$(SRC_DIR),$(subst .c,.o,$(wildcard $(dir)/*.c))) + +# External dependencies +DEPENDS = fnd crypto +LIB_DIR = .. +INCS = -I"include" $(foreach dep,$(DEPENDS), -I"$(LIB_DIR)/lib$(dep)/include") + + +# Compiler Settings +CXXFLAGS = -std=c++11 $(INCS) -D__STDC_FORMAT_MACROS -Wall -Wno-unused-but-set-variable -Wno-unused-value +CFLAGS = -std=c11 $(INCS) -Wall -Wno-unused-but-set-variable -Wno-unused-value +ARFLAGS = cr -o +ifeq ($(OS),Windows_NT) + # Windows Only Flags/Libs + CC = x86_64-w64-mingw32-gcc + CXX = x86_64-w64-mingw32-g++ + CFLAGS += + CXXFLAGS += +else + UNAME = $(shell uname -s) + ifeq ($(UNAME), Darwin) + # MacOS Only Flags/Libs + CFLAGS += + CXXFLAGS += + ARFLAGS = rc + else + # *nix Only Flags/Libs + CFLAGS += + CXXFLAGS += + endif + +endif + +# Output +OUTPUT = $(shell basename $(CURDIR)).a + +main: build + +rebuild: clean build + +build: $(OBJS) + ar $(ARFLAGS) $(OUTPUT) $(OBJS) + +clean: + rm -rf $(OUTPUT) $(OBJS) diff --git a/lib/libctr/notes.txt b/lib/libctr/notes.txt new file mode 100644 index 0000000..f00ffb3 --- /dev/null +++ b/lib/libctr/notes.txt @@ -0,0 +1,28 @@ +Proposed classes +NcchHeader +NcsdHeader +CiaHeader +CiaFooter +ExtendedHeader + +ncch: + NcchHeader + ExtendedHeader + SystemControlInfo + Arm11SystemCaps + Arm11KernelCaps + Arm9Caps + ExefsHeader + IvfcHeader + RomfsHeader + RomfsNodes** + +ncsd: + NcsdHeader + CardInfoHeader + +cia: + CiaHeader + CiaFooter + es::Ticket? + es::Tmd? \ No newline at end of file diff --git a/lib/libctr/source/CodeBinaryHeader.cpp b/lib/libctr/source/CodeBinaryHeader.cpp new file mode 100644 index 0000000..07ec726 --- /dev/null +++ b/lib/libctr/source/CodeBinaryHeader.cpp @@ -0,0 +1,201 @@ +#include +#include + +nn::ctr::CodeBinaryHeader::CodeBinaryHeader() : + mRawBinary(), + mCodeBinSize(0), + mStackSize(0), + mBssSize(0), + mSegmentList() +{ +} + +nn::ctr::CodeBinaryHeader::CodeBinaryHeader(const CodeBinaryHeader& other) : + CodeBinaryHeader() +{ + *this = other; +} + +void nn::ctr::CodeBinaryHeader::operator=(const CodeBinaryHeader& other) +{ + clear(); + mCodeBinOffset = other.mCodeBinOffset; + mCodeBinSize = other.mCodeBinSize; + mStackSize = other.mStackSize; + mBssSize = other.mBssSize; + mSegmentList[code::TEXT_SEGMENT] = other.mSegmentList[code::TEXT_SEGMENT]; + mSegmentList[code::RODATA_SEGMENT] = other.mSegmentList[code::RODATA_SEGMENT]; + mSegmentList[code::DATA_SEGMENT] = other.mSegmentList[code::DATA_SEGMENT]; +} + +bool nn::ctr::CodeBinaryHeader::operator==(const CodeBinaryHeader& other) const +{ + return (mCodeBinOffset == other.mCodeBinOffset) \ + && (mCodeBinSize == other.mCodeBinSize) \ + && (mStackSize == other.mStackSize) \ + && (mBssSize == other.mBssSize) \ + && (mSegmentList[code::TEXT_SEGMENT] == other.mSegmentList[code::TEXT_SEGMENT]) \ + && (mSegmentList[code::RODATA_SEGMENT] == other.mSegmentList[code::RODATA_SEGMENT]) \ + && (mSegmentList[code::DATA_SEGMENT] == other.mSegmentList[code::DATA_SEGMENT]); +} + +bool nn::ctr::CodeBinaryHeader::operator!=(const CodeBinaryHeader& other) const +{ + return !(*this == other); +} + +void nn::ctr::CodeBinaryHeader::toBytes() +{ + // allocate memory for header + mRawBinary.clear(); + mRawBinary.alloc(sizeof(sCodeBinaryHeader)); + + // get ptr to header struct + sCodeBinaryHeader* hdr = (sCodeBinaryHeader*)mRawBinary.data(); + + // write variables + hdr->st_magic = code::kStructMagic; + hdr->code_bin_size = le_word(mCodeBinSize); + hdr->stack_size = le_word(mStackSize); + hdr->bss_size = le_word(mBssSize); + for (size_t i = 0; i < code::kCodeSegmentNum; i++) + { + hdr->segments[i].v_addr = le_word(mSegmentList[i].getAddress()); + hdr->segments[i].size = le_word(mSegmentList[i].getSize()); + hdr->segments[i].page_num = le_word(mSegmentList[i].getPageNum()); + } +} + +void nn::ctr::CodeBinaryHeader::fromBytes(const byte_t* data, size_t len) +{ + // check size + if (len < sizeof(sCodeBinaryHeader)) + { + throw fnd::Exception(kModuleName, "CODE header corrupt (binary is too small)"); + } + + // clear variables + clear(); + + // save a copy of the header + sCodeBinaryHeader hdr; + memcpy((void*)&hdr, data, sizeof(sCodeBinaryHeader)); + + // check magic + if (hdr.st_magic.get() != code::kStructMagic) + { + throw fnd::Exception(kModuleName, "CODE header corrupt (unrecognised header signature)"); + } + + if (hdr.format_ident.get() != code::kCtrFormatIdent) + { + throw fnd::Exception(kModuleName, "CODE header corrupt (unsupported format version)"); + } + + // allocate memory for header + mRawBinary.alloc(sizeof(sCodeBinaryHeader)); + memcpy(mRawBinary.data(), data, mRawBinary.size()); + + // save variables + mCodeBinOffset = hdr.code_bin_offset.get(); + mCodeBinSize = hdr.code_bin_size.get(); + mStackSize = hdr.stack_size.get(); + mBssSize = hdr.bss_size.get(); + for (size_t i = 0; i < code::kCodeSegmentNum; i++) + { + mSegmentList[i].setAddress(hdr.segments[i].v_addr.get()); + mSegmentList[i].setSize(hdr.segments[i].size.get()); + mSegmentList[i].setPageNum(hdr.segments[i].page_num.get()); + } +} + +const fnd::Vec& nn::ctr::CodeBinaryHeader::getBytes() const +{ + return mRawBinary; +} + + +void nn::ctr::CodeBinaryHeader::clear() +{ + mRawBinary.clear(); + mCodeBinOffset = 0; + mCodeBinSize = 0; + mStackSize = 0; + mBssSize = 0; + for (size_t i = 0; i < code::kCodeSegmentNum; i++) + { + mSegmentList[i].setAddress(0); + mSegmentList[i].setSize(0); + mSegmentList[i].setPageNum(0); + } +} + +void nn::ctr::CodeBinaryHeader::setCodeBinOffset(uint32_t offset) +{ + mCodeBinOffset = offset; +} + +uint32_t nn::ctr::CodeBinaryHeader::getCodeBinOffset() const +{ + return mCodeBinOffset; +} + +void nn::ctr::CodeBinaryHeader::setCodeBinSize(uint32_t size) +{ + mCodeBinSize = size; +} + +uint32_t nn::ctr::CodeBinaryHeader::getCodeBinSize() const +{ + return mCodeBinSize; +} + +void nn::ctr::CodeBinaryHeader::setStackSize(uint32_t size) +{ + mStackSize = size; +} + +uint32_t nn::ctr::CodeBinaryHeader::getStackSize() const +{ + return mStackSize; +} + +void nn::ctr::CodeBinaryHeader::setBssSize(uint32_t size) +{ + mBssSize = size; +} + +uint32_t nn::ctr::CodeBinaryHeader::getBssSize() const +{ + return mBssSize; +} + +void nn::ctr::CodeBinaryHeader::setTextSegment(const nn::ctr::CodeSegment & segment) +{ + mSegmentList[code::TEXT_SEGMENT] = segment; +} + +const nn::ctr::CodeSegment & nn::ctr::CodeBinaryHeader::getTextSegment() const +{ + return mSegmentList[code::TEXT_SEGMENT]; +} + +void nn::ctr::CodeBinaryHeader::setRODataSegment(const nn::ctr::CodeSegment & segment) +{ + mSegmentList[code::RODATA_SEGMENT] = segment; +} + +const nn::ctr::CodeSegment & nn::ctr::CodeBinaryHeader::getRODataSegment() const +{ + return mSegmentList[code::RODATA_SEGMENT]; +} + +void nn::ctr::CodeBinaryHeader::setDataSegment(const nn::ctr::CodeSegment & segment) +{ + mSegmentList[code::DATA_SEGMENT] = segment; +} + +const nn::ctr::CodeSegment & nn::ctr::CodeBinaryHeader::getDataSegment() const +{ + return mSegmentList[code::DATA_SEGMENT]; +} diff --git a/lib/libctr/source/CodeSegment.cpp b/lib/libctr/source/CodeSegment.cpp new file mode 100644 index 0000000..ae8dcaa --- /dev/null +++ b/lib/libctr/source/CodeSegment.cpp @@ -0,0 +1,70 @@ +#include + +nn::ctr::CodeSegment::CodeSegment() : + mAddress(0), + mSize(0), + mPageNum(0) +{ +} + +nn::ctr::CodeSegment::CodeSegment(const CodeSegment& other) : + mAddress(other.mAddress), + mSize(other.mSize), + mPageNum(other.mPageNum) +{} + + +nn::ctr::CodeSegment::CodeSegment(uint32_t address, uint32_t size, uint32_t page_num) : + mAddress(address), + mSize(size), + mPageNum(page_num) +{} + +void nn::ctr::CodeSegment::operator=(const CodeSegment& other) +{ + mAddress = other.mAddress; + mSize = other.mSize; + mPageNum = other.mPageNum; +} + +bool nn::ctr::CodeSegment::operator==(const CodeSegment& other) const +{ + return (mAddress == other.mAddress) \ + && (mSize == other.mSize) \ + && (mPageNum == other.mPageNum); +} + +bool nn::ctr::CodeSegment::operator!=(const CodeSegment& other) const +{ + return !(*this == other); +} + +void nn::ctr::CodeSegment::setAddress(uint32_t address) +{ + mAddress = address; +} + +uint32_t nn::ctr::CodeSegment::getAddress() const +{ + return mAddress; +} + +void nn::ctr::CodeSegment::setSize(uint32_t size) +{ + mSize = size; +} + +uint32_t nn::ctr::CodeSegment::getSize() const +{ + return mSize; +} + +void nn::ctr::CodeSegment::setPageNum(uint32_t num) +{ + mPageNum = num; +} + +uint32_t nn::ctr::CodeSegment::getPageNum() const +{ + return mPageNum; +} diff --git a/lib/libctr/source/NcchHeader.cpp b/lib/libctr/source/NcchHeader.cpp new file mode 100644 index 0000000..cdb958a --- /dev/null +++ b/lib/libctr/source/NcchHeader.cpp @@ -0,0 +1,473 @@ +#include + +nn::ctr::NcchHeader::NcchHeader() : + mRawBinary(), + mNcchBinarySize(), + mExHeaderLayout(), + mPlainRegionLayout(), + mLogoLayout(), + mExefsLayout(), + mRomfsLayout(), + mCompanyCode(), + mProductCode(), + mSeedChecksum(), + mNcchType(), + mTitleId(), + mProgramId(), + mNewCryptoFlag(), + mPlatform(), + mFormType(), + mContentType(), + mBlockSize(), + mOtherFlagList() +{ +} + +nn::ctr::NcchHeader::NcchHeader(const NcchHeader& other) : + NcchHeader() +{ + *this = other; +} + +void nn::ctr::NcchHeader::operator=(const NcchHeader& other) +{ + clear(); + mRawBinary = other.mRawBinary; + mNcchBinarySize = other.mNcchBinarySize; + mExHeaderLayout = other.mExHeaderLayout; + mPlainRegionLayout = other.mPlainRegionLayout; + mLogoLayout = other.mLogoLayout; + mExefsLayout = other.mExefsLayout; + mRomfsLayout = other.mRomfsLayout; + mCompanyCode = other.mCompanyCode; + mProductCode = other.mProductCode; + mSeedChecksum = other.mSeedChecksum; + mNcchType = other.mNcchType; + mTitleId = other.mTitleId; + mProgramId = other.mProgramId; + mNewCryptoFlag = other.mNewCryptoFlag; + mPlatform = other.mPlatform; + mFormType = other.mFormType; + mContentType = other.mContentType; + mBlockSize = other.mBlockSize; + mOtherFlagList = other.mOtherFlagList; +} + +bool nn::ctr::NcchHeader::operator==(const NcchHeader& other) const +{ + return (mRawBinary == other.mRawBinary) \ + && (mNcchBinarySize == other.mNcchBinarySize) \ + && (mExHeaderLayout == other.mExHeaderLayout) \ + && (mPlainRegionLayout == other.mPlainRegionLayout) \ + && (mLogoLayout == other.mLogoLayout) \ + && (mExefsLayout == other.mExefsLayout) \ + && (mRomfsLayout == other.mRomfsLayout) \ + && (mCompanyCode == other.mCompanyCode) \ + && (mProductCode == other.mProductCode) \ + && (mSeedChecksum == other.mSeedChecksum) \ + && (mNcchType == other.mNcchType) \ + && (mTitleId == other.mTitleId) \ + && (mProgramId == other.mProgramId) \ + && (mNewCryptoFlag == other.mNewCryptoFlag) \ + && (mPlatform == other.mPlatform) \ + && (mFormType == other.mFormType) \ + && (mContentType == other.mContentType) \ + && (mBlockSize == other.mBlockSize) \ + && (mOtherFlagList == other.mOtherFlagList); +} + +bool nn::ctr::NcchHeader::operator!=(const NcchHeader& other) const +{ + return !(*this == other); +} + +void nn::ctr::NcchHeader::toBytes() +{ + mRawBinary.alloc(sizeof(sNcchHeader)); + nn::ctr::sNcchHeader* hdr = (nn::ctr::sNcchHeader*)mRawBinary.data(); + + // set header identifers + hdr->st_magic = ncch::kNcchStructMagic; + hdr->ncch_type = mNcchType; + + // variable to store flag before commiting to header + byte_t otherflag = 0; + byte_t contentflag = 0; + for (size_t i = 0; i < mOtherFlagList.size(); i++) + { + _SET_BIT(otherflag, mOtherFlagList[i]); + } + contentflag |= (mContentType & _BITMASK(ncch::kContentTypeBitWidth)) << ncch::kFormTypeBitWidth; + contentflag |= (mFormType & _BITMASK(ncch::kFormTypeBitWidth)); + + // set flags + hdr->flags[ncch::CRYPTO_FLAG_INDEX] = mNewCryptoFlag; + hdr->flags[ncch::PLATFORM_INDEX] = mPlatform; + hdr->flags[ncch::CONTENT_TYPE_INDEX] = contentflag; + hdr->flags[ncch::BLOCK_SIZE_INDEX] = mBlockSize; + hdr->flags[ncch::OTHER_FLAG_INDEX] = otherflag; + + // set strings + strncpy(hdr->product_code, mProductCode.c_str(), ncch::kProductCodeLen); + strncpy(hdr->company_code, mCompanyCode.c_str(), ncch::kCompanyCodeLen); + + // misc + hdr->title_id = mTitleId.getInnerValue(); + hdr->program_id = mProgramId.getInnerValue(); + hdr->seed_checksum = mSeedChecksum; + + // exheader + hdr->exheader_size = mExHeaderLayout.getSize(); + hdr->exheader_hash = mExHeaderLayout.getHash(); + + // block size + size_t block_size = 0; + if (mBlockSize == ncch::BLOCK_SIZE_512) + block_size = 512; + else + throw fnd::Exception(kModuleName, "Illegal block size flag"); + + // plain region + if (mPlainRegionLayout.getOffset() % block_size || mPlainRegionLayout.getSize() % block_size) + throw fnd::Exception(kModuleName, "Plain region geometry not aligned to block size"); + hdr->plain_region.block_offset = mPlainRegionLayout.getOffset() / block_size; + hdr->plain_region.block_num = mPlainRegionLayout.getSize() / block_size; + + // logo + if (mLogoLayout.getOffset() % block_size || mLogoLayout.getSize() % block_size) + throw fnd::Exception(kModuleName, "Logo geometry not aligned to block size"); + hdr->logo.block_offset = mLogoLayout.getOffset() / block_size; + hdr->logo.block_num = mLogoLayout.getSize() / block_size; + hdr->logo_hash = mLogoLayout.getHash(); + + // exefs + if (mExefsLayout.getOffset() % block_size || mExefsLayout.getSize() % block_size || mExefsLayout.getHashedSize() % block_size) + throw fnd::Exception(kModuleName, "ExeFs geometry not aligned to block size"); + hdr->exefs.block_offset = mExefsLayout.getOffset() / block_size; + hdr->exefs.block_num = mExefsLayout.getSize() / block_size; + hdr->exefs.block_num_hash_protected = mExefsLayout.getHashedSize() / block_size; + hdr->exefs_hash = mExefsLayout.getHash(); + + // romfs + if (mRomfsLayout.getOffset() % block_size || mRomfsLayout.getSize() % block_size || mRomfsLayout.getHashedSize() % block_size) + throw fnd::Exception(kModuleName, "RomFs geometry not aligned to block size"); + hdr->romfs.block_offset = mRomfsLayout.getOffset() / block_size; + hdr->romfs.block_num = mRomfsLayout.getSize() / block_size; + hdr->romfs.block_num_hash_protected = mRomfsLayout.getHashedSize() / block_size; + hdr->romfs_hash = mRomfsLayout.getHash(); +} + +void nn::ctr::NcchHeader::fromBytes(const byte_t* data, size_t len) +{ + // check input data size + if (len < sizeof(sNcchHeader)) + { + throw fnd::Exception(kModuleName, "NCCH header size is too small"); + } + + // clear internal members + clear(); + + // allocate internal local binary copy + mRawBinary.alloc(sizeof(sNcchHeader)); + memcpy(mRawBinary.data(), data, mRawBinary.size()); + + // get ptr + const nn::ctr::sNcchHeader* hdr = (const nn::ctr::sNcchHeader*)mRawBinary.data(); + + // check NCCH signature + if (hdr->st_magic.get() != ncch::kNcchStructMagic) + { + throw fnd::Exception(kModuleName, "NCCH header corrupt (unrecognised header signature)"); + } + + // check NCCH type + if (hdr->ncch_type.get() != ncch::TYPE_CFA && hdr->ncch_type.get() != ncch::TYPE_CXI) + { + throw fnd::Exception(kModuleName, "NCCH header corrupt (unsupported NCCH type)"); + } + + // save flags + mNewCryptoFlag = (ncch::NewCryptoFlag)hdr->flags[ncch::CRYPTO_FLAG_INDEX]; + mPlatform = (ncch::Platform)hdr->flags[ncch::PLATFORM_INDEX]; + mBlockSize = (ncch::BlockSizeFlag)hdr->flags[ncch::BLOCK_SIZE_INDEX]; + mContentType = (ncch::ContentType)((hdr->flags[ncch::CONTENT_TYPE_INDEX] >> ncch::kFormTypeBitWidth) & (byte_t)_BITMASK(ncch::kContentTypeBitWidth)); + mFormType = (ncch::FormType)(hdr->flags[ncch::CONTENT_TYPE_INDEX] & (byte_t)_BITMASK(ncch::kFormTypeBitWidth)); + for (size_t i = 0; i < 8; i++) + { + if (_HAS_BIT(hdr->flags[ncch::OTHER_FLAG_INDEX], i)) + mOtherFlagList.addElement((ncch::OtherFlag)i); + } + + // save strings + if (hdr->company_code[0] != '\0') + mCompanyCode = std::string(hdr->company_code, _MIN(strlen(hdr->company_code), ncch::kCompanyCodeLen)); + if (hdr->product_code[0] != '\0') + mCompanyCode = std::string(hdr->product_code, _MIN(strlen(hdr->product_code), ncch::kProductCodeLen)); + + // save misc + mTitleId = ProgramId(hdr->title_id.get()); + mProgramId = ProgramId(hdr->program_id.get()); + mSeedChecksum = hdr->seed_checksum.get(); + + + // block size + size_t block_size = 0; + if (mBlockSize == ncch::BLOCK_SIZE_512) + block_size = 512; + else + throw fnd::Exception(kModuleName, "NCCH header corrupt (illegal block size flag)"); + + // save layouts + mNcchBinarySize = (size_t)hdr->size.get() * block_size; + + // exheader + if (hdr->exheader_size.get() != 0) + { + mExHeaderLayout.setSize(hdr->exheader_size.get()); + mExHeaderLayout.setHash(hdr->exheader_hash); + } + + // plain region + if (hdr->plain_region.block_num.get() != 0) + { + mPlainRegionLayout.setOffset((size_t)hdr->plain_region.block_offset.get() * block_size); + mPlainRegionLayout.setSize((size_t)hdr->plain_region.block_num.get() * block_size); + } + + // logo region + if (hdr->logo.block_num.get() != 0) + { + mLogoLayout.setOffset((size_t)hdr->logo.block_offset.get() * block_size); + mLogoLayout.setSize((size_t)hdr->logo.block_num.get() * block_size); + mLogoLayout.setHash(hdr->logo_hash); + } + + + // exefs region + if (hdr->exefs.block_num.get() != 0) + { + mExefsLayout.setOffset((size_t)hdr->exefs.block_offset.get() * block_size); + mExefsLayout.setSize((size_t)hdr->exefs.block_num.get() * block_size); + mExefsLayout.setHashedSize((size_t)hdr->exefs.block_num_hash_protected.get() * block_size); + mExefsLayout.setHash(hdr->exefs_hash); + } + + // romfs region + if (hdr->romfs.block_num.get() != 0) + { + mRomfsLayout.setOffset((size_t)hdr->romfs.block_offset.get() * block_size); + mRomfsLayout.setSize((size_t)hdr->romfs.block_num.get() * block_size); + mRomfsLayout.setHashedSize((size_t)hdr->romfs.block_num_hash_protected.get() * block_size); + mRomfsLayout.setHash(hdr->romfs_hash); + } +} + +const fnd::Vec& nn::ctr::NcchHeader::getBytes() const +{ + return mRawBinary; +} + +void nn::ctr::NcchHeader::clear() +{ + mRawBinary.clear(); + mNcchBinarySize = 0; + mExHeaderLayout = Layout(); + mPlainRegionLayout = Layout(); + mLogoLayout = Layout(); + mExefsLayout = Layout(); + mRomfsLayout = Layout(); + mCompanyCode = std::string(); + mProductCode = std::string(); + mSeedChecksum = 0; + mNcchType = ncch::NcchType::TYPE_CFA; + mTitleId = ProgramId(); + mProgramId = ProgramId(); + mNewCryptoFlag = ncch::NewCryptoFlag::CRYPTO_DEFAULT; + mPlatform = ncch::Platform::PLATFORM_CTR; + mFormType = ncch::FormType::UNASSIGNED; + mContentType = ncch::ContentType::APPLICATION; + mBlockSize = ncch::BlockSizeFlag::BLOCK_SIZE_512; + mOtherFlagList.clear(); +} + +void nn::ctr::NcchHeader::setNcchBinarySize(size_t size) +{ + mNcchBinarySize = size; +} + +size_t nn::ctr::NcchHeader::getNcchBinarySize() const +{ + return mNcchBinarySize; +} + +void nn::ctr::NcchHeader::setExtendedHeaderLayout(const Layout layout) +{ + mExHeaderLayout = layout; +} + +const nn::ctr::NcchHeader::Layout& nn::ctr::NcchHeader::getExtendedHeaderLayout() const +{ + return mExHeaderLayout; +} + +void nn::ctr::NcchHeader::setPlainRegionLayout(const Layout layout) +{ + mPlainRegionLayout = layout; +} + +const nn::ctr::NcchHeader::Layout& nn::ctr::NcchHeader::getPlainRegionLayout() const +{ + return mPlainRegionLayout; +} + +void nn::ctr::NcchHeader::setLogoLayout(const Layout layout) +{ + mLogoLayout = layout; +} + +const nn::ctr::NcchHeader::Layout& nn::ctr::NcchHeader::getLogoLayout() const +{ + return mLogoLayout; +} + +void nn::ctr::NcchHeader::setExefsLayout(const Layout layout) +{ + mExefsLayout = layout; +} + +const nn::ctr::NcchHeader::Layout& nn::ctr::NcchHeader::getExefsLayout() const +{ + return mExefsLayout; +} + +void nn::ctr::NcchHeader::setRomfsLayout(const Layout layout) +{ + mRomfsLayout = layout; +} + +const nn::ctr::NcchHeader::Layout& nn::ctr::NcchHeader::getRomfsLayout() const +{ + return mRomfsLayout; +} + +// property mutators +void nn::ctr::NcchHeader::setNcchType(ncch::NcchType type) +{ + mNcchType = type; +} + +nn::ctr::ncch::NcchType nn::ctr::NcchHeader::getNcchType() const +{ + return mNcchType; +} + +void nn::ctr::NcchHeader::setNewCryptoFlag(ncch::NewCryptoFlag flag) +{ + mNewCryptoFlag = flag; +} + +nn::ctr::ncch::NewCryptoFlag nn::ctr::NcchHeader::getNewCryptoFlag() const +{ + return mNewCryptoFlag; +} + +void nn::ctr::NcchHeader::setPlatform(ncch::Platform platform) +{ + mPlatform = platform; +} + +nn::ctr::ncch::Platform nn::ctr::NcchHeader::getPlatform() const +{ + return mPlatform; +} + +void nn::ctr::NcchHeader::setFormType(ncch::FormType type) +{ + mFormType = type; +} + +nn::ctr::ncch::FormType nn::ctr::NcchHeader::getFormType() const +{ + return mFormType; +} + +void nn::ctr::NcchHeader::setContentType(ncch::ContentType type) +{ + mContentType = type; +} + +nn::ctr::ncch::ContentType nn::ctr::NcchHeader::getContentType() const +{ + return mContentType; +} + +void nn::ctr::NcchHeader::setBlockSize(ncch::BlockSizeFlag size) +{ + mBlockSize = size; +} + +nn::ctr::ncch::BlockSizeFlag nn::ctr::NcchHeader::getBlockSize() const +{ + return mBlockSize; +} + +void nn::ctr::NcchHeader::setOtherFlagList(const fnd::List& list) +{ + mOtherFlagList = list; +} + +const fnd::List& nn::ctr::NcchHeader::getOtherFlagList() const +{ + return mOtherFlagList; +} + +void nn::ctr::NcchHeader::setCompanyCode(const std::string& company_code) +{ + mCompanyCode = company_code; +} + +const std::string& nn::ctr::NcchHeader::getCompanyCode() const +{ + return mCompanyCode; +} + +void nn::ctr::NcchHeader::setTitleId(const ctr::ProgramId& id) +{ + mTitleId = id; +} + +const nn::ctr::ProgramId& nn::ctr::NcchHeader::getTitleId() const +{ + return mTitleId; +} + +void nn::ctr::NcchHeader::setProgramId(const ctr::ProgramId& id) +{ + mProgramId = id; +} + +const nn::ctr::ProgramId& nn::ctr::NcchHeader::getProgramId() const +{ + return mProgramId; +} + +void nn::ctr::NcchHeader::setProductCode(const std::string& product_code) +{ + mProductCode = product_code; +} + +const std::string& nn::ctr::NcchHeader::getProductCode() const +{ + return mProductCode; +} + +void nn::ctr::NcchHeader::setSeedChecksum(uint32_t checksum) +{ + mSeedChecksum = checksum; +} + +uint32_t nn::ctr::NcchHeader::getSeedChecksum() const +{ + return mSeedChecksum; +} \ No newline at end of file diff --git a/lib/libctr/source/ProgramId.cpp b/lib/libctr/source/ProgramId.cpp new file mode 100644 index 0000000..b161855 --- /dev/null +++ b/lib/libctr/source/ProgramId.cpp @@ -0,0 +1,91 @@ +#include + +nn::ctr::ProgramId::ProgramId() : + mProgramId(0) +{} + +nn::ctr::ProgramId::ProgramId(const ProgramId & other) : + mProgramId(other.getInnerValue()) +{} + +nn::ctr::ProgramId::ProgramId(uint64_t prog_id) : + mProgramId(prog_id) +{} + +nn::ctr::ProgramId::ProgramId(uint16_t device_group, uint16_t category, uint32_t unique_id, uint8_t variation) : + mProgramId(0) +{ + setDeviceGroup(device_group); + setCategory(category); + setUniqueId(unique_id); + setVariation(variation); +} + +void nn::ctr::ProgramId::operator=(const ProgramId& other) +{ + mProgramId = other.mProgramId; +} + +bool nn::ctr::ProgramId::operator==(const ProgramId& other) const +{ + return mProgramId == other.mProgramId; +} + +bool nn::ctr::ProgramId::operator!=(const ProgramId& other) const +{ + return !(*this == other); +} + +void nn::ctr::ProgramId::setInnerValue(uint64_t id) +{ + mProgramId = id; +} + +uint64_t nn::ctr::ProgramId::getInnerValue() const +{ + return mProgramId; +} + +void nn::ctr::ProgramId::setDeviceGroup(uint16_t device_group) +{ + mProgramId &= ~prog_id::kDeviceGroupMask; + mProgramId |= ((uint64_t)device_group) << prog_id::kDeviceGroupShift; +} + +uint16_t nn::ctr::ProgramId::getDeviceGroup() const +{ + return (mProgramId & prog_id::kDeviceGroupMask) >> prog_id::kDeviceGroupShift; +} + +void nn::ctr::ProgramId::setCategory(uint16_t category) +{ + mProgramId &= ~prog_id::kCategoryMask; + mProgramId |= ((uint64_t)category) << prog_id::kCategoryShift; +} + +uint16_t nn::ctr::ProgramId::getCategory() const +{ + return (mProgramId & prog_id::kCategoryMask) >> prog_id::kCategoryShift; +} + +void nn::ctr::ProgramId::setUniqueId(uint32_t uid) +{ + mProgramId &= ~prog_id::kUniqueIdMask; + mProgramId |= ((uint64_t)uid) << prog_id::kUniqueIdShift; +} + +uint32_t nn::ctr::ProgramId::getUniqueId() const +{ + return (mProgramId & prog_id::kUniqueIdMask) >> prog_id::kUniqueIdShift; +} + +void nn::ctr::ProgramId::setVariation(uint8_t variation) +{ + mProgramId &= ~prog_id::kVariationMask; + mProgramId |= (uint64_t)variation; +} + +uint8_t nn::ctr::ProgramId::getVariation() const +{ + return mProgramId & prog_id::kVariationMask; +} diff --git a/makefile b/makefile index ee116a5..c99fe52 100644 --- a/makefile +++ b/makefile @@ -3,8 +3,8 @@ LIB_DIR = $(PROJECT_DIR)/lib PROGRAM_DIR = $(PROJECT_DIR)/programs BIN_DIR = $(PROJECT_DIR)/bin -LIBS = libpolarssl liblz4 libfnd libes libpki libhac libhac-hb -PROGS = nstool +LIBS = libpolarssl liblz4 libfnd libes libpki libctr libhac libhac-hb +PROGS = nstool ctr-test main: build diff --git a/programs/ctr-test/ctr-test.vcxproj b/programs/ctr-test/ctr-test.vcxproj new file mode 100644 index 0000000..bda45a7 --- /dev/null +++ b/programs/ctr-test/ctr-test.vcxproj @@ -0,0 +1,147 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {93C2F450-6682-41F8-9E19-35363BA15EF6} + ctrtest + 10.0.16299.0 + + + + Application + true + v141 + MultiByte + + + Application + false + v141 + true + MultiByte + + + Application + true + v141 + MultiByte + + + Application + false + v141 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + Level3 + MaxSpeed + true + true + true + true + ..\..\lib\libpki\include;..\..\lib\libes\include;..\..\lib\libfnd\include;..\..\lib\libctr\include; + + + true + true + + + + + Level3 + Disabled + true + true + ..\..\lib\libpki\include;..\..\lib\libes\include;..\..\lib\libfnd\include;..\..\lib\libctr\include; + + + + + Level3 + Disabled + true + true + ..\..\lib\libpki\include;..\..\lib\libes\include;..\..\lib\libfnd\include;..\..\lib\libctr\include; + + + + + Level3 + MaxSpeed + true + true + true + true + ..\..\lib\libpki\include;..\..\lib\libes\include;..\..\lib\libfnd\include;..\..\lib\libctr\include; + + + true + true + + + + + {96900163-d5a3-42aa-a387-d7d5dad0a11e} + + + {7be99936-0d40-410d-944b-4513c2eff8dc} + + + {4d27edb9-5110-44fe-8ce2-d46c5ad3c55b} + + + {b9113734-6e84-44ff-8cf7-58199aa815c5} + + + {394efc16-bd3a-4538-b33d-7ba1edb8dac1} + + + + + + + + + + + + \ No newline at end of file diff --git a/programs/ctr-test/ctr-test.vcxproj.filters b/programs/ctr-test/ctr-test.vcxproj.filters new file mode 100644 index 0000000..3d734e4 --- /dev/null +++ b/programs/ctr-test/ctr-test.vcxproj.filters @@ -0,0 +1,25 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + + + + \ No newline at end of file diff --git a/programs/ctr-test/ctr-test.vcxproj.user b/programs/ctr-test/ctr-test.vcxproj.user new file mode 100644 index 0000000..be25078 --- /dev/null +++ b/programs/ctr-test/ctr-test.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/programs/ctr-test/makefile b/programs/ctr-test/makefile new file mode 100644 index 0000000..95a4bd3 --- /dev/null +++ b/programs/ctr-test/makefile @@ -0,0 +1,39 @@ +# Sources +SRC_DIR = source +OBJS = $(foreach dir,$(SRC_DIR),$(subst .cpp,.o,$(wildcard $(dir)/*.cpp))) $(foreach dir,$(SRC_DIR),$(subst .c,.o,$(wildcard $(dir)/*.c))) + +# External dependencies +DEPENDS = polarssl lz4 fnd ctr +LIB_DIR = ../../lib +LIBS = $(foreach dep,$(DEPENDS), -L"$(LIB_DIR)/lib$(dep)" -l$(dep)) +INCS = $(foreach dep,$(DEPENDS), -I"$(LIB_DIR)/lib$(dep)/include") + +BIN_DIR = bin +OUTPUT = $(BIN_DIR)/$(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) + mkdir -p $(BIN_DIR) + $(CXX) $(OBJS) $(LIBS) -o $(OUTPUT) + +clean: + rm -rf $(OBJS) $(OUTPUT) $(BIN_DIR) \ No newline at end of file diff --git a/programs/ctr-test/source/main.cpp b/programs/ctr-test/source/main.cpp new file mode 100644 index 0000000..e801f4b --- /dev/null +++ b/programs/ctr-test/source/main.cpp @@ -0,0 +1,307 @@ +/* +(c) 2018 Jakcron +*/ +#include + +#include +#include +#include +#include +#include + +void dump_code_header(const nn::ctr::CodeBinaryHeader& codeHdr) +{ + printf("Code Bin Size: 0x%08x\n", codeHdr.getCodeBinSize()); + printf("Stack Size: 0x%08x\n", codeHdr.getStackSize()); + printf("BSS Size: 0x%08x\n", codeHdr.getBssSize()); + printf("Text Segment:\n"); + printf(" Addr: 0x%08x\n", codeHdr.getTextSegment().getAddress()); + printf(" Size: 0x%08x\n", codeHdr.getTextSegment().getSize()); + printf(" PageNum: 0x%08x\n", codeHdr.getTextSegment().getPageNum()); + printf("RO Data Segment:\n"); + printf(" Addr: 0x%08x\n", codeHdr.getRODataSegment().getAddress()); + printf(" Size: 0x%08x\n", codeHdr.getRODataSegment().getSize()); + printf(" PageNum: 0x%08x\n", codeHdr.getRODataSegment().getPageNum()); + printf("RW Data Segment:\n"); + printf(" Addr: 0x%08x\n", codeHdr.getDataSegment().getAddress()); + printf(" Size: 0x%08x\n", codeHdr.getDataSegment().getSize()); + printf(" PageNum: 0x%08x\n", codeHdr.getDataSegment().getPageNum()); +} + +const char* dump_ncch_header_ncch_type(uint16_t type) +{ + const char* ret_string = nullptr; + + switch (type) + { + case (nn::ctr::ncch::TYPE_CFA): + ret_string = "CTR File Archive (CFA)"; + break; + case (nn::ctr::ncch::TYPE_CXI): + ret_string = "CTR Excutable Image (CXI)"; + break; + default: + ret_string = "Unknown"; + } + + return ret_string; +} + +const char* dump_ncch_header_crypto_flag(uint8_t type) +{ + const char* ret_string = nullptr; + + switch (type) + { + case (nn::ctr::ncch::CRYPTO_DEFAULT): + ret_string = "[CTR|SNAKE] 0x2C"; + break; + case (nn::ctr::ncch::CRYPTO_25): + ret_string = "[CTR|SNAKE] 0x2C/0x25"; + break; + case (nn::ctr::ncch::CRYPTO_18): + ret_string = "[SNAKE] 0x2C/0x18"; + break; + case (nn::ctr::ncch::CRYPTO_1B): + ret_string = "[SNAKE] 0x2C/0x1B"; + break; + default: + ret_string = "Unknown"; + } + + return ret_string; +} + +const char* dump_ncch_header_platform_type(uint8_t type) +{ + const char* ret_string = nullptr; + + switch (type) + { + case (nn::ctr::ncch::PLATFORM_CTR): + ret_string = "CTR"; + break; + case (nn::ctr::ncch::PLATFORM_SNAKE): + ret_string = "SNAKE"; + break; + default: + ret_string = "Unknown"; + } + + return ret_string; +} + +const char* dump_ncch_header_form_type(uint8_t type) +{ + const char* ret_string = nullptr; + + switch (type) + { + case (nn::ctr::ncch::SIMPLE_CONTENT): + ret_string = "Simple Content"; + break; + case (nn::ctr::ncch::EXECUTABLE_WITHOUT_ROMFS): + ret_string = "Executable Without RomFS"; + break; + case (nn::ctr::ncch::EXECUTABLE): + ret_string = "Executable"; + break; + default: + ret_string = "Unknown"; + } + + return ret_string; +} + +const char* dump_ncch_header_content_type(uint8_t type) +{ + const char* ret_string = nullptr; + + switch (type) + { + case (nn::ctr::ncch::APPLICATION): + ret_string = "Application"; + break; + case (nn::ctr::ncch::SYSTEM_UPDATE): + ret_string = "System Update"; + break; + case (nn::ctr::ncch::MANUAL): + ret_string = "Manual"; + break; + case (nn::ctr::ncch::CHILD): + ret_string = "Child"; + break; + case (nn::ctr::ncch::TRIAL): + ret_string = "Trial"; + break; + case (nn::ctr::ncch::EXTENDED_SYSTEM_UPDATE): + ret_string = "Extended System Update"; + break; + default: + ret_string = "Unknown"; + } + + return ret_string; +} + +const char* dump_ncch_header_other_flag_bit(uint8_t bit) +{ + const char* ret_string = nullptr; + + switch (_BIT(bit)) + { + case (nn::ctr::ncch::FIXED_AES_KEY): + ret_string = "FIXED_AES_KEY"; + break; + case (nn::ctr::ncch::NO_MOUNT_ROMFS): + ret_string = "NO_MOUNT_ROMFS"; + break; + case (nn::ctr::ncch::NO_AES): + ret_string = "NO_AES"; + break; + case (nn::ctr::ncch::SEED_KEY): + ret_string = "SEED_KEY"; + break; + case (nn::ctr::ncch::MANUAL_DISCLOSURE): + ret_string = "MANUAL_DISCLOSURE"; + break; + default: + ret_string = nullptr; + } + + return ret_string; +} + +void dump_ncch_header(const nn::ctr::NcchHeader& ncchHdr) +{ + printf("NcchSize: 0x%08x\n", ncchHdr.getNcchBinarySize()); + printf("TitleID: %04x-%04x-%06x-%02x\n", ncchHdr.getTitleId().getDeviceGroup(), ncchHdr.getTitleId().getCategory(), ncchHdr.getTitleId().getUniqueId(), ncchHdr.getTitleId().getVariation()); + printf("CompanyCode: %s\n", ncchHdr.getCompanyCode().c_str()); + printf("NcchType: %04x (%s)\n", ncchHdr.getNcchType(), dump_ncch_header_ncch_type(ncchHdr.getNcchType())); + printf("SeedChecksum: %08x\n", ncchHdr.getSeedChecksum()); + printf("ProgramID: %04x-%04x-%06x-%02x\n", ncchHdr.getProgramId().getDeviceGroup(), ncchHdr.getProgramId().getCategory(), ncchHdr.getProgramId().getUniqueId(), ncchHdr.getProgramId().getVariation()); + printf("Product Code: %s\n", ncchHdr.getProductCode().c_str()); + printf("Flags:\n"); + printf(" NewCryptoFlag: %02x (%s)\n", ncchHdr.getNewCryptoFlag(), dump_ncch_header_crypto_flag(ncchHdr.getNewCryptoFlag())); + printf(" Platform: %02x (%s)\n", ncchHdr.getPlatform(), dump_ncch_header_platform_type(ncchHdr.getPlatform())); + printf(" FormType: %02x (%s)\n", ncchHdr.getFormType(), dump_ncch_header_form_type(ncchHdr.getFormType())); + printf(" ContentType: %02x (%s)\n", ncchHdr.getContentType(), dump_ncch_header_content_type(ncchHdr.getContentType())); + printf(" BlockSize: %02x\n", ncchHdr.getBlockSize()); + + if (ncchHdr.getOtherFlagList().size() > 0) + printf(" OtherFlag:\n"); + for (size_t i = 0; i < ncchHdr.getOtherFlagList().size(); i++) + { + nn::ctr::ncch::OtherFlag flag = ncchHdr.getOtherFlagList()[i]; + if (dump_ncch_header_other_flag_bit(flag) != nullptr) + { + printf(" %s\n", dump_ncch_header_other_flag_bit(flag)); + } + else + { + printf(" [bit%d]", flag); + } + } + printf("Exheader:\n"); + printf(" Size: 0x%08x\n", ncchHdr.getExtendedHeaderLayout().getSize()); + printf(" Hash: "); + for (int i = 0; i < 32; i++) + { + printf("%02x", ncchHdr.getExtendedHeaderLayout().getHash().bytes[i]); + } + printf("\n"); + printf("PlainRegion:\n"); + printf(" Offset: 0x%08x\n", ncchHdr.getPlainRegionLayout().getOffset()); + printf(" Size: 0x%08x\n", ncchHdr.getPlainRegionLayout().getSize()); + printf("Logo:\n"); + printf(" Offset: 0x%08x\n", ncchHdr.getLogoLayout().getOffset()); + printf(" Size: 0x%08x\n", ncchHdr.getLogoLayout().getSize()); + printf(" Hash: "); + for (int i = 0; i < 32; i++) + { + printf("%02x", ncchHdr.getLogoLayout().getHash().bytes[i]); + } + printf("\n"); + printf("Exefs:\n"); + printf(" Offset: 0x%08x\n", ncchHdr.getExefsLayout().getOffset()); + printf(" Size: 0x%08x\n", ncchHdr.getExefsLayout().getSize()); + printf(" ProtSize: 0x%08x\n", ncchHdr.getExefsLayout().getHashedSize()); + printf(" Hash: "); + for (int i = 0; i < 32; i++) + { + printf("%02x", ncchHdr.getExefsLayout().getHash().bytes[i]); + } + printf("\n"); + printf("Romfs:\n"); + printf(" Offset: 0x%08x\n", ncchHdr.getRomfsLayout().getOffset()); + printf(" Size: 0x%08x\n", ncchHdr.getRomfsLayout().getSize()); + printf(" ProtSize: 0x%08x\n", ncchHdr.getRomfsLayout().getHashedSize()); + printf(" Hash: "); + for (int i = 0; i < 32; i++) + { + printf("%02x", ncchHdr.getRomfsLayout().getHash().bytes[i]); + } + printf("\n"); +} + +int main(int argc, char **argv) +{ + fnd::SimpleFile fileObj; + fnd::Vec fileData; + + if (argc < 2) + { + printf("usage: %s \n", argv[0]); + return 1; + } + + // read file + try + { + fileObj.open(argv[1], fnd::SimpleFile::Read); + fileData.alloc(_MAX(fileObj.size(), 0x1000)); + fileObj.read(fileData.data(), fileData.size()); + } + catch (const fnd::Exception &e) + { + printf("[ERROR] %s\n", e.error()); + return 2; + } + + // attempt to read file + try + { + nn::ctr::CodeBinaryHeader hdr, test; + hdr.fromBytes(fileData.data(), fileData.size()); + printf("[NOTICE] .code file detected\n"); + dump_code_header(hdr); + + hdr.toBytes(); + test.fromBytes(hdr.getBytes().data(), hdr.getBytes().size()); + printf("Testing re-serialisation\n"); + dump_code_header(test); + } + catch (const fnd::Exception &e) + { + printf("[NOTICE] not a .code file (%s)\n", e.what()); + } + + try + { + nn::ctr::NcchHeader hdr, test; + hdr.fromBytes(fileData.data() + 0x100, fileData.size() >= 0x200 ? 0x100 : 0); + printf("[NOTICE] NCCH file detected\n"); + dump_ncch_header(hdr); + + hdr.toBytes(); + test.fromBytes(hdr.getBytes().data(), hdr.getBytes().size()); + printf("Testing re-serialisation\n"); + dump_ncch_header(test); + } + catch (const fnd::Exception &e) + { + printf("[NOTICE] not a NCCH file (%s)\n", e.what()); + } + + return 0; +} \ No newline at end of file