mirror of
https://github.com/jakcron/nstool
synced 2024-11-23 14:09:29 +00:00
307 lines
No EOL
8.5 KiB
C++
307 lines
No EOL
8.5 KiB
C++
/*
|
|
(c) 2018 Jakcron
|
|
*/
|
|
#include <cstdio>
|
|
|
|
#include <fnd/types.h>
|
|
#include <fnd/Vec.h>
|
|
#include <fnd/SimpleFile.h>
|
|
#include <nn/ctr/CodeBinaryHeader.h>
|
|
#include <nn/ctr/NcchHeader.h>
|
|
|
|
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<byte_t> fileData;
|
|
|
|
if (argc < 2)
|
|
{
|
|
printf("usage: %s <file>\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;
|
|
} |