mirror of
https://github.com/jakcron/nstool.git
synced 2024-12-22 18:55:29 +00:00
[nx-hb|nstool]Added better HB Asset support.
This commit is contained in:
parent
99e4eee3ea
commit
8924222c55
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
|
@ -28,5 +28,6 @@
|
|||
"tuple": "cpp",
|
||||
"__locale": "cpp",
|
||||
"cinttypes": "cpp"
|
||||
"__bit_reference": "cpp",
|
||||
}
|
||||
}
|
|
@ -1,12 +1,12 @@
|
|||
#pragma once
|
||||
#include <nx/nro-hb.h>
|
||||
#include <nx/aset.h>
|
||||
#include <fnd/MemoryBlob.h>
|
||||
#include <fnd/List.h>
|
||||
#include <fnd/ISerialiseableBinary.h>
|
||||
|
||||
namespace nx
|
||||
{
|
||||
class NroAssetHeader :
|
||||
class AssetHeader :
|
||||
public fnd::ISerialiseableBinary
|
||||
{
|
||||
public:
|
||||
|
@ -33,13 +33,13 @@ namespace nx
|
|||
}
|
||||
};
|
||||
|
||||
NroAssetHeader();
|
||||
NroAssetHeader(const NroAssetHeader& other);
|
||||
NroAssetHeader(const byte_t* bytes, size_t len);
|
||||
AssetHeader();
|
||||
AssetHeader(const AssetHeader& other);
|
||||
AssetHeader(const byte_t* bytes, size_t len);
|
||||
|
||||
bool operator==(const NroAssetHeader& other) const;
|
||||
bool operator!=(const NroAssetHeader& other) const;
|
||||
void operator=(const NroAssetHeader& other);
|
||||
bool operator==(const AssetHeader& other) const;
|
||||
bool operator!=(const AssetHeader& other) const;
|
||||
void operator=(const AssetHeader& other);
|
||||
|
||||
// to be used after export
|
||||
const byte_t* getBytes() const;
|
||||
|
@ -72,8 +72,8 @@ namespace nx
|
|||
sSection mRomfsInfo;
|
||||
|
||||
// helpers
|
||||
bool isEqual(const NroAssetHeader& other) const;
|
||||
void copyFrom(const NroAssetHeader& other);
|
||||
bool isEqual(const AssetHeader& other) const;
|
||||
void copyFrom(const AssetHeader& other);
|
||||
};
|
||||
|
||||
}
|
30
lib/libnx-hb/include/nx/aset.h
Normal file
30
lib/libnx-hb/include/nx/aset.h
Normal file
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#include <fnd/types.h>
|
||||
#include <nx/macro.h>
|
||||
namespace nx
|
||||
{
|
||||
namespace aset
|
||||
{
|
||||
static const uint32_t kAssetSig = _MAKE_STRUCT_SIGNATURE("ASET");
|
||||
|
||||
static const uint32_t kDefaultAssetFormatVersion = 0;
|
||||
}
|
||||
|
||||
#pragma pack(push,1)
|
||||
struct sAssetSection
|
||||
{
|
||||
le_uint64_t offset;
|
||||
le_uint64_t size;
|
||||
};
|
||||
|
||||
struct sAssetHeader
|
||||
{
|
||||
le_uint32_t signature;
|
||||
le_uint32_t format_version;
|
||||
sAssetSection icon;
|
||||
sAssetSection nacp;
|
||||
sAssetSection romfs;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
}
|
|
@ -7,25 +7,5 @@ namespace nx
|
|||
namespace nro
|
||||
{
|
||||
static const uint64_t kNroHomebrewSig = _MAKE_STRUCT_SIGNATURE_U64("HOMEBREW");
|
||||
static const uint32_t kAssetSig = _MAKE_STRUCT_SIGNATURE("ASET");
|
||||
|
||||
static const uint32_t kDefaultAssetFormatVersion = 0;
|
||||
}
|
||||
|
||||
#pragma pack(push,1)
|
||||
struct sNroAssetSection
|
||||
{
|
||||
le_uint64_t offset;
|
||||
le_uint64_t size;
|
||||
};
|
||||
|
||||
struct sNroAssetHeader
|
||||
{
|
||||
le_uint32_t signature;
|
||||
le_uint32_t format_version;
|
||||
sNroAssetSection icon;
|
||||
sNroAssetSection nacp;
|
||||
sNroAssetSection romfs;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
}
|
153
lib/libnx-hb/source/AssetHeader.cpp
Normal file
153
lib/libnx-hb/source/AssetHeader.cpp
Normal file
|
@ -0,0 +1,153 @@
|
|||
#include <nx/AssetHeader.h>
|
||||
|
||||
nx::AssetHeader::AssetHeader()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
nx::AssetHeader::AssetHeader(const AssetHeader& other)
|
||||
{
|
||||
copyFrom(other);
|
||||
}
|
||||
|
||||
nx::AssetHeader::AssetHeader(const byte_t* bytes, size_t len)
|
||||
{
|
||||
importBinary(bytes, len);
|
||||
}
|
||||
|
||||
bool nx::AssetHeader::operator==(const AssetHeader& other) const
|
||||
{
|
||||
return isEqual(other);
|
||||
}
|
||||
|
||||
bool nx::AssetHeader::operator!=(const AssetHeader& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
void nx::AssetHeader::operator=(const AssetHeader& other)
|
||||
{
|
||||
copyFrom(other);
|
||||
}
|
||||
|
||||
const byte_t* nx::AssetHeader::getBytes() const
|
||||
{
|
||||
return mBinaryBlob.getBytes();
|
||||
}
|
||||
|
||||
size_t nx::AssetHeader::getSize() const
|
||||
{
|
||||
return mBinaryBlob.getSize();
|
||||
}
|
||||
|
||||
void nx::AssetHeader::exportBinary()
|
||||
{
|
||||
mBinaryBlob.alloc(sizeof(sAssetHeader));
|
||||
nx::sAssetHeader* hdr = (nx::sAssetHeader*)mBinaryBlob.getBytes();
|
||||
|
||||
// set header identifers
|
||||
hdr->signature = aset::kAssetSig;
|
||||
hdr->format_version = aset::kDefaultAssetFormatVersion;
|
||||
|
||||
// set icon section
|
||||
hdr->icon.offset = mIconInfo.offset;
|
||||
hdr->icon.size = mIconInfo.size;
|
||||
|
||||
// set nacp section
|
||||
hdr->nacp.offset = mNacpInfo.offset;
|
||||
hdr->nacp.size = mNacpInfo.size;
|
||||
|
||||
// set romfs section
|
||||
hdr->romfs.offset = mRomfsInfo.offset;
|
||||
hdr->romfs.size = mRomfsInfo.size;
|
||||
}
|
||||
|
||||
void nx::AssetHeader::importBinary(const byte_t* bytes, size_t len)
|
||||
{
|
||||
// check input data size
|
||||
if (len < sizeof(sAssetHeader))
|
||||
{
|
||||
throw fnd::Exception(kModuleName, "ASET header size is too small");
|
||||
}
|
||||
|
||||
// clear internal members
|
||||
clear();
|
||||
|
||||
// allocate internal local binary copy
|
||||
mBinaryBlob.alloc(sizeof(sAssetHeader));
|
||||
memcpy(mBinaryBlob.getBytes(), bytes, mBinaryBlob.getSize());
|
||||
|
||||
// get sAssetHeader ptr
|
||||
const nx::sAssetHeader* hdr = (const nx::sAssetHeader*)mBinaryBlob.getBytes();
|
||||
|
||||
// check NRO signature
|
||||
if (hdr->signature.get() != aset::kAssetSig)
|
||||
{
|
||||
throw fnd::Exception(kModuleName, "ASET header corrupt (unrecognised header signature)");
|
||||
}
|
||||
|
||||
// check NRO format version
|
||||
if (hdr->format_version.get() != aset::kDefaultAssetFormatVersion)
|
||||
{
|
||||
throw fnd::Exception(kModuleName, "ASET header corrupt (unsupported format version)");
|
||||
}
|
||||
|
||||
mIconInfo.offset = hdr->icon.offset.get();
|
||||
mIconInfo.size = hdr->icon.size.get();
|
||||
mNacpInfo.offset = hdr->nacp.offset.get();
|
||||
mNacpInfo.size = hdr->nacp.size.get();
|
||||
mRomfsInfo.offset = hdr->romfs.offset.get();
|
||||
mRomfsInfo.size = hdr->romfs.size.get();
|
||||
}
|
||||
|
||||
void nx::AssetHeader::clear()
|
||||
{
|
||||
mBinaryBlob.clear();
|
||||
memset(&mIconInfo, 0, sizeof(mIconInfo));
|
||||
memset(&mNacpInfo, 0, sizeof(mNacpInfo));
|
||||
memset(&mRomfsInfo, 0, sizeof(mRomfsInfo));
|
||||
}
|
||||
|
||||
const nx::AssetHeader::sSection& nx::AssetHeader::getIconInfo() const
|
||||
{
|
||||
return mIconInfo;
|
||||
}
|
||||
|
||||
void nx::AssetHeader::setIconInfo(const nx::AssetHeader::sSection& info)
|
||||
{
|
||||
mIconInfo = info;
|
||||
}
|
||||
|
||||
const nx::AssetHeader::sSection& nx::AssetHeader::getNacpInfo() const
|
||||
{
|
||||
return mNacpInfo;
|
||||
}
|
||||
|
||||
void nx::AssetHeader::setNacpInfo(const sSection& info)
|
||||
{
|
||||
mNacpInfo = info;
|
||||
}
|
||||
|
||||
const nx::AssetHeader::sSection& nx::AssetHeader::getRomfsInfo() const
|
||||
{
|
||||
return mRomfsInfo;
|
||||
}
|
||||
|
||||
void nx::AssetHeader::setRomfsInfo(const sSection& info)
|
||||
{
|
||||
mRomfsInfo = info;
|
||||
}
|
||||
|
||||
bool nx::AssetHeader::isEqual(const AssetHeader& other) const
|
||||
{
|
||||
return (mIconInfo == other.mIconInfo) \
|
||||
&& (mNacpInfo == other.mNacpInfo) \
|
||||
&& (mRomfsInfo == other.mRomfsInfo);
|
||||
}
|
||||
|
||||
void nx::AssetHeader::copyFrom(const AssetHeader& other)
|
||||
{
|
||||
mIconInfo = other.mIconInfo;
|
||||
mNacpInfo = other.mNacpInfo;
|
||||
mRomfsInfo = other.mRomfsInfo;
|
||||
}
|
|
@ -1,153 +0,0 @@
|
|||
#include <nx/NroAssetHeader.h>
|
||||
|
||||
nx::NroAssetHeader::NroAssetHeader()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
nx::NroAssetHeader::NroAssetHeader(const NroAssetHeader& other)
|
||||
{
|
||||
copyFrom(other);
|
||||
}
|
||||
|
||||
nx::NroAssetHeader::NroAssetHeader(const byte_t* bytes, size_t len)
|
||||
{
|
||||
importBinary(bytes, len);
|
||||
}
|
||||
|
||||
bool nx::NroAssetHeader::operator==(const NroAssetHeader& other) const
|
||||
{
|
||||
return isEqual(other);
|
||||
}
|
||||
|
||||
bool nx::NroAssetHeader::operator!=(const NroAssetHeader& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
void nx::NroAssetHeader::operator=(const NroAssetHeader& other)
|
||||
{
|
||||
copyFrom(other);
|
||||
}
|
||||
|
||||
const byte_t* nx::NroAssetHeader::getBytes() const
|
||||
{
|
||||
return mBinaryBlob.getBytes();
|
||||
}
|
||||
|
||||
size_t nx::NroAssetHeader::getSize() const
|
||||
{
|
||||
return mBinaryBlob.getSize();
|
||||
}
|
||||
|
||||
void nx::NroAssetHeader::exportBinary()
|
||||
{
|
||||
mBinaryBlob.alloc(sizeof(sNroAssetHeader));
|
||||
nx::sNroAssetHeader* hdr = (nx::sNroAssetHeader*)mBinaryBlob.getBytes();
|
||||
|
||||
// set header identifers
|
||||
hdr->signature = nro::kAssetSig;
|
||||
hdr->format_version = nro::kDefaultAssetFormatVersion;
|
||||
|
||||
// set icon section
|
||||
hdr->icon.offset = mIconInfo.offset;
|
||||
hdr->icon.size = mIconInfo.size;
|
||||
|
||||
// set nacp section
|
||||
hdr->nacp.offset = mNacpInfo.offset;
|
||||
hdr->nacp.size = mNacpInfo.size;
|
||||
|
||||
// set romfs section
|
||||
hdr->romfs.offset = mRomfsInfo.offset;
|
||||
hdr->romfs.size = mRomfsInfo.size;
|
||||
}
|
||||
|
||||
void nx::NroAssetHeader::importBinary(const byte_t* bytes, size_t len)
|
||||
{
|
||||
// check input data size
|
||||
if (len < sizeof(sNroAssetHeader))
|
||||
{
|
||||
throw fnd::Exception(kModuleName, "NRO Asset header size is too small");
|
||||
}
|
||||
|
||||
// clear internal members
|
||||
clear();
|
||||
|
||||
// allocate internal local binary copy
|
||||
mBinaryBlob.alloc(sizeof(sNroAssetHeader));
|
||||
memcpy(mBinaryBlob.getBytes(), bytes, mBinaryBlob.getSize());
|
||||
|
||||
// get sNroAssetHeader ptr
|
||||
const nx::sNroAssetHeader* hdr = (const nx::sNroAssetHeader*)mBinaryBlob.getBytes();
|
||||
|
||||
// check NRO signature
|
||||
if (hdr->signature.get() != nro::kAssetSig)
|
||||
{
|
||||
throw fnd::Exception(kModuleName, "NRO Asset header corrupt (unrecognised header signature)");
|
||||
}
|
||||
|
||||
// check NRO format version
|
||||
if (hdr->format_version.get() != nro::kDefaultAssetFormatVersion)
|
||||
{
|
||||
throw fnd::Exception(kModuleName, "NRO Asset header corrupt (unsupported format version)");
|
||||
}
|
||||
|
||||
mIconInfo.offset = hdr->icon.offset.get();
|
||||
mIconInfo.size = hdr->icon.size.get();
|
||||
mNacpInfo.offset = hdr->nacp.offset.get();
|
||||
mNacpInfo.size = hdr->nacp.size.get();
|
||||
mRomfsInfo.offset = hdr->romfs.offset.get();
|
||||
mRomfsInfo.size = hdr->romfs.size.get();
|
||||
}
|
||||
|
||||
void nx::NroAssetHeader::clear()
|
||||
{
|
||||
mBinaryBlob.clear();
|
||||
memset(&mIconInfo, 0, sizeof(mIconInfo));
|
||||
memset(&mNacpInfo, 0, sizeof(mNacpInfo));
|
||||
memset(&mRomfsInfo, 0, sizeof(mRomfsInfo));
|
||||
}
|
||||
|
||||
const nx::NroAssetHeader::sSection& nx::NroAssetHeader::getIconInfo() const
|
||||
{
|
||||
return mIconInfo;
|
||||
}
|
||||
|
||||
void nx::NroAssetHeader::setIconInfo(const nx::NroAssetHeader::sSection& info)
|
||||
{
|
||||
mIconInfo = info;
|
||||
}
|
||||
|
||||
const nx::NroAssetHeader::sSection& nx::NroAssetHeader::getNacpInfo() const
|
||||
{
|
||||
return mNacpInfo;
|
||||
}
|
||||
|
||||
void nx::NroAssetHeader::setNacpInfo(const sSection& info)
|
||||
{
|
||||
mNacpInfo = info;
|
||||
}
|
||||
|
||||
const nx::NroAssetHeader::sSection& nx::NroAssetHeader::getRomfsInfo() const
|
||||
{
|
||||
return mRomfsInfo;
|
||||
}
|
||||
|
||||
void nx::NroAssetHeader::setRomfsInfo(const sSection& info)
|
||||
{
|
||||
mRomfsInfo = info;
|
||||
}
|
||||
|
||||
bool nx::NroAssetHeader::isEqual(const NroAssetHeader& other) const
|
||||
{
|
||||
return (mIconInfo == other.mIconInfo) \
|
||||
&& (mNacpInfo == other.mNacpInfo) \
|
||||
&& (mRomfsInfo == other.mRomfsInfo);
|
||||
}
|
||||
|
||||
void nx::NroAssetHeader::copyFrom(const NroAssetHeader& other)
|
||||
{
|
||||
mIconInfo = other.mIconInfo;
|
||||
mNacpInfo = other.mNacpInfo;
|
||||
mRomfsInfo = other.mRomfsInfo;
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
#define _MAKE_STRUCT_SIGNATURE(x) ((uint32_t)(x[3]) << 24 | (uint32_t)(x[2]) << 16 | (uint32_t)(x[1]) << 8 | (uint32_t)(x[0]))
|
||||
#define _MAKE_STRUCT_SIGNATURE_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]))
|
||||
|
|
146
programs/nstool/source/AssetProcess.cpp
Normal file
146
programs/nstool/source/AssetProcess.cpp
Normal file
|
@ -0,0 +1,146 @@
|
|||
#include <fnd/SimpleFile.h>
|
||||
#include <fnd/MemoryBlob.h>
|
||||
#include "AssetProcess.h"
|
||||
#include "OffsetAdjustedIFile.h"
|
||||
|
||||
|
||||
AssetProcess::AssetProcess() :
|
||||
mFile(nullptr),
|
||||
mOwnIFile(false),
|
||||
mCliOutputType(OUTPUT_NORMAL),
|
||||
mVerify(false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
AssetProcess::~AssetProcess()
|
||||
{
|
||||
if (mOwnIFile)
|
||||
{
|
||||
delete mFile;
|
||||
}
|
||||
}
|
||||
|
||||
void AssetProcess::process()
|
||||
{
|
||||
if (mFile == nullptr)
|
||||
{
|
||||
throw fnd::Exception(kModuleName, "No file reader set.");
|
||||
}
|
||||
|
||||
importHeader();
|
||||
displayHeader();
|
||||
processSections();
|
||||
}
|
||||
|
||||
void AssetProcess::setInputFile(fnd::IFile* file, bool ownIFile)
|
||||
{
|
||||
mFile = file;
|
||||
mOwnIFile = ownIFile;
|
||||
}
|
||||
|
||||
void AssetProcess::setCliOutputMode(CliOutputType type)
|
||||
{
|
||||
mCliOutputType = type;
|
||||
}
|
||||
|
||||
void AssetProcess::setVerifyMode(bool verify)
|
||||
{
|
||||
mVerify = verify;
|
||||
}
|
||||
|
||||
void AssetProcess::setListFs(bool list)
|
||||
{
|
||||
mRomfs.setListFs(list);
|
||||
}
|
||||
|
||||
void AssetProcess::setIconExtractPath(const std::string& path)
|
||||
{
|
||||
mIconExtractPath = path;
|
||||
}
|
||||
|
||||
void AssetProcess::setNacpExtractPath(const std::string& path)
|
||||
{
|
||||
mNacpExtractPath = path;
|
||||
}
|
||||
|
||||
void AssetProcess::setRomfsExtractPath(const std::string& path)
|
||||
{
|
||||
mRomfs.setExtractPath(path);
|
||||
}
|
||||
|
||||
|
||||
void AssetProcess::importHeader()
|
||||
{
|
||||
fnd::MemoryBlob scratch;
|
||||
if (mFile->size() < sizeof(nx::sAssetHeader))
|
||||
{
|
||||
throw fnd::Exception(kModuleName, "Corrupt ASET: file too small");
|
||||
}
|
||||
|
||||
scratch.alloc(sizeof(nx::sAssetHeader));
|
||||
mFile->read(scratch.getBytes(), 0, scratch.getSize());
|
||||
|
||||
mHdr.importBinary(scratch.getBytes(), scratch.getSize());
|
||||
}
|
||||
|
||||
void AssetProcess::processSections()
|
||||
{
|
||||
if (mHdr.getIconInfo().size > 0 && mIconExtractPath.isSet)
|
||||
{
|
||||
if ((mHdr.getIconInfo().size + mHdr.getIconInfo().offset) > mFile->size())
|
||||
throw fnd::Exception(kModuleName, "ASET geometry for icon beyond file size");
|
||||
|
||||
fnd::SimpleFile outfile(mIconExtractPath.var, fnd::SimpleFile::Create);
|
||||
fnd::MemoryBlob cache;
|
||||
|
||||
cache.alloc(mHdr.getIconInfo().size);
|
||||
mFile->read(cache.getBytes(), mHdr.getIconInfo().offset, cache.getSize());
|
||||
outfile.write(cache.getBytes(), cache.getSize());
|
||||
outfile.close();
|
||||
}
|
||||
|
||||
if (mHdr.getNacpInfo().size > 0 && mNacpExtractPath.isSet)
|
||||
{
|
||||
if ((mHdr.getNacpInfo().size + mHdr.getNacpInfo().offset) > mFile->size())
|
||||
throw fnd::Exception(kModuleName, "ASET geometry for nacp beyond file size");
|
||||
|
||||
fnd::SimpleFile outfile(mNacpExtractPath.var, fnd::SimpleFile::Create);
|
||||
fnd::MemoryBlob cache;
|
||||
|
||||
cache.alloc(mHdr.getNacpInfo().size);
|
||||
mFile->read(cache.getBytes(), mHdr.getNacpInfo().offset, cache.getSize());
|
||||
outfile.write(cache.getBytes(), cache.getSize());
|
||||
outfile.close();
|
||||
}
|
||||
|
||||
if (mHdr.getRomfsInfo().size > 0)
|
||||
{
|
||||
if ((mHdr.getRomfsInfo().size + mHdr.getRomfsInfo().offset) > mFile->size())
|
||||
throw fnd::Exception(kModuleName, "ASET geometry for romfs beyond file size");
|
||||
|
||||
mRomfs.setInputFile(new OffsetAdjustedIFile(mFile, false, mHdr.getRomfsInfo().offset, mHdr.getRomfsInfo().size), true);
|
||||
mRomfs.setCliOutputMode(mCliOutputType);
|
||||
mRomfs.setVerifyMode(mVerify);
|
||||
|
||||
mRomfs.process();
|
||||
}
|
||||
}
|
||||
|
||||
void AssetProcess::displayHeader()
|
||||
{
|
||||
if (mCliOutputType >= OUTPUT_NORMAL)
|
||||
{
|
||||
printf("[ASET Header]\n");
|
||||
printf(" Icon:\n");
|
||||
printf(" Offset: 0x%" PRIx64 "\n", mHdr.getIconInfo().offset);
|
||||
printf(" Size: 0x%" PRIx64 "\n", mHdr.getIconInfo().size);
|
||||
printf(" NACP:\n");
|
||||
printf(" Offset: 0x%" PRIx64 "\n", mHdr.getNacpInfo().offset);
|
||||
printf(" Size: 0x%" PRIx64 "\n", mHdr.getNacpInfo().size);
|
||||
printf(" RomFS:\n");
|
||||
printf(" Offset: 0x%" PRIx64 "\n", mHdr.getRomfsInfo().offset);
|
||||
printf(" Size: 0x%" PRIx64 "\n", mHdr.getRomfsInfo().size);
|
||||
}
|
||||
}
|
||||
|
46
programs/nstool/source/AssetProcess.h
Normal file
46
programs/nstool/source/AssetProcess.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
#include <fnd/types.h>
|
||||
#include <fnd/IFile.h>
|
||||
#include <nx/AssetHeader.h>
|
||||
#include "RomfsProcess.h"
|
||||
|
||||
#include "nstool.h"
|
||||
|
||||
class AssetProcess
|
||||
{
|
||||
public:
|
||||
AssetProcess();
|
||||
~AssetProcess();
|
||||
|
||||
void process();
|
||||
|
||||
void setInputFile(fnd::IFile* file, bool ownIFile);
|
||||
void setCliOutputMode(CliOutputType type);
|
||||
void setVerifyMode(bool verify);
|
||||
|
||||
void setListFs(bool list);
|
||||
|
||||
void setIconExtractPath(const std::string& path);
|
||||
void setNacpExtractPath(const std::string& path);
|
||||
void setRomfsExtractPath(const std::string& path);
|
||||
|
||||
|
||||
private:
|
||||
const std::string kModuleName = "AssetProcess";
|
||||
|
||||
fnd::IFile* mFile;
|
||||
bool mOwnIFile;
|
||||
CliOutputType mCliOutputType;
|
||||
bool mVerify;
|
||||
|
||||
sOptional<std::string> mIconExtractPath;
|
||||
sOptional<std::string> mNacpExtractPath;
|
||||
|
||||
nx::AssetHeader mHdr;
|
||||
RomfsProcess mRomfs;
|
||||
|
||||
void importHeader();
|
||||
void processSections();
|
||||
void displayHeader();
|
||||
};
|
|
@ -1,549 +0,0 @@
|
|||
#include <sstream>
|
||||
#include <fnd/SimpleTextOutput.h>
|
||||
#include <fnd/MemoryBlob.h>
|
||||
#include <compress/lz4.h>
|
||||
#include "OffsetAdjustedIFile.h"
|
||||
#include "CodeObjectProcess.h"
|
||||
|
||||
CodeObjectProcess::CodeObjectProcess():
|
||||
mFile(nullptr),
|
||||
mOwnIFile(false),
|
||||
mCliOutputType(OUTPUT_NORMAL),
|
||||
mVerify(false),
|
||||
mObjType(OBJ_INVALID),
|
||||
mInstructionType(nx::npdm::INSTR_64BIT),
|
||||
mListApi(false),
|
||||
mListSymbols(false)
|
||||
{
|
||||
}
|
||||
|
||||
CodeObjectProcess::~CodeObjectProcess()
|
||||
{
|
||||
if (mOwnIFile)
|
||||
{
|
||||
delete mFile;
|
||||
}
|
||||
}
|
||||
|
||||
void CodeObjectProcess::process()
|
||||
{
|
||||
if (mFile == nullptr)
|
||||
{
|
||||
throw fnd::Exception(kModuleName, "No file reader set.");
|
||||
}
|
||||
|
||||
if (mObjType == OBJ_INVALID)
|
||||
{
|
||||
throw fnd::Exception(kModuleName, "Object type undefined.");
|
||||
}
|
||||
|
||||
importHeader();
|
||||
importCodeSegments();
|
||||
importApiList();
|
||||
|
||||
if (mCliOutputType >= OUTPUT_NORMAL)
|
||||
{
|
||||
if (mObjType == OBJ_NSO)
|
||||
displayNsoHeader();
|
||||
else if (mObjType == OBJ_NRO)
|
||||
{
|
||||
displayNroHeader();
|
||||
if (mIsHomebrewNro)
|
||||
displayNroAssetHeader();
|
||||
}
|
||||
|
||||
}
|
||||
displayRoMetaData();
|
||||
}
|
||||
|
||||
void CodeObjectProcess::setInputFile(fnd::IFile* file, bool ownIFile)
|
||||
{
|
||||
mFile = file;
|
||||
mOwnIFile = ownIFile;
|
||||
}
|
||||
|
||||
void CodeObjectProcess::setCliOutputMode(CliOutputType type)
|
||||
{
|
||||
mCliOutputType = type;
|
||||
}
|
||||
|
||||
void CodeObjectProcess::setVerifyMode(bool verify)
|
||||
{
|
||||
mVerify = verify;
|
||||
}
|
||||
|
||||
void CodeObjectProcess::setCodeObjectType(CodeObjectType type)
|
||||
{
|
||||
mObjType = type;
|
||||
}
|
||||
|
||||
void CodeObjectProcess::setInstructionType(nx::npdm::InstructionType type)
|
||||
{
|
||||
mInstructionType = type;
|
||||
}
|
||||
|
||||
void CodeObjectProcess::setListApi(bool listApi)
|
||||
{
|
||||
mListApi = listApi;
|
||||
}
|
||||
|
||||
void CodeObjectProcess::setListSymbols(bool listSymbols)
|
||||
{
|
||||
mListSymbols = listSymbols;
|
||||
}
|
||||
|
||||
const nx::NsoHeader& CodeObjectProcess::getNsoHeader() const
|
||||
{
|
||||
return mNsoHdr;
|
||||
}
|
||||
|
||||
const fnd::MemoryBlob& CodeObjectProcess::getTextBlob() const
|
||||
{
|
||||
return mTextBlob;
|
||||
}
|
||||
|
||||
const fnd::MemoryBlob& CodeObjectProcess::getRoBlob() const
|
||||
{
|
||||
return mRoBlob;
|
||||
}
|
||||
|
||||
const fnd::MemoryBlob& CodeObjectProcess::getDataBlob() const
|
||||
{
|
||||
return mDataBlob;
|
||||
}
|
||||
|
||||
const std::vector<SdkApiString>& CodeObjectProcess::getApiList() const
|
||||
{
|
||||
return mApiList;
|
||||
}
|
||||
|
||||
void CodeObjectProcess::importHeader()
|
||||
{
|
||||
if (mObjType == OBJ_NSO)
|
||||
{
|
||||
fnd::MemoryBlob scratch;
|
||||
if (mFile->size() < sizeof(nx::sNsoHeader))
|
||||
{
|
||||
throw fnd::Exception(kModuleName, "Corrupt NSO file too small");
|
||||
}
|
||||
|
||||
scratch.alloc(sizeof(nx::sNsoHeader));
|
||||
mFile->read(scratch.getBytes(), 0, scratch.getSize());
|
||||
|
||||
mNsoHdr.importBinary(scratch.getBytes(), scratch.getSize());
|
||||
}
|
||||
else
|
||||
{
|
||||
fnd::MemoryBlob scratch;
|
||||
if (mFile->size() < sizeof(nx::sNroHeader))
|
||||
{
|
||||
throw fnd::Exception(kModuleName, "Corrupt NSO file too small");
|
||||
}
|
||||
|
||||
scratch.alloc(sizeof(nx::sNroHeader));
|
||||
mFile->read(scratch.getBytes(), 0, scratch.getSize());
|
||||
|
||||
mNroHdr.importBinary(scratch.getBytes(), scratch.getSize());
|
||||
|
||||
nx::sNroHeader* raw_hdr = (nx::sNroHeader*)scratch.getBytes();
|
||||
|
||||
if (((le_uint64_t*)raw_hdr->reserved_0)->get() == nx::nro::kNroHomebrewSig)
|
||||
{
|
||||
mIsHomebrewNro = true;
|
||||
scratch.alloc(sizeof(nx::sNroAssetHeader));
|
||||
mFile->read(scratch.getBytes(), mNroHdr.getNroSize(), scratch.getSize());
|
||||
mNroAssetHdr.importBinary(scratch.getBytes(), scratch.getSize());
|
||||
}
|
||||
else
|
||||
mIsHomebrewNro = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void CodeObjectProcess::importCodeSegments()
|
||||
{
|
||||
if (mObjType == OBJ_NSO)
|
||||
{
|
||||
fnd::MemoryBlob scratch;
|
||||
uint32_t decompressed_len;
|
||||
crypto::sha::sSha256Hash calc_hash;
|
||||
|
||||
// process text segment
|
||||
if (mNsoHdr.getTextSegmentInfo().is_compressed)
|
||||
{
|
||||
scratch.alloc(mNsoHdr.getTextSegmentInfo().file_layout.size);
|
||||
mFile->read(scratch.getBytes(), mNsoHdr.getTextSegmentInfo().file_layout.offset, scratch.getSize());
|
||||
mTextBlob.alloc(mNsoHdr.getTextSegmentInfo().memory_layout.size);
|
||||
compress::lz4::decompressData(scratch.getBytes(), scratch.getSize(), mTextBlob.getBytes(), mTextBlob.getSize(), decompressed_len);
|
||||
if (decompressed_len != mTextBlob.getSize())
|
||||
{
|
||||
throw fnd::Exception(kModuleName, "NSO text segment failed to decompress");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mTextBlob.alloc(mNsoHdr.getTextSegmentInfo().file_layout.size);
|
||||
mFile->read(mTextBlob.getBytes(), mNsoHdr.getTextSegmentInfo().file_layout.offset, mTextBlob.getSize());
|
||||
}
|
||||
if (mNsoHdr.getTextSegmentInfo().is_hashed)
|
||||
{
|
||||
crypto::sha::Sha256(mTextBlob.getBytes(), mTextBlob.getSize(), calc_hash.bytes);
|
||||
if (calc_hash != mNsoHdr.getTextSegmentInfo().hash)
|
||||
{
|
||||
throw fnd::Exception(kModuleName, "NSO text segment failed SHA256 verification");
|
||||
}
|
||||
}
|
||||
|
||||
// process ro segment
|
||||
if (mNsoHdr.getRoSegmentInfo().is_compressed)
|
||||
{
|
||||
scratch.alloc(mNsoHdr.getRoSegmentInfo().file_layout.size);
|
||||
mFile->read(scratch.getBytes(), mNsoHdr.getRoSegmentInfo().file_layout.offset, scratch.getSize());
|
||||
mRoBlob.alloc(mNsoHdr.getRoSegmentInfo().memory_layout.size);
|
||||
compress::lz4::decompressData(scratch.getBytes(), scratch.getSize(), mRoBlob.getBytes(), mRoBlob.getSize(), decompressed_len);
|
||||
if (decompressed_len != mRoBlob.getSize())
|
||||
{
|
||||
throw fnd::Exception(kModuleName, "NSO ro segment failed to decompress");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mRoBlob.alloc(mNsoHdr.getRoSegmentInfo().file_layout.size);
|
||||
mFile->read(mRoBlob.getBytes(), mNsoHdr.getRoSegmentInfo().file_layout.offset, mRoBlob.getSize());
|
||||
}
|
||||
if (mNsoHdr.getRoSegmentInfo().is_hashed)
|
||||
{
|
||||
crypto::sha::Sha256(mRoBlob.getBytes(), mRoBlob.getSize(), calc_hash.bytes);
|
||||
if (calc_hash != mNsoHdr.getRoSegmentInfo().hash)
|
||||
{
|
||||
throw fnd::Exception(kModuleName, "NSO ro segment failed SHA256 verification");
|
||||
}
|
||||
}
|
||||
|
||||
// process data segment
|
||||
if (mNsoHdr.getDataSegmentInfo().is_compressed)
|
||||
{
|
||||
scratch.alloc(mNsoHdr.getDataSegmentInfo().file_layout.size);
|
||||
mFile->read(scratch.getBytes(), mNsoHdr.getDataSegmentInfo().file_layout.offset, scratch.getSize());
|
||||
mDataBlob.alloc(mNsoHdr.getDataSegmentInfo().memory_layout.size);
|
||||
compress::lz4::decompressData(scratch.getBytes(), scratch.getSize(), mDataBlob.getBytes(), mDataBlob.getSize(), decompressed_len);
|
||||
if (decompressed_len != mDataBlob.getSize())
|
||||
{
|
||||
throw fnd::Exception(kModuleName, "NSO data segment failed to decompress");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mDataBlob.alloc(mNsoHdr.getDataSegmentInfo().file_layout.size);
|
||||
mFile->read(mDataBlob.getBytes(), mNsoHdr.getDataSegmentInfo().file_layout.offset, mDataBlob.getSize());
|
||||
}
|
||||
if (mNsoHdr.getDataSegmentInfo().is_hashed)
|
||||
{
|
||||
crypto::sha::Sha256(mDataBlob.getBytes(), mDataBlob.getSize(), calc_hash.bytes);
|
||||
if (calc_hash != mNsoHdr.getDataSegmentInfo().hash)
|
||||
{
|
||||
throw fnd::Exception(kModuleName, "NSO data segment failed SHA256 verification");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (mObjType == OBJ_NRO)
|
||||
{
|
||||
mTextBlob.alloc(mNroHdr.getTextInfo().size);
|
||||
mFile->read(mTextBlob.getBytes(), mNroHdr.getTextInfo().memory_offset, mTextBlob.getSize());
|
||||
mRoBlob.alloc(mNroHdr.getRoInfo().size);
|
||||
mFile->read(mRoBlob.getBytes(), mNroHdr.getRoInfo().memory_offset, mRoBlob.getSize());
|
||||
mDataBlob.alloc(mNroHdr.getDataInfo().size);
|
||||
mFile->read(mDataBlob.getBytes(), mNroHdr.getDataInfo().memory_offset, mDataBlob.getSize());
|
||||
}
|
||||
}
|
||||
|
||||
void CodeObjectProcess::importApiList()
|
||||
{
|
||||
struct sLayout { size_t offset; size_t size; } api_info, dyn_str, dyn_sym;
|
||||
|
||||
if (mObjType == OBJ_NSO)
|
||||
{
|
||||
api_info.offset = mNsoHdr.getRoEmbeddedInfo().offset;
|
||||
api_info.size = mNsoHdr.getRoEmbeddedInfo().size;
|
||||
dyn_str.offset = mNsoHdr.getRoDynStrInfo().offset;
|
||||
dyn_str.size = mNsoHdr.getRoDynStrInfo().size;
|
||||
dyn_sym.offset = mNsoHdr.getRoDynSymInfo().offset;
|
||||
dyn_sym.size = mNsoHdr.getRoDynSymInfo().size;
|
||||
}
|
||||
else
|
||||
{
|
||||
api_info.offset = mNroHdr.getRoEmbeddedInfo().memory_offset;
|
||||
api_info.size = mNroHdr.getRoEmbeddedInfo().size;
|
||||
dyn_str.offset = mNroHdr.getRoDynStrInfo().memory_offset;
|
||||
dyn_str.size = mNroHdr.getRoDynStrInfo().size;
|
||||
dyn_sym.offset = mNroHdr.getRoDynSymInfo().memory_offset;
|
||||
dyn_sym.size = mNroHdr.getRoDynSymInfo().size;
|
||||
}
|
||||
|
||||
|
||||
if (api_info.size > 0)
|
||||
{
|
||||
std::stringstream list_stream(std::string((char*)mRoBlob.getBytes() + api_info.offset, api_info.size));
|
||||
std::string api;
|
||||
|
||||
while(std::getline(list_stream, api, (char)0x00))
|
||||
{
|
||||
mApiList.push_back(api);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mApiList.clear();
|
||||
}
|
||||
|
||||
if (dyn_sym.size > 0)
|
||||
{
|
||||
mDynSymbolList.parseData(mRoBlob.getBytes() + dyn_sym.offset, dyn_sym.size, mRoBlob.getBytes() + dyn_str.offset, dyn_str.size, mInstructionType == nx::npdm::INSTR_64BIT);
|
||||
}
|
||||
}
|
||||
|
||||
void CodeObjectProcess::displayNsoHeader()
|
||||
{
|
||||
#define _HEXDUMP_L(var, len) do { for (size_t a__a__A = 0; a__a__A < len; a__a__A++) printf("%02x", var[a__a__A]); } while(0)
|
||||
|
||||
printf("[NSO Header]\n");
|
||||
printf(" ModuleId: ");
|
||||
_HEXDUMP_L(mNsoHdr.getModuleId().data, nx::nso::kModuleIdSize);
|
||||
printf("\n");
|
||||
printf(" Program Segments:\n");
|
||||
printf(" .module_name:\n");
|
||||
printf(" FileOffset: 0x%" PRIx32 "\n", mNsoHdr.getModuleNameInfo().offset);
|
||||
printf(" FileSize: 0x%" PRIx32 "\n", mNsoHdr.getModuleNameInfo().size);
|
||||
printf(" .text:\n");
|
||||
printf(" FileOffset: 0x%" PRIx32 "\n", mNsoHdr.getTextSegmentInfo().file_layout.offset);
|
||||
printf(" FileSize: 0x%" PRIx32 "%s\n", mNsoHdr.getTextSegmentInfo().file_layout.size, mNsoHdr.getTextSegmentInfo().is_compressed? " (COMPRESSED)" : "");
|
||||
printf(" .ro:\n");
|
||||
printf(" FileOffset: 0x%" PRIx32 "\n", mNsoHdr.getRoSegmentInfo().file_layout.offset);
|
||||
printf(" FileSize: 0x%" PRIx32 "%s\n", mNsoHdr.getRoSegmentInfo().file_layout.size, mNsoHdr.getRoSegmentInfo().is_compressed? " (COMPRESSED)" : "");
|
||||
printf(" .data:\n");
|
||||
printf(" FileOffset: 0x%" PRIx32 "\n", mNsoHdr.getDataSegmentInfo().file_layout.offset);
|
||||
printf(" FileSize: 0x%" PRIx32 "%s\n", mNsoHdr.getDataSegmentInfo().file_layout.size, mNsoHdr.getDataSegmentInfo().is_compressed? " (COMPRESSED)" : "");
|
||||
printf(" Program Sections:\n");
|
||||
printf(" .text:\n");
|
||||
printf(" MemoryOffset: 0x%" PRIx32 "\n", mNsoHdr.getTextSegmentInfo().memory_layout.offset);
|
||||
printf(" MemorySize: 0x%" PRIx32 "\n", mNsoHdr.getTextSegmentInfo().memory_layout.size);
|
||||
if (mNsoHdr.getTextSegmentInfo().is_hashed && mCliOutputType >= OUTPUT_VERBOSE)
|
||||
{
|
||||
printf(" Hash: ");
|
||||
_HEXDUMP_L(mNsoHdr.getTextSegmentInfo().hash.bytes, 32);
|
||||
printf("\n");
|
||||
}
|
||||
printf(" .ro:\n");
|
||||
printf(" MemoryOffset: 0x%" PRIx32 "\n", mNsoHdr.getRoSegmentInfo().memory_layout.offset);
|
||||
printf(" MemorySize: 0x%" PRIx32 "\n", mNsoHdr.getRoSegmentInfo().memory_layout.size);
|
||||
if (mNsoHdr.getRoSegmentInfo().is_hashed && mCliOutputType >= OUTPUT_VERBOSE)
|
||||
{
|
||||
printf(" Hash: ");
|
||||
_HEXDUMP_L(mNsoHdr.getRoSegmentInfo().hash.bytes, 32);
|
||||
printf("\n");
|
||||
}
|
||||
if (mCliOutputType >= OUTPUT_VERBOSE)
|
||||
{
|
||||
printf(" .api_info:\n");
|
||||
printf(" MemoryOffset: 0x%" PRIx32 "\n", mNsoHdr.getRoEmbeddedInfo().offset);
|
||||
printf(" MemorySize: 0x%" PRIx32 "\n", mNsoHdr.getRoEmbeddedInfo().size);
|
||||
printf(" .dynstr:\n");
|
||||
printf(" MemoryOffset: 0x%" PRIx32 "\n", mNsoHdr.getRoDynStrInfo().offset);
|
||||
printf(" MemorySize: 0x%" PRIx32 "\n", mNsoHdr.getRoDynStrInfo().size);
|
||||
printf(" .dynsym:\n");
|
||||
printf(" MemoryOffset: 0x%" PRIx32 "\n", mNsoHdr.getRoDynSymInfo().offset);
|
||||
printf(" MemorySize: 0x%" PRIx32 "\n", mNsoHdr.getRoDynSymInfo().size);
|
||||
}
|
||||
|
||||
printf(" .data:\n");
|
||||
printf(" MemoryOffset: 0x%" PRIx32 "\n", mNsoHdr.getDataSegmentInfo().memory_layout.offset);
|
||||
printf(" MemorySize: 0x%" PRIx32 "\n", mNsoHdr.getDataSegmentInfo().memory_layout.size);
|
||||
if (mNsoHdr.getDataSegmentInfo().is_hashed && mCliOutputType >= OUTPUT_VERBOSE)
|
||||
{
|
||||
printf(" Hash: ");
|
||||
_HEXDUMP_L(mNsoHdr.getDataSegmentInfo().hash.bytes, 32);
|
||||
printf("\n");
|
||||
}
|
||||
printf(" .bss:\n");
|
||||
printf(" MemorySize: 0x%" PRIx32 "\n", mNsoHdr.getBssSize());
|
||||
|
||||
#undef _HEXDUMP_L
|
||||
}
|
||||
|
||||
void CodeObjectProcess::displayNroHeader()
|
||||
{
|
||||
#define _HEXDUMP_L(var, len) do { for (size_t a__a__A = 0; a__a__A < len; a__a__A++) printf("%02x", var[a__a__A]); } while(0)
|
||||
|
||||
printf("[NRO Header]\n");
|
||||
printf(" RoCrt: ");
|
||||
_HEXDUMP_L(mNroHdr.getRoCrt().data, nx::nro::kRoCrtSize);
|
||||
printf("\n");
|
||||
printf(" ModuleId: ");
|
||||
_HEXDUMP_L(mNroHdr.getModuleId().data, nx::nro::kModuleIdSize);
|
||||
printf("\n");
|
||||
printf(" NroSize: 0x%" PRIx32 "\n", mNroHdr.getNroSize());
|
||||
printf(" Program Sections:\n");
|
||||
printf(" .text:\n");
|
||||
printf(" Offset: 0x%" PRIx32 "\n", mNroHdr.getTextInfo().memory_offset);
|
||||
printf(" Size: 0x%" PRIx32 "\n", mNroHdr.getTextInfo().size);
|
||||
printf(" .ro:\n");
|
||||
printf(" Offset: 0x%" PRIx32 "\n", mNroHdr.getRoInfo().memory_offset);
|
||||
printf(" Size: 0x%" PRIx32 "\n", mNroHdr.getRoInfo().size);
|
||||
if (mCliOutputType >= OUTPUT_VERBOSE)
|
||||
{
|
||||
printf(" .api_info:\n");
|
||||
printf(" Offset: 0x%" PRIx32 "\n", mNroHdr.getRoEmbeddedInfo().memory_offset);
|
||||
printf(" Size: 0x%" PRIx32 "\n", mNroHdr.getRoEmbeddedInfo().size);
|
||||
printf(" .dynstr:\n");
|
||||
printf(" Offset: 0x%" PRIx32 "\n", mNroHdr.getRoDynStrInfo().memory_offset);
|
||||
printf(" Size: 0x%" PRIx32 "\n", mNroHdr.getRoDynStrInfo().size);
|
||||
printf(" .dynsym:\n");
|
||||
printf(" Offset: 0x%" PRIx32 "\n", mNroHdr.getRoDynSymInfo().memory_offset);
|
||||
printf(" Size: 0x%" PRIx32 "\n", mNroHdr.getRoDynSymInfo().size);
|
||||
}
|
||||
printf(" .data:\n");
|
||||
printf(" Offset: 0x%" PRIx32 "\n", mNroHdr.getDataInfo().memory_offset);
|
||||
printf(" Size: 0x%" PRIx32 "\n", mNroHdr.getDataInfo().size);
|
||||
printf(" .bss:\n");
|
||||
printf(" Size: 0x%" PRIx32 "\n", mNroHdr.getBssSize());
|
||||
|
||||
#undef _HEXDUMP_L
|
||||
}
|
||||
|
||||
void CodeObjectProcess::displayNroAssetHeader()
|
||||
{
|
||||
printf("[ASET Header]\n");
|
||||
printf(" Icon:\n");
|
||||
printf(" Offset: 0x%" PRIx64 "\n", mNroAssetHdr.getIconInfo().offset);
|
||||
printf(" Size: 0x%" PRIx64 "\n", mNroAssetHdr.getIconInfo().size);
|
||||
printf(" NACP:\n");
|
||||
printf(" Offset: 0x%" PRIx64 "\n", mNroAssetHdr.getNacpInfo().offset);
|
||||
printf(" Size: 0x%" PRIx64 "\n", mNroAssetHdr.getNacpInfo().size);
|
||||
printf(" RomFS:\n");
|
||||
printf(" Offset: 0x%" PRIx64 "\n", mNroAssetHdr.getRomfsInfo().offset);
|
||||
printf(" Size: 0x%" PRIx64 "\n", mNroAssetHdr.getRomfsInfo().size);
|
||||
}
|
||||
|
||||
void CodeObjectProcess::displayRoMetaData()
|
||||
{
|
||||
if (mApiList.size() > 0 && (mListApi || mCliOutputType > OUTPUT_NORMAL))
|
||||
{
|
||||
printf("[SDK API List]\n");
|
||||
for (size_t i = 0; i < mApiList.size(); i++)
|
||||
{
|
||||
printf(" API %d:\n", (int)i);
|
||||
printf(" Type: %s\n", getApiTypeStr(mApiList[i].getApiType()));
|
||||
printf(" Vender: %s\n", mApiList[i].getVenderName().c_str());
|
||||
printf(" Module: %s\n", mApiList[i].getModuleName().c_str());
|
||||
}
|
||||
}
|
||||
if (mDynSymbolList.getDynamicSymbolList().getSize() > 0 && (mListSymbols || mCliOutputType > OUTPUT_NORMAL))
|
||||
{
|
||||
printf("[Symbol List]\n");
|
||||
for (size_t i = 0; i < mDynSymbolList.getDynamicSymbolList().getSize(); i++)
|
||||
{
|
||||
const DynamicSymbolParser::sDynSymbol& symbol = mDynSymbolList.getDynamicSymbolList()[i];
|
||||
printf(" %s [SHN=%s (%04x)][STT=%s]\n", symbol.name.c_str(), getSectionIndexStr(symbol.shn_index), symbol.shn_index, getSymbolTypeStr(symbol.symbol_type));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const char* CodeObjectProcess::getApiTypeStr(SdkApiString::ApiType type) const
|
||||
{
|
||||
const char* str;
|
||||
switch (type)
|
||||
{
|
||||
case (SdkApiString::API_MIDDLEWARE):
|
||||
str = "Middleware";
|
||||
break;
|
||||
case (SdkApiString::API_DEBUG):
|
||||
str = "Debug";
|
||||
break;
|
||||
case (SdkApiString::API_PRIVATE):
|
||||
str = "Private";
|
||||
break;
|
||||
case (SdkApiString::API_SDK_VERSION):
|
||||
str = "SDK Version";
|
||||
break;
|
||||
default:
|
||||
str = "UNKNOWN";
|
||||
break;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
const char* CodeObjectProcess::getSectionIndexStr(nx::dynsym::SpecialSectionIndex shn_index) const
|
||||
{
|
||||
const char* str;
|
||||
switch (shn_index)
|
||||
{
|
||||
case (nx::dynsym::SHN_UNDEF):
|
||||
str = "UNDEF";
|
||||
break;
|
||||
case (nx::dynsym::SHN_EXPORT):
|
||||
str = "EXPORT";
|
||||
break;
|
||||
case (nx::dynsym::SHN_LOPROC):
|
||||
str = "LOPROC";
|
||||
break;
|
||||
case (nx::dynsym::SHN_HIPROC):
|
||||
str = "HIPROC";
|
||||
break;
|
||||
case (nx::dynsym::SHN_LOOS):
|
||||
str = "LOOS";
|
||||
break;
|
||||
case (nx::dynsym::SHN_HIOS):
|
||||
str = "HIOS";
|
||||
break;
|
||||
case (nx::dynsym::SHN_ABS):
|
||||
str = "ABS";
|
||||
break;
|
||||
case (nx::dynsym::SHN_COMMON):
|
||||
str = "COMMON";
|
||||
break;
|
||||
default:
|
||||
str = "UNKNOWN";
|
||||
break;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
const char* CodeObjectProcess::getSymbolTypeStr(nx::dynsym::SymbolType symbol_type) const
|
||||
{
|
||||
const char* str;
|
||||
switch (symbol_type)
|
||||
{
|
||||
case (nx::dynsym::STT_NOTYPE):
|
||||
str = "NOTYPE";
|
||||
break;
|
||||
case (nx::dynsym::STT_OBJECT):
|
||||
str = "OBJECT";
|
||||
break;
|
||||
case (nx::dynsym::STT_FUNC):
|
||||
str = "FUNC";
|
||||
break;
|
||||
case (nx::dynsym::STT_SECTION):
|
||||
str = "SECTION";
|
||||
break;
|
||||
case (nx::dynsym::STT_FILE):
|
||||
str = "FILE";
|
||||
break;
|
||||
case (nx::dynsym::STT_LOOS):
|
||||
str = "LOOS";
|
||||
break;
|
||||
case (nx::dynsym::STT_HIOS):
|
||||
str = "HIOS";
|
||||
break;
|
||||
case (nx::dynsym::STT_LOPROC):
|
||||
str = "LOPROC";
|
||||
break;
|
||||
case (nx::dynsym::STT_HIPROC):
|
||||
str = "HIPROC";
|
||||
break;
|
||||
default:
|
||||
str = "UNKNOWN";
|
||||
break;
|
||||
}
|
||||
return str;
|
||||
}
|
328
programs/nstool/source/NroProcess.cpp
Normal file
328
programs/nstool/source/NroProcess.cpp
Normal file
|
@ -0,0 +1,328 @@
|
|||
#include <sstream>
|
||||
#include <fnd/SimpleTextOutput.h>
|
||||
#include <fnd/MemoryBlob.h>
|
||||
#include <compress/lz4.h>
|
||||
#include <nx/nro-hb.h>
|
||||
#include "OffsetAdjustedIFile.h"
|
||||
#include "NroProcess.h"
|
||||
|
||||
NroProcess::NroProcess():
|
||||
mFile(nullptr),
|
||||
mOwnIFile(false),
|
||||
mCliOutputType(OUTPUT_NORMAL),
|
||||
mVerify(false),
|
||||
mInstructionType(nx::npdm::INSTR_64BIT),
|
||||
mListApi(false),
|
||||
mListSymbols(false)
|
||||
{
|
||||
}
|
||||
|
||||
NroProcess::~NroProcess()
|
||||
{
|
||||
if (mOwnIFile)
|
||||
{
|
||||
delete mFile;
|
||||
}
|
||||
}
|
||||
|
||||
void NroProcess::process()
|
||||
{
|
||||
if (mFile == nullptr)
|
||||
{
|
||||
throw fnd::Exception(kModuleName, "No file reader set.");
|
||||
}
|
||||
|
||||
importHeader();
|
||||
importCodeSegments();
|
||||
importApiList();
|
||||
displayHeader();
|
||||
displayRoMetaData();
|
||||
if (mIsHomebrewNro)
|
||||
mAssetProc.process();
|
||||
}
|
||||
|
||||
void NroProcess::setInputFile(fnd::IFile* file, bool ownIFile)
|
||||
{
|
||||
mFile = file;
|
||||
mOwnIFile = ownIFile;
|
||||
}
|
||||
|
||||
void NroProcess::setCliOutputMode(CliOutputType type)
|
||||
{
|
||||
mCliOutputType = type;
|
||||
}
|
||||
|
||||
void NroProcess::setVerifyMode(bool verify)
|
||||
{
|
||||
mVerify = verify;
|
||||
}
|
||||
|
||||
void NroProcess::setInstructionType(nx::npdm::InstructionType type)
|
||||
{
|
||||
mInstructionType = type;
|
||||
}
|
||||
|
||||
void NroProcess::setListApi(bool listApi)
|
||||
{
|
||||
mListApi = listApi;
|
||||
}
|
||||
|
||||
void NroProcess::setListSymbols(bool listSymbols)
|
||||
{
|
||||
mListSymbols = listSymbols;
|
||||
}
|
||||
|
||||
void NroProcess::setAssetListFs(bool list)
|
||||
{
|
||||
mAssetProc.setListFs(list);
|
||||
}
|
||||
|
||||
void NroProcess::setAssetIconExtractPath(const std::string& path)
|
||||
{
|
||||
mAssetProc.setIconExtractPath(path);
|
||||
}
|
||||
|
||||
void NroProcess::setAssetNacpExtractPath(const std::string& path)
|
||||
{
|
||||
mAssetProc.setNacpExtractPath(path);
|
||||
}
|
||||
|
||||
void NroProcess::setAssetRomfsExtractPath(const std::string& path)
|
||||
{
|
||||
mAssetProc.setRomfsExtractPath(path);
|
||||
}
|
||||
|
||||
void NroProcess::importHeader()
|
||||
{
|
||||
fnd::MemoryBlob scratch;
|
||||
if (mFile->size() < sizeof(nx::sNroHeader))
|
||||
{
|
||||
throw fnd::Exception(kModuleName, "Corrupt NRO: file too small");
|
||||
}
|
||||
|
||||
scratch.alloc(sizeof(nx::sNroHeader));
|
||||
mFile->read(scratch.getBytes(), 0, scratch.getSize());
|
||||
|
||||
mHdr.importBinary(scratch.getBytes(), scratch.getSize());
|
||||
|
||||
nx::sNroHeader* raw_hdr = (nx::sNroHeader*)scratch.getBytes();
|
||||
|
||||
if (((le_uint64_t*)raw_hdr->reserved_0)->get() == nx::nro::kNroHomebrewSig && mFile->size() > mHdr.getNroSize())
|
||||
{
|
||||
mIsHomebrewNro = true;
|
||||
mAssetProc.setInputFile(new OffsetAdjustedIFile(mFile, false, mHdr.getNroSize(), mFile->size() - mHdr.getNroSize()), true);
|
||||
mAssetProc.setCliOutputMode(mCliOutputType);
|
||||
mAssetProc.setVerifyMode(mVerify);
|
||||
}
|
||||
else
|
||||
mIsHomebrewNro = false;
|
||||
|
||||
}
|
||||
|
||||
void NroProcess::importCodeSegments()
|
||||
{
|
||||
mTextBlob.alloc(mHdr.getTextInfo().size);
|
||||
mFile->read(mTextBlob.getBytes(), mHdr.getTextInfo().memory_offset, mTextBlob.getSize());
|
||||
mRoBlob.alloc(mHdr.getRoInfo().size);
|
||||
mFile->read(mRoBlob.getBytes(), mHdr.getRoInfo().memory_offset, mRoBlob.getSize());
|
||||
mDataBlob.alloc(mHdr.getDataInfo().size);
|
||||
mFile->read(mDataBlob.getBytes(), mHdr.getDataInfo().memory_offset, mDataBlob.getSize());
|
||||
}
|
||||
|
||||
void NroProcess::importApiList()
|
||||
{
|
||||
struct sLayout { size_t offset; size_t size; } api_info, dyn_str, dyn_sym;
|
||||
|
||||
api_info.offset = mHdr.getRoEmbeddedInfo().memory_offset;
|
||||
api_info.size = mHdr.getRoEmbeddedInfo().size;
|
||||
dyn_str.offset = mHdr.getRoDynStrInfo().memory_offset;
|
||||
dyn_str.size = mHdr.getRoDynStrInfo().size;
|
||||
dyn_sym.offset = mHdr.getRoDynSymInfo().memory_offset;
|
||||
dyn_sym.size = mHdr.getRoDynSymInfo().size;
|
||||
|
||||
if (api_info.size > 0)
|
||||
{
|
||||
std::stringstream list_stream(std::string((char*)mRoBlob.getBytes() + api_info.offset, api_info.size));
|
||||
std::string api;
|
||||
|
||||
while(std::getline(list_stream, api, (char)0x00))
|
||||
{
|
||||
mApiList.push_back(api);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mApiList.clear();
|
||||
}
|
||||
|
||||
if (dyn_sym.size > 0)
|
||||
{
|
||||
mDynSymbolList.parseData(mRoBlob.getBytes() + dyn_sym.offset, dyn_sym.size, mRoBlob.getBytes() + dyn_str.offset, dyn_str.size, mInstructionType == nx::npdm::INSTR_64BIT);
|
||||
}
|
||||
}
|
||||
|
||||
void NroProcess::displayHeader()
|
||||
{
|
||||
#define _HEXDUMP_L(var, len) do { for (size_t a__a__A = 0; a__a__A < len; a__a__A++) printf("%02x", var[a__a__A]); } while(0)
|
||||
if (mCliOutputType >= OUTPUT_NORMAL)
|
||||
{
|
||||
printf("[NRO Header]\n");
|
||||
printf(" RoCrt: ");
|
||||
_HEXDUMP_L(mHdr.getRoCrt().data, nx::nro::kRoCrtSize);
|
||||
printf("\n");
|
||||
printf(" ModuleId: ");
|
||||
_HEXDUMP_L(mHdr.getModuleId().data, nx::nro::kModuleIdSize);
|
||||
printf("\n");
|
||||
printf(" NroSize: 0x%" PRIx32 "\n", mHdr.getNroSize());
|
||||
printf(" Program Sections:\n");
|
||||
printf(" .text:\n");
|
||||
printf(" Offset: 0x%" PRIx32 "\n", mHdr.getTextInfo().memory_offset);
|
||||
printf(" Size: 0x%" PRIx32 "\n", mHdr.getTextInfo().size);
|
||||
printf(" .ro:\n");
|
||||
printf(" Offset: 0x%" PRIx32 "\n", mHdr.getRoInfo().memory_offset);
|
||||
printf(" Size: 0x%" PRIx32 "\n", mHdr.getRoInfo().size);
|
||||
if (mCliOutputType >= OUTPUT_VERBOSE)
|
||||
{
|
||||
printf(" .api_info:\n");
|
||||
printf(" Offset: 0x%" PRIx32 "\n", mHdr.getRoEmbeddedInfo().memory_offset);
|
||||
printf(" Size: 0x%" PRIx32 "\n", mHdr.getRoEmbeddedInfo().size);
|
||||
printf(" .dynstr:\n");
|
||||
printf(" Offset: 0x%" PRIx32 "\n", mHdr.getRoDynStrInfo().memory_offset);
|
||||
printf(" Size: 0x%" PRIx32 "\n", mHdr.getRoDynStrInfo().size);
|
||||
printf(" .dynsym:\n");
|
||||
printf(" Offset: 0x%" PRIx32 "\n", mHdr.getRoDynSymInfo().memory_offset);
|
||||
printf(" Size: 0x%" PRIx32 "\n", mHdr.getRoDynSymInfo().size);
|
||||
}
|
||||
printf(" .data:\n");
|
||||
printf(" Offset: 0x%" PRIx32 "\n", mHdr.getDataInfo().memory_offset);
|
||||
printf(" Size: 0x%" PRIx32 "\n", mHdr.getDataInfo().size);
|
||||
printf(" .bss:\n");
|
||||
printf(" Size: 0x%" PRIx32 "\n", mHdr.getBssSize());
|
||||
}
|
||||
|
||||
#undef _HEXDUMP_L
|
||||
}
|
||||
|
||||
void NroProcess::displayRoMetaData()
|
||||
{
|
||||
if (mApiList.size() > 0 && (mListApi || mCliOutputType > OUTPUT_NORMAL))
|
||||
{
|
||||
printf("[SDK API List]\n");
|
||||
for (size_t i = 0; i < mApiList.size(); i++)
|
||||
{
|
||||
printf(" API %d:\n", (int)i);
|
||||
printf(" Type: %s\n", getApiTypeStr(mApiList[i].getApiType()));
|
||||
printf(" Vender: %s\n", mApiList[i].getVenderName().c_str());
|
||||
printf(" Module: %s\n", mApiList[i].getModuleName().c_str());
|
||||
}
|
||||
}
|
||||
if (mDynSymbolList.getDynamicSymbolList().getSize() > 0 && (mListSymbols || mCliOutputType > OUTPUT_NORMAL))
|
||||
{
|
||||
printf("[Symbol List]\n");
|
||||
for (size_t i = 0; i < mDynSymbolList.getDynamicSymbolList().getSize(); i++)
|
||||
{
|
||||
const DynamicSymbolParser::sDynSymbol& symbol = mDynSymbolList.getDynamicSymbolList()[i];
|
||||
printf(" %s [SHN=%s (%04x)][STT=%s]\n", symbol.name.c_str(), getSectionIndexStr(symbol.shn_index), symbol.shn_index, getSymbolTypeStr(symbol.symbol_type));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const char* NroProcess::getApiTypeStr(SdkApiString::ApiType type) const
|
||||
{
|
||||
const char* str;
|
||||
switch (type)
|
||||
{
|
||||
case (SdkApiString::API_MIDDLEWARE):
|
||||
str = "Middleware";
|
||||
break;
|
||||
case (SdkApiString::API_DEBUG):
|
||||
str = "Debug";
|
||||
break;
|
||||
case (SdkApiString::API_PRIVATE):
|
||||
str = "Private";
|
||||
break;
|
||||
case (SdkApiString::API_SDK_VERSION):
|
||||
str = "SDK Version";
|
||||
break;
|
||||
default:
|
||||
str = "UNKNOWN";
|
||||
break;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
const char* NroProcess::getSectionIndexStr(nx::dynsym::SpecialSectionIndex shn_index) const
|
||||
{
|
||||
const char* str;
|
||||
switch (shn_index)
|
||||
{
|
||||
case (nx::dynsym::SHN_UNDEF):
|
||||
str = "UNDEF";
|
||||
break;
|
||||
case (nx::dynsym::SHN_EXPORT):
|
||||
str = "EXPORT";
|
||||
break;
|
||||
case (nx::dynsym::SHN_LOPROC):
|
||||
str = "LOPROC";
|
||||
break;
|
||||
case (nx::dynsym::SHN_HIPROC):
|
||||
str = "HIPROC";
|
||||
break;
|
||||
case (nx::dynsym::SHN_LOOS):
|
||||
str = "LOOS";
|
||||
break;
|
||||
case (nx::dynsym::SHN_HIOS):
|
||||
str = "HIOS";
|
||||
break;
|
||||
case (nx::dynsym::SHN_ABS):
|
||||
str = "ABS";
|
||||
break;
|
||||
case (nx::dynsym::SHN_COMMON):
|
||||
str = "COMMON";
|
||||
break;
|
||||
default:
|
||||
str = "UNKNOWN";
|
||||
break;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
const char* NroProcess::getSymbolTypeStr(nx::dynsym::SymbolType symbol_type) const
|
||||
{
|
||||
const char* str;
|
||||
switch (symbol_type)
|
||||
{
|
||||
case (nx::dynsym::STT_NOTYPE):
|
||||
str = "NOTYPE";
|
||||
break;
|
||||
case (nx::dynsym::STT_OBJECT):
|
||||
str = "OBJECT";
|
||||
break;
|
||||
case (nx::dynsym::STT_FUNC):
|
||||
str = "FUNC";
|
||||
break;
|
||||
case (nx::dynsym::STT_SECTION):
|
||||
str = "SECTION";
|
||||
break;
|
||||
case (nx::dynsym::STT_FILE):
|
||||
str = "FILE";
|
||||
break;
|
||||
case (nx::dynsym::STT_LOOS):
|
||||
str = "LOOS";
|
||||
break;
|
||||
case (nx::dynsym::STT_HIOS):
|
||||
str = "HIOS";
|
||||
break;
|
||||
case (nx::dynsym::STT_LOPROC):
|
||||
str = "LOPROC";
|
||||
break;
|
||||
case (nx::dynsym::STT_HIPROC):
|
||||
str = "HIPROC";
|
||||
break;
|
||||
default:
|
||||
str = "UNKNOWN";
|
||||
break;
|
||||
}
|
||||
return str;
|
||||
}
|
63
programs/nstool/source/NroProcess.h
Normal file
63
programs/nstool/source/NroProcess.h
Normal file
|
@ -0,0 +1,63 @@
|
|||
#pragma once
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <fnd/types.h>
|
||||
#include <fnd/IFile.h>
|
||||
#include <nx/npdm.h>
|
||||
#include <nx/NroHeader.h>
|
||||
#include "AssetProcess.h"
|
||||
|
||||
#include "nstool.h"
|
||||
#include "SdkApiString.h"
|
||||
#include "DynamicSymbolParser.h"
|
||||
|
||||
class NroProcess
|
||||
{
|
||||
public:
|
||||
NroProcess();
|
||||
~NroProcess();
|
||||
|
||||
void process();
|
||||
|
||||
void setInputFile(fnd::IFile* file, bool ownIFile);
|
||||
void setCliOutputMode(CliOutputType type);
|
||||
void setVerifyMode(bool verify);
|
||||
|
||||
void setInstructionType(nx::npdm::InstructionType type);
|
||||
void setListApi(bool listApi);
|
||||
void setListSymbols(bool listSymbols);
|
||||
|
||||
// for homebrew NROs with Asset blobs appended
|
||||
void setAssetListFs(bool list);
|
||||
void setAssetIconExtractPath(const std::string& path);
|
||||
void setAssetNacpExtractPath(const std::string& path);
|
||||
void setAssetRomfsExtractPath(const std::string& path);
|
||||
private:
|
||||
const std::string kModuleName = "NroProcess";
|
||||
|
||||
fnd::IFile* mFile;
|
||||
bool mOwnIFile;
|
||||
|
||||
CliOutputType mCliOutputType;
|
||||
bool mVerify;
|
||||
nx::npdm::InstructionType mInstructionType;
|
||||
bool mListApi;
|
||||
bool mListSymbols;
|
||||
|
||||
nx::NroHeader mHdr;
|
||||
bool mIsHomebrewNro;
|
||||
AssetProcess mAssetProc;
|
||||
fnd::MemoryBlob mTextBlob, mRoBlob, mDataBlob;
|
||||
std::vector<SdkApiString> mApiList;
|
||||
DynamicSymbolParser mDynSymbolList;
|
||||
|
||||
void importHeader();
|
||||
void importCodeSegments();
|
||||
void importApiList();
|
||||
void displayHeader();
|
||||
void displayRoMetaData();
|
||||
|
||||
const char* getApiTypeStr(SdkApiString::ApiType type) const;
|
||||
const char* getSectionIndexStr(nx::dynsym::SpecialSectionIndex shn_index) const;
|
||||
const char* getSymbolTypeStr(nx::dynsym::SymbolType symbol_type) const;
|
||||
};
|
395
programs/nstool/source/NsoProcess.cpp
Normal file
395
programs/nstool/source/NsoProcess.cpp
Normal file
|
@ -0,0 +1,395 @@
|
|||
#include <sstream>
|
||||
#include <fnd/SimpleTextOutput.h>
|
||||
#include <fnd/MemoryBlob.h>
|
||||
#include <compress/lz4.h>
|
||||
#include "OffsetAdjustedIFile.h"
|
||||
#include "NsoProcess.h"
|
||||
|
||||
NsoProcess::NsoProcess():
|
||||
mFile(nullptr),
|
||||
mOwnIFile(false),
|
||||
mCliOutputType(OUTPUT_NORMAL),
|
||||
mVerify(false),
|
||||
mInstructionType(nx::npdm::INSTR_64BIT),
|
||||
mListApi(false),
|
||||
mListSymbols(false)
|
||||
{
|
||||
}
|
||||
|
||||
NsoProcess::~NsoProcess()
|
||||
{
|
||||
if (mOwnIFile)
|
||||
{
|
||||
delete mFile;
|
||||
}
|
||||
}
|
||||
|
||||
void NsoProcess::process()
|
||||
{
|
||||
if (mFile == nullptr)
|
||||
{
|
||||
throw fnd::Exception(kModuleName, "No file reader set.");
|
||||
}
|
||||
|
||||
importHeader();
|
||||
importCodeSegments();
|
||||
importApiList();
|
||||
displayNsoHeader();
|
||||
displayRoMetaData();
|
||||
}
|
||||
|
||||
void NsoProcess::setInputFile(fnd::IFile* file, bool ownIFile)
|
||||
{
|
||||
mFile = file;
|
||||
mOwnIFile = ownIFile;
|
||||
}
|
||||
|
||||
void NsoProcess::setCliOutputMode(CliOutputType type)
|
||||
{
|
||||
mCliOutputType = type;
|
||||
}
|
||||
|
||||
void NsoProcess::setVerifyMode(bool verify)
|
||||
{
|
||||
mVerify = verify;
|
||||
}
|
||||
|
||||
void NsoProcess::setInstructionType(nx::npdm::InstructionType type)
|
||||
{
|
||||
mInstructionType = type;
|
||||
}
|
||||
|
||||
void NsoProcess::setListApi(bool listApi)
|
||||
{
|
||||
mListApi = listApi;
|
||||
}
|
||||
|
||||
void NsoProcess::setListSymbols(bool listSymbols)
|
||||
{
|
||||
mListSymbols = listSymbols;
|
||||
}
|
||||
|
||||
void NsoProcess::importHeader()
|
||||
{
|
||||
fnd::MemoryBlob scratch;
|
||||
if (mFile->size() < sizeof(nx::sNsoHeader))
|
||||
{
|
||||
throw fnd::Exception(kModuleName, "Corrupt NSO: file too small");
|
||||
}
|
||||
|
||||
scratch.alloc(sizeof(nx::sNsoHeader));
|
||||
mFile->read(scratch.getBytes(), 0, scratch.getSize());
|
||||
|
||||
mNsoHdr.importBinary(scratch.getBytes(), scratch.getSize());
|
||||
}
|
||||
|
||||
void NsoProcess::importCodeSegments()
|
||||
{
|
||||
fnd::MemoryBlob scratch;
|
||||
uint32_t decompressed_len;
|
||||
crypto::sha::sSha256Hash calc_hash;
|
||||
|
||||
// process text segment
|
||||
if (mNsoHdr.getTextSegmentInfo().is_compressed)
|
||||
{
|
||||
scratch.alloc(mNsoHdr.getTextSegmentInfo().file_layout.size);
|
||||
mFile->read(scratch.getBytes(), mNsoHdr.getTextSegmentInfo().file_layout.offset, scratch.getSize());
|
||||
mTextBlob.alloc(mNsoHdr.getTextSegmentInfo().memory_layout.size);
|
||||
compress::lz4::decompressData(scratch.getBytes(), scratch.getSize(), mTextBlob.getBytes(), mTextBlob.getSize(), decompressed_len);
|
||||
if (decompressed_len != mTextBlob.getSize())
|
||||
{
|
||||
throw fnd::Exception(kModuleName, "NSO text segment failed to decompress");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mTextBlob.alloc(mNsoHdr.getTextSegmentInfo().file_layout.size);
|
||||
mFile->read(mTextBlob.getBytes(), mNsoHdr.getTextSegmentInfo().file_layout.offset, mTextBlob.getSize());
|
||||
}
|
||||
if (mNsoHdr.getTextSegmentInfo().is_hashed)
|
||||
{
|
||||
crypto::sha::Sha256(mTextBlob.getBytes(), mTextBlob.getSize(), calc_hash.bytes);
|
||||
if (calc_hash != mNsoHdr.getTextSegmentInfo().hash)
|
||||
{
|
||||
throw fnd::Exception(kModuleName, "NSO text segment failed SHA256 verification");
|
||||
}
|
||||
}
|
||||
|
||||
// process ro segment
|
||||
if (mNsoHdr.getRoSegmentInfo().is_compressed)
|
||||
{
|
||||
scratch.alloc(mNsoHdr.getRoSegmentInfo().file_layout.size);
|
||||
mFile->read(scratch.getBytes(), mNsoHdr.getRoSegmentInfo().file_layout.offset, scratch.getSize());
|
||||
mRoBlob.alloc(mNsoHdr.getRoSegmentInfo().memory_layout.size);
|
||||
compress::lz4::decompressData(scratch.getBytes(), scratch.getSize(), mRoBlob.getBytes(), mRoBlob.getSize(), decompressed_len);
|
||||
if (decompressed_len != mRoBlob.getSize())
|
||||
{
|
||||
throw fnd::Exception(kModuleName, "NSO ro segment failed to decompress");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mRoBlob.alloc(mNsoHdr.getRoSegmentInfo().file_layout.size);
|
||||
mFile->read(mRoBlob.getBytes(), mNsoHdr.getRoSegmentInfo().file_layout.offset, mRoBlob.getSize());
|
||||
}
|
||||
if (mNsoHdr.getRoSegmentInfo().is_hashed)
|
||||
{
|
||||
crypto::sha::Sha256(mRoBlob.getBytes(), mRoBlob.getSize(), calc_hash.bytes);
|
||||
if (calc_hash != mNsoHdr.getRoSegmentInfo().hash)
|
||||
{
|
||||
throw fnd::Exception(kModuleName, "NSO ro segment failed SHA256 verification");
|
||||
}
|
||||
}
|
||||
|
||||
// process data segment
|
||||
if (mNsoHdr.getDataSegmentInfo().is_compressed)
|
||||
{
|
||||
scratch.alloc(mNsoHdr.getDataSegmentInfo().file_layout.size);
|
||||
mFile->read(scratch.getBytes(), mNsoHdr.getDataSegmentInfo().file_layout.offset, scratch.getSize());
|
||||
mDataBlob.alloc(mNsoHdr.getDataSegmentInfo().memory_layout.size);
|
||||
compress::lz4::decompressData(scratch.getBytes(), scratch.getSize(), mDataBlob.getBytes(), mDataBlob.getSize(), decompressed_len);
|
||||
if (decompressed_len != mDataBlob.getSize())
|
||||
{
|
||||
throw fnd::Exception(kModuleName, "NSO data segment failed to decompress");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mDataBlob.alloc(mNsoHdr.getDataSegmentInfo().file_layout.size);
|
||||
mFile->read(mDataBlob.getBytes(), mNsoHdr.getDataSegmentInfo().file_layout.offset, mDataBlob.getSize());
|
||||
}
|
||||
if (mNsoHdr.getDataSegmentInfo().is_hashed)
|
||||
{
|
||||
crypto::sha::Sha256(mDataBlob.getBytes(), mDataBlob.getSize(), calc_hash.bytes);
|
||||
if (calc_hash != mNsoHdr.getDataSegmentInfo().hash)
|
||||
{
|
||||
throw fnd::Exception(kModuleName, "NSO data segment failed SHA256 verification");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NsoProcess::importApiList()
|
||||
{
|
||||
struct sLayout { size_t offset; size_t size; } api_info, dyn_str, dyn_sym;
|
||||
|
||||
api_info.offset = mNsoHdr.getRoEmbeddedInfo().offset;
|
||||
api_info.size = mNsoHdr.getRoEmbeddedInfo().size;
|
||||
dyn_str.offset = mNsoHdr.getRoDynStrInfo().offset;
|
||||
dyn_str.size = mNsoHdr.getRoDynStrInfo().size;
|
||||
dyn_sym.offset = mNsoHdr.getRoDynSymInfo().offset;
|
||||
dyn_sym.size = mNsoHdr.getRoDynSymInfo().size;
|
||||
|
||||
if (api_info.size > 0)
|
||||
{
|
||||
std::stringstream list_stream(std::string((char*)mRoBlob.getBytes() + api_info.offset, api_info.size));
|
||||
std::string api;
|
||||
|
||||
while(std::getline(list_stream, api, (char)0x00))
|
||||
{
|
||||
mApiList.push_back(api);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mApiList.clear();
|
||||
}
|
||||
|
||||
if (dyn_sym.size > 0)
|
||||
{
|
||||
mDynSymbolList.parseData(mRoBlob.getBytes() + dyn_sym.offset, dyn_sym.size, mRoBlob.getBytes() + dyn_str.offset, dyn_str.size, mInstructionType == nx::npdm::INSTR_64BIT);
|
||||
}
|
||||
}
|
||||
|
||||
void NsoProcess::displayNsoHeader()
|
||||
{
|
||||
#define _HEXDUMP_L(var, len) do { for (size_t a__a__A = 0; a__a__A < len; a__a__A++) printf("%02x", var[a__a__A]); } while(0)
|
||||
|
||||
if (mCliOutputType >= OUTPUT_NORMAL)
|
||||
{
|
||||
printf("[NSO Header]\n");
|
||||
printf(" ModuleId: ");
|
||||
_HEXDUMP_L(mNsoHdr.getModuleId().data, nx::nso::kModuleIdSize);
|
||||
printf("\n");
|
||||
printf(" Program Segments:\n");
|
||||
printf(" .module_name:\n");
|
||||
printf(" FileOffset: 0x%" PRIx32 "\n", mNsoHdr.getModuleNameInfo().offset);
|
||||
printf(" FileSize: 0x%" PRIx32 "\n", mNsoHdr.getModuleNameInfo().size);
|
||||
printf(" .text:\n");
|
||||
printf(" FileOffset: 0x%" PRIx32 "\n", mNsoHdr.getTextSegmentInfo().file_layout.offset);
|
||||
printf(" FileSize: 0x%" PRIx32 "%s\n", mNsoHdr.getTextSegmentInfo().file_layout.size, mNsoHdr.getTextSegmentInfo().is_compressed? " (COMPRESSED)" : "");
|
||||
printf(" .ro:\n");
|
||||
printf(" FileOffset: 0x%" PRIx32 "\n", mNsoHdr.getRoSegmentInfo().file_layout.offset);
|
||||
printf(" FileSize: 0x%" PRIx32 "%s\n", mNsoHdr.getRoSegmentInfo().file_layout.size, mNsoHdr.getRoSegmentInfo().is_compressed? " (COMPRESSED)" : "");
|
||||
printf(" .data:\n");
|
||||
printf(" FileOffset: 0x%" PRIx32 "\n", mNsoHdr.getDataSegmentInfo().file_layout.offset);
|
||||
printf(" FileSize: 0x%" PRIx32 "%s\n", mNsoHdr.getDataSegmentInfo().file_layout.size, mNsoHdr.getDataSegmentInfo().is_compressed? " (COMPRESSED)" : "");
|
||||
printf(" Program Sections:\n");
|
||||
printf(" .text:\n");
|
||||
printf(" MemoryOffset: 0x%" PRIx32 "\n", mNsoHdr.getTextSegmentInfo().memory_layout.offset);
|
||||
printf(" MemorySize: 0x%" PRIx32 "\n", mNsoHdr.getTextSegmentInfo().memory_layout.size);
|
||||
if (mNsoHdr.getTextSegmentInfo().is_hashed && mCliOutputType >= OUTPUT_VERBOSE)
|
||||
{
|
||||
printf(" Hash: ");
|
||||
_HEXDUMP_L(mNsoHdr.getTextSegmentInfo().hash.bytes, 32);
|
||||
printf("\n");
|
||||
}
|
||||
printf(" .ro:\n");
|
||||
printf(" MemoryOffset: 0x%" PRIx32 "\n", mNsoHdr.getRoSegmentInfo().memory_layout.offset);
|
||||
printf(" MemorySize: 0x%" PRIx32 "\n", mNsoHdr.getRoSegmentInfo().memory_layout.size);
|
||||
if (mNsoHdr.getRoSegmentInfo().is_hashed && mCliOutputType >= OUTPUT_VERBOSE)
|
||||
{
|
||||
printf(" Hash: ");
|
||||
_HEXDUMP_L(mNsoHdr.getRoSegmentInfo().hash.bytes, 32);
|
||||
printf("\n");
|
||||
}
|
||||
if (mCliOutputType >= OUTPUT_VERBOSE)
|
||||
{
|
||||
printf(" .api_info:\n");
|
||||
printf(" MemoryOffset: 0x%" PRIx32 "\n", mNsoHdr.getRoEmbeddedInfo().offset);
|
||||
printf(" MemorySize: 0x%" PRIx32 "\n", mNsoHdr.getRoEmbeddedInfo().size);
|
||||
printf(" .dynstr:\n");
|
||||
printf(" MemoryOffset: 0x%" PRIx32 "\n", mNsoHdr.getRoDynStrInfo().offset);
|
||||
printf(" MemorySize: 0x%" PRIx32 "\n", mNsoHdr.getRoDynStrInfo().size);
|
||||
printf(" .dynsym:\n");
|
||||
printf(" MemoryOffset: 0x%" PRIx32 "\n", mNsoHdr.getRoDynSymInfo().offset);
|
||||
printf(" MemorySize: 0x%" PRIx32 "\n", mNsoHdr.getRoDynSymInfo().size);
|
||||
}
|
||||
|
||||
printf(" .data:\n");
|
||||
printf(" MemoryOffset: 0x%" PRIx32 "\n", mNsoHdr.getDataSegmentInfo().memory_layout.offset);
|
||||
printf(" MemorySize: 0x%" PRIx32 "\n", mNsoHdr.getDataSegmentInfo().memory_layout.size);
|
||||
if (mNsoHdr.getDataSegmentInfo().is_hashed && mCliOutputType >= OUTPUT_VERBOSE)
|
||||
{
|
||||
printf(" Hash: ");
|
||||
_HEXDUMP_L(mNsoHdr.getDataSegmentInfo().hash.bytes, 32);
|
||||
printf("\n");
|
||||
}
|
||||
printf(" .bss:\n");
|
||||
printf(" MemorySize: 0x%" PRIx32 "\n", mNsoHdr.getBssSize());
|
||||
}
|
||||
#undef _HEXDUMP_L
|
||||
}
|
||||
|
||||
void NsoProcess::displayRoMetaData()
|
||||
{
|
||||
if (mApiList.size() > 0 && (mListApi || mCliOutputType > OUTPUT_NORMAL))
|
||||
{
|
||||
printf("[SDK API List]\n");
|
||||
for (size_t i = 0; i < mApiList.size(); i++)
|
||||
{
|
||||
printf(" API %d:\n", (int)i);
|
||||
printf(" Type: %s\n", getApiTypeStr(mApiList[i].getApiType()));
|
||||
printf(" Vender: %s\n", mApiList[i].getVenderName().c_str());
|
||||
printf(" Module: %s\n", mApiList[i].getModuleName().c_str());
|
||||
}
|
||||
}
|
||||
if (mDynSymbolList.getDynamicSymbolList().getSize() > 0 && (mListSymbols || mCliOutputType > OUTPUT_NORMAL))
|
||||
{
|
||||
printf("[Symbol List]\n");
|
||||
for (size_t i = 0; i < mDynSymbolList.getDynamicSymbolList().getSize(); i++)
|
||||
{
|
||||
const DynamicSymbolParser::sDynSymbol& symbol = mDynSymbolList.getDynamicSymbolList()[i];
|
||||
printf(" %s [SHN=%s (%04x)][STT=%s]\n", symbol.name.c_str(), getSectionIndexStr(symbol.shn_index), symbol.shn_index, getSymbolTypeStr(symbol.symbol_type));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const char* NsoProcess::getApiTypeStr(SdkApiString::ApiType type) const
|
||||
{
|
||||
const char* str;
|
||||
switch (type)
|
||||
{
|
||||
case (SdkApiString::API_MIDDLEWARE):
|
||||
str = "Middleware";
|
||||
break;
|
||||
case (SdkApiString::API_DEBUG):
|
||||
str = "Debug";
|
||||
break;
|
||||
case (SdkApiString::API_PRIVATE):
|
||||
str = "Private";
|
||||
break;
|
||||
case (SdkApiString::API_SDK_VERSION):
|
||||
str = "SDK Version";
|
||||
break;
|
||||
default:
|
||||
str = "UNKNOWN";
|
||||
break;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
const char* NsoProcess::getSectionIndexStr(nx::dynsym::SpecialSectionIndex shn_index) const
|
||||
{
|
||||
const char* str;
|
||||
switch (shn_index)
|
||||
{
|
||||
case (nx::dynsym::SHN_UNDEF):
|
||||
str = "UNDEF";
|
||||
break;
|
||||
case (nx::dynsym::SHN_EXPORT):
|
||||
str = "EXPORT";
|
||||
break;
|
||||
case (nx::dynsym::SHN_LOPROC):
|
||||
str = "LOPROC";
|
||||
break;
|
||||
case (nx::dynsym::SHN_HIPROC):
|
||||
str = "HIPROC";
|
||||
break;
|
||||
case (nx::dynsym::SHN_LOOS):
|
||||
str = "LOOS";
|
||||
break;
|
||||
case (nx::dynsym::SHN_HIOS):
|
||||
str = "HIOS";
|
||||
break;
|
||||
case (nx::dynsym::SHN_ABS):
|
||||
str = "ABS";
|
||||
break;
|
||||
case (nx::dynsym::SHN_COMMON):
|
||||
str = "COMMON";
|
||||
break;
|
||||
default:
|
||||
str = "UNKNOWN";
|
||||
break;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
const char* NsoProcess::getSymbolTypeStr(nx::dynsym::SymbolType symbol_type) const
|
||||
{
|
||||
const char* str;
|
||||
switch (symbol_type)
|
||||
{
|
||||
case (nx::dynsym::STT_NOTYPE):
|
||||
str = "NOTYPE";
|
||||
break;
|
||||
case (nx::dynsym::STT_OBJECT):
|
||||
str = "OBJECT";
|
||||
break;
|
||||
case (nx::dynsym::STT_FUNC):
|
||||
str = "FUNC";
|
||||
break;
|
||||
case (nx::dynsym::STT_SECTION):
|
||||
str = "SECTION";
|
||||
break;
|
||||
case (nx::dynsym::STT_FILE):
|
||||
str = "FILE";
|
||||
break;
|
||||
case (nx::dynsym::STT_LOOS):
|
||||
str = "LOOS";
|
||||
break;
|
||||
case (nx::dynsym::STT_HIOS):
|
||||
str = "HIOS";
|
||||
break;
|
||||
case (nx::dynsym::STT_LOPROC):
|
||||
str = "LOPROC";
|
||||
break;
|
||||
case (nx::dynsym::STT_HIPROC):
|
||||
str = "HIPROC";
|
||||
break;
|
||||
default:
|
||||
str = "UNKNOWN";
|
||||
break;
|
||||
}
|
||||
return str;
|
||||
}
|
|
@ -5,25 +5,16 @@
|
|||
#include <fnd/IFile.h>
|
||||
#include <nx/npdm.h>
|
||||
#include <nx/NsoHeader.h>
|
||||
#include <nx/NroHeader.h>
|
||||
#include <nx/NroAssetHeader.h>
|
||||
|
||||
#include "nstool.h"
|
||||
#include "SdkApiString.h"
|
||||
#include "DynamicSymbolParser.h"
|
||||
|
||||
class CodeObjectProcess
|
||||
class NsoProcess
|
||||
{
|
||||
public:
|
||||
enum CodeObjectType
|
||||
{
|
||||
OBJ_NSO,
|
||||
OBJ_NRO,
|
||||
OBJ_INVALID
|
||||
};
|
||||
|
||||
CodeObjectProcess();
|
||||
~CodeObjectProcess();
|
||||
NsoProcess();
|
||||
~NsoProcess();
|
||||
|
||||
void process();
|
||||
|
||||
|
@ -31,35 +22,22 @@ public:
|
|||
void setCliOutputMode(CliOutputType type);
|
||||
void setVerifyMode(bool verify);
|
||||
|
||||
void setCodeObjectType(CodeObjectType type);
|
||||
void setInstructionType(nx::npdm::InstructionType type);
|
||||
void setListApi(bool listApi);
|
||||
void setListSymbols(bool listSymbols);
|
||||
|
||||
// processed data
|
||||
const nx::NsoHeader& getNsoHeader() const;
|
||||
const fnd::MemoryBlob& getTextBlob() const;
|
||||
const fnd::MemoryBlob& getRoBlob() const;
|
||||
const fnd::MemoryBlob& getDataBlob() const;
|
||||
const std::vector<SdkApiString>& getApiList() const;
|
||||
|
||||
private:
|
||||
const std::string kModuleName = "CodeObjectProcess";
|
||||
const std::string kModuleName = "NsoProcess";
|
||||
|
||||
fnd::IFile* mFile;
|
||||
bool mOwnIFile;
|
||||
|
||||
CliOutputType mCliOutputType;
|
||||
bool mVerify;
|
||||
CodeObjectType mObjType;
|
||||
nx::npdm::InstructionType mInstructionType;
|
||||
bool mListApi;
|
||||
bool mListSymbols;
|
||||
|
||||
nx::NsoHeader mNsoHdr;
|
||||
nx::NroHeader mNroHdr;
|
||||
bool mIsHomebrewNro;
|
||||
nx::NroAssetHeader mNroAssetHdr;
|
||||
fnd::MemoryBlob mTextBlob, mRoBlob, mDataBlob;
|
||||
std::vector<SdkApiString> mApiList;
|
||||
DynamicSymbolParser mDynSymbolList;
|
||||
|
@ -68,8 +46,6 @@ private:
|
|||
void importCodeSegments();
|
||||
void importApiList();
|
||||
void displayNsoHeader();
|
||||
void displayNroHeader();
|
||||
void displayNroAssetHeader();
|
||||
void displayRoMetaData();
|
||||
|
||||
const char* getApiTypeStr(SdkApiString::ApiType type) const;
|
|
@ -19,6 +19,7 @@
|
|||
#include <nx/romfs.h>
|
||||
#include <nx/nso.h>
|
||||
#include <nx/nro.h>
|
||||
#include <nx/aset.h>
|
||||
|
||||
UserSettings::UserSettings()
|
||||
{}
|
||||
|
@ -40,7 +41,7 @@ void UserSettings::showHelp()
|
|||
printf("\n General Options:\n");
|
||||
printf(" -d, --dev Use devkit keyset\n");
|
||||
printf(" -k, --keyset Specify keyset file\n");
|
||||
printf(" -t, --type Specify input file type [xci, pfs, romfs, nca, npdm, cnmt, nso, nro]\n");
|
||||
printf(" -t, --type Specify input file type [xci, pfs, romfs, nca, npdm, cnmt, nso, nro, aset]\n");
|
||||
printf(" -y, --verify Verify file\n");
|
||||
printf(" -v, --verbose Verbose output\n");
|
||||
printf(" -q, --quiet Minimal output\n");
|
||||
|
@ -69,6 +70,12 @@ void UserSettings::showHelp()
|
|||
printf(" --listapi Print SDK API List.\n");
|
||||
printf(" --listsym Print Dynamic Symbols.\n");
|
||||
printf(" --insttype Specify instruction type [64bit|32bit] (64bit is assumed).\n");
|
||||
printf("\n ASET (Homebrew Asset Blob)\n");
|
||||
printf(" nstool [--listfs] [--icon <file> --nacp <file> --fsdir <dir>] <file>\n");
|
||||
printf(" --listfs Print filesystem in embedded RomFS partition.\n");
|
||||
printf(" --icon Extract icon partition to file.\n");
|
||||
printf(" --nacp Extract NACP partition to file.\n");
|
||||
printf(" --fsdir Extract RomFS partition to directory.\n");
|
||||
|
||||
}
|
||||
|
||||
|
@ -161,6 +168,15 @@ const sOptional<std::string>& UserSettings::getNcaPart3Path() const
|
|||
return mNcaPart3Path;
|
||||
}
|
||||
|
||||
const sOptional<std::string>& UserSettings::getAssetIconPath() const
|
||||
{
|
||||
return mAssetIconPath;
|
||||
}
|
||||
|
||||
const sOptional<std::string>& UserSettings::getAssetNacpPath() const
|
||||
{
|
||||
return mAssetNacpPath;
|
||||
}
|
||||
|
||||
void UserSettings::populateCmdArgs(int argc, char** argv, sCmdArgs& cmd_args)
|
||||
{
|
||||
|
@ -319,6 +335,18 @@ void UserSettings::populateCmdArgs(int argc, char** argv, sCmdArgs& cmd_args)
|
|||
cmd_args.inst_type = args[i + 1];
|
||||
}
|
||||
|
||||
else if (args[i] == "--icon")
|
||||
{
|
||||
if (!hasParamter) throw fnd::Exception(kModuleName, args[i] + " requries a parameter.");
|
||||
cmd_args.asset_icon_path = args[i + 1];
|
||||
}
|
||||
|
||||
else if (args[i] == "--nacp")
|
||||
{
|
||||
if (!hasParamter) throw fnd::Exception(kModuleName, args[i] + " requries a parameter.");
|
||||
cmd_args.asset_nacp_path = args[i + 1];
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
throw fnd::Exception(kModuleName, args[i] + " is not recognised.");
|
||||
|
@ -585,6 +613,9 @@ void UserSettings::populateUserSettings(sCmdArgs& args)
|
|||
mListApi = args.list_api.isSet;
|
||||
mListSymbols = args.list_sym.isSet;
|
||||
|
||||
mAssetIconPath = args.asset_icon_path;
|
||||
mAssetNacpPath = args.asset_nacp_path;
|
||||
|
||||
// determine output path
|
||||
if (args.verbose_output.isSet)
|
||||
mOutputType = OUTPUT_VERBOSE;
|
||||
|
@ -645,6 +676,8 @@ FileType UserSettings::getFileTypeFromString(const std::string& type_str)
|
|||
type = FILE_NSO;
|
||||
else if (str == "nro")
|
||||
type = FILE_NRO;
|
||||
else if (str == "aset" || str == "asset")
|
||||
type = FILE_HB_ASSET;
|
||||
else
|
||||
type = FILE_INVALID;
|
||||
|
||||
|
@ -667,7 +700,7 @@ FileType UserSettings::determineFileTypeFromFile(const std::string& path)
|
|||
// close file
|
||||
file.close();
|
||||
|
||||
fnd::SimpleTextOutput::hxdStyleDump(scratch.getBytes(), scratch.getSize());
|
||||
//fnd::SimpleTextOutput::hxdStyleDump(scratch.getBytes(), scratch.getSize());
|
||||
|
||||
// prepare decrypted NCA data
|
||||
byte_t nca_raw[nx::nca::kHeaderSize];
|
||||
|
@ -709,6 +742,9 @@ FileType UserSettings::determineFileTypeFromFile(const std::string& path)
|
|||
// test nso
|
||||
else if (_ASSERT_SIZE(sizeof(nx::sNroHeader)) && _QUICK_CAST(nx::sNroHeader, 0)->signature.get() == nx::nro::kNroSig)
|
||||
file_type = FILE_NRO;
|
||||
// test hb asset
|
||||
else if (_ASSERT_SIZE(sizeof(nx::sAssetHeader)) && _QUICK_CAST(nx::sAssetHeader, 0)->signature.get() == nx::aset::kAssetSig)
|
||||
file_type = FILE_HB_ASSET;
|
||||
// else unrecognised
|
||||
else
|
||||
file_type = FILE_INVALID;
|
||||
|
|
|
@ -35,6 +35,8 @@ public:
|
|||
const sOptional<std::string>& getNcaPart1Path() const;
|
||||
const sOptional<std::string>& getNcaPart2Path() const;
|
||||
const sOptional<std::string>& getNcaPart3Path() const;
|
||||
const sOptional<std::string>& getAssetIconPath() const;
|
||||
const sOptional<std::string>& getAssetNacpPath() const;
|
||||
|
||||
private:
|
||||
const std::string kModuleName = "UserSettings";
|
||||
|
@ -64,6 +66,8 @@ private:
|
|||
sOptional<bool> list_api;
|
||||
sOptional<bool> list_sym;
|
||||
sOptional<std::string> inst_type;
|
||||
sOptional<std::string> asset_icon_path;
|
||||
sOptional<std::string> asset_nacp_path;
|
||||
};
|
||||
|
||||
std::string mInputPath;
|
||||
|
@ -84,6 +88,9 @@ private:
|
|||
sOptional<std::string> mNcaPart2Path;
|
||||
sOptional<std::string> mNcaPart3Path;
|
||||
|
||||
sOptional<std::string> mAssetIconPath;
|
||||
sOptional<std::string> mAssetNacpPath;
|
||||
|
||||
bool mListApi;
|
||||
bool mListSymbols;
|
||||
nx::npdm::InstructionType mInstructionType;
|
||||
|
|
|
@ -7,7 +7,9 @@
|
|||
#include "NcaProcess.h"
|
||||
#include "NpdmProcess.h"
|
||||
#include "CnmtProcess.h"
|
||||
#include "CodeObjectProcess.h"
|
||||
#include "NsoProcess.h"
|
||||
#include "NroProcess.h"
|
||||
#include "AssetProcess.h"
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
|
@ -108,19 +110,60 @@ int main(int argc, char** argv)
|
|||
|
||||
cnmt.process();
|
||||
}
|
||||
else if (user_set.getFileType() == FILE_NSO || user_set.getFileType() == FILE_NRO)
|
||||
else if (user_set.getFileType() == FILE_NSO)
|
||||
{
|
||||
CodeObjectProcess obj;
|
||||
NsoProcess obj;
|
||||
|
||||
obj.setInputFile(new fnd::SimpleFile(user_set.getInputPath(), fnd::SimpleFile::Read), OWN_IFILE);
|
||||
obj.setCliOutputMode(user_set.getCliOutputType());
|
||||
obj.setVerifyMode(user_set.isVerifyFile());
|
||||
|
||||
obj.setCodeObjectType(user_set.getFileType() == FILE_NSO ? obj.OBJ_NSO : obj.OBJ_NRO);
|
||||
obj.setInstructionType(user_set.getInstType());
|
||||
obj.setListApi(user_set.isListApi());
|
||||
obj.setListSymbols(user_set.isListSymbols());
|
||||
|
||||
obj.process();
|
||||
}
|
||||
else if (user_set.getFileType() == FILE_NRO)
|
||||
{
|
||||
NroProcess obj;
|
||||
|
||||
obj.setInputFile(new fnd::SimpleFile(user_set.getInputPath(), fnd::SimpleFile::Read), OWN_IFILE);
|
||||
obj.setCliOutputMode(user_set.getCliOutputType());
|
||||
obj.setVerifyMode(user_set.isVerifyFile());
|
||||
|
||||
obj.setInstructionType(user_set.getInstType());
|
||||
obj.setListApi(user_set.isListApi());
|
||||
obj.setListSymbols(user_set.isListSymbols());
|
||||
|
||||
if (user_set.getAssetIconPath().isSet)
|
||||
obj.setAssetIconExtractPath(user_set.getAssetIconPath().var);
|
||||
if (user_set.getAssetNacpPath().isSet)
|
||||
obj.setAssetNacpExtractPath(user_set.getAssetNacpPath().var);
|
||||
|
||||
if (user_set.getFsPath().isSet)
|
||||
obj.setAssetRomfsExtractPath(user_set.getFsPath().var);
|
||||
obj.setAssetListFs(user_set.isListFs());
|
||||
|
||||
obj.process();
|
||||
}
|
||||
else if (user_set.getFileType() == FILE_HB_ASSET)
|
||||
{
|
||||
AssetProcess obj;
|
||||
|
||||
obj.setInputFile(new fnd::SimpleFile(user_set.getInputPath(), fnd::SimpleFile::Read), OWN_IFILE);
|
||||
obj.setCliOutputMode(user_set.getCliOutputType());
|
||||
obj.setVerifyMode(user_set.isVerifyFile());
|
||||
|
||||
if (user_set.getAssetIconPath().isSet)
|
||||
obj.setIconExtractPath(user_set.getAssetIconPath().var);
|
||||
if (user_set.getAssetNacpPath().isSet)
|
||||
obj.setNacpExtractPath(user_set.getAssetNacpPath().var);
|
||||
|
||||
if (user_set.getFsPath().isSet)
|
||||
obj.setRomfsExtractPath(user_set.getFsPath().var);
|
||||
obj.setListFs(user_set.isListFs());
|
||||
|
||||
obj.process();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ enum FileType
|
|||
FILE_CNMT,
|
||||
FILE_NSO,
|
||||
FILE_NRO,
|
||||
FILE_HB_ASSET,
|
||||
FILE_INVALID = -1,
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue