Merge pull request #20 from jakcron/nro-development

Improved NRO Support.
This commit is contained in:
Jack 2018-06-11 14:15:11 +08:00 committed by GitHub
commit c50e93d181
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 1675 additions and 559 deletions

View file

@ -13,7 +13,8 @@
"${workspaceRoot}/lib/libcompress/include", "${workspaceRoot}/lib/libcompress/include",
"${workspaceRoot}/lib/libes/include", "${workspaceRoot}/lib/libes/include",
"${workspaceRoot}/lib/libfnd/include", "${workspaceRoot}/lib/libfnd/include",
"${workspaceRoot}/lib/libnx/include" "${workspaceRoot}/lib/libnx/include",
"${workspaceRoot}/lib/libnx-hb/include"
], ],
"defines": [], "defines": [],
"intelliSenseMode": "clang-x64", "intelliSenseMode": "clang-x64",

View file

@ -28,5 +28,6 @@
"tuple": "cpp", "tuple": "cpp",
"__locale": "cpp", "__locale": "cpp",
"cinttypes": "cpp" "cinttypes": "cpp"
"__bit_reference": "cpp",
} }
} }

View file

@ -37,10 +37,13 @@ EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nstool", "programs\nstool\nstool.vcxproj", "{AF09FA96-4463-417D-8FE6-526063F41349}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nstool", "programs\nstool\nstool.vcxproj", "{AF09FA96-4463-417D-8FE6-526063F41349}"
ProjectSection(ProjectDependencies) = postProject ProjectSection(ProjectDependencies) = postProject
{CF01B5B7-730A-447F-9BB2-5EDA9B082177} = {CF01B5B7-730A-447F-9BB2-5EDA9B082177} {CF01B5B7-730A-447F-9BB2-5EDA9B082177} = {CF01B5B7-730A-447F-9BB2-5EDA9B082177}
{738CB4FC-CD9E-4B81-A04B-DEADBFA71C63} = {738CB4FC-CD9E-4B81-A04B-DEADBFA71C63}
EndProjectSection EndProjectSection
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libcompress", "lib\libcompress\libcompress.vcxproj", "{CF01B5B7-730A-447F-9BB2-5EDA9B082177}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libcompress", "lib\libcompress\libcompress.vcxproj", "{CF01B5B7-730A-447F-9BB2-5EDA9B082177}"
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libnx-hb", "lib\libnx-hb\libnx-hb.vcxproj", "{738CB4FC-CD9E-4B81-A04B-DEADBFA71C63}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64 Debug|x64 = Debug|x64
@ -97,6 +100,14 @@ Global
{CF01B5B7-730A-447F-9BB2-5EDA9B082177}.Release|x64.Build.0 = Release|x64 {CF01B5B7-730A-447F-9BB2-5EDA9B082177}.Release|x64.Build.0 = Release|x64
{CF01B5B7-730A-447F-9BB2-5EDA9B082177}.Release|x86.ActiveCfg = Release|Win32 {CF01B5B7-730A-447F-9BB2-5EDA9B082177}.Release|x86.ActiveCfg = Release|Win32
{CF01B5B7-730A-447F-9BB2-5EDA9B082177}.Release|x86.Build.0 = Release|Win32 {CF01B5B7-730A-447F-9BB2-5EDA9B082177}.Release|x86.Build.0 = Release|Win32
{738CB4FC-CD9E-4B81-A04B-DEADBFA71C63}.Debug|x64.ActiveCfg = Debug|x64
{738CB4FC-CD9E-4B81-A04B-DEADBFA71C63}.Debug|x64.Build.0 = Debug|x64
{738CB4FC-CD9E-4B81-A04B-DEADBFA71C63}.Debug|x86.ActiveCfg = Debug|Win32
{738CB4FC-CD9E-4B81-A04B-DEADBFA71C63}.Debug|x86.Build.0 = Debug|Win32
{738CB4FC-CD9E-4B81-A04B-DEADBFA71C63}.Release|x64.ActiveCfg = Release|x64
{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
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -108,6 +119,7 @@ Global
{7BE99936-0D40-410D-944B-4513C2EFF8DC} = {170B4A09-1B67-4A62-93AB-116EBCFF4A8C} {7BE99936-0D40-410D-944B-4513C2EFF8DC} = {170B4A09-1B67-4A62-93AB-116EBCFF4A8C}
{AF09FA96-4463-417D-8FE6-526063F41349} = {E0863FCC-8E72-490D-BE1B-458F12CA8298} {AF09FA96-4463-417D-8FE6-526063F41349} = {E0863FCC-8E72-490D-BE1B-458F12CA8298}
{CF01B5B7-730A-447F-9BB2-5EDA9B082177} = {170B4A09-1B67-4A62-93AB-116EBCFF4A8C} {CF01B5B7-730A-447F-9BB2-5EDA9B082177} = {170B4A09-1B67-4A62-93AB-116EBCFF4A8C}
{738CB4FC-CD9E-4B81-A04B-DEADBFA71C63} = {170B4A09-1B67-4A62-93AB-116EBCFF4A8C}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {07DCCACC-D10D-47C9-85AE-FB9C54DB7D62} SolutionGuid = {07DCCACC-D10D-47C9-85AE-FB9C54DB7D62}

View file

@ -14,7 +14,8 @@ Tools & Libraries for NX (Nintendo Switch).
* __libcrypto__ - Cryptographic functions (AES,SHA,RSA). Wrapper for [mbedTLS](https://github.com/ARMmbed/mbedtls) * __libcrypto__ - Cryptographic functions (AES,SHA,RSA). Wrapper for [mbedTLS](https://github.com/ARMmbed/mbedtls)
* __libcompress__ - Compression algorithms (LZ4). Wrapper for [lz4](https://github.com/lz4/lz4) * __libcompress__ - Compression algorithms (LZ4). Wrapper for [lz4](https://github.com/lz4/lz4)
* __libes__ - Handling of (NX relevant) eShop file type processing. (eTickets, etc) * __libes__ - Handling of (NX relevant) eShop file type processing. (eTickets, etc)
* __libnx__ - Handling of NX file types * __libnx__ - Handling of NX file types.
* __libnx-hb__ - Handling of NX (homebrew extensions) file types.
# Building # Building

View file

@ -0,0 +1,79 @@
#pragma once
#include <nx/aset.h>
#include <fnd/MemoryBlob.h>
#include <fnd/List.h>
#include <fnd/ISerialiseableBinary.h>
namespace nx
{
class AssetHeader :
public fnd::ISerialiseableBinary
{
public:
struct sSection
{
uint64_t offset;
uint64_t size;
void operator=(const sSection& other)
{
offset = other.offset;
size = other.size;
}
bool operator==(const sSection& other) const
{
return (offset == other.offset) \
&& (size == other.size);
}
bool operator!=(const sSection& other) const
{
return !(*this == other);
}
};
AssetHeader();
AssetHeader(const AssetHeader& other);
AssetHeader(const byte_t* bytes, size_t len);
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;
size_t getSize() const;
// export/import binary
void exportBinary();
void importBinary(const byte_t* bytes, size_t len);
// variables
void clear();
const sSection& getIconInfo() const;
void setIconInfo(const sSection& info);
const sSection& getNacpInfo() const;
void setNacpInfo(const sSection& info);
const sSection& getRomfsInfo() const;
void setRomfsInfo(const sSection& info);
private:
const std::string kModuleName = "NRO_ASSET_HEADER";
// binary
fnd::MemoryBlob mBinaryBlob;
// data
sSection mIconInfo;
sSection mNacpInfo;
sSection mRomfsInfo;
// helpers
bool isEqual(const AssetHeader& other) const;
void copyFrom(const AssetHeader& other);
};
}

View 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)
}

View file

@ -0,0 +1,11 @@
#pragma once
#include <nx/nro.h>
#include <nx/macro.h>
namespace nx
{
namespace nro
{
static const uint64_t kNroHomebrewSig = _MAKE_STRUCT_SIGNATURE_U64("HOMEBREW");
}
}

View file

@ -0,0 +1,135 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{738CB4FC-CD9E-4B81-A04B-DEADBFA71C63}</ProjectGuid>
<RootNamespace>libnxhb</RootNamespace>
<WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>..\libfnd\include;..\libcrypto\include;..\libnx\include;..\libnx-hb\include;</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>..\libfnd\include;..\libcrypto\include;..\libnx\include;..\libnx-hb\include;</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>..\libfnd\include;..\libcrypto\include;..\libnx\include;..\libnx-hb\include;</AdditionalIncludeDirectories>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>..\libfnd\include;..\libcrypto\include;..\libnx\include;..\libnx-hb\include;</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="include\nx\aset.h" />
<ClInclude Include="include\nx\AssetHeader.h" />
<ClInclude Include="include\nx\nro-hb.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="source\AssetHeader.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="makefile" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\nx\aset.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\nx\AssetHeader.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\nx\nro-hb.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="source\AssetHeader.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="makefile" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
</Project>

47
lib/libnx-hb/makefile Normal file
View file

@ -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 nx
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-value
CFLAGS = -std=c11 $(INCS) -Wall -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 += -Wno-unused-but-set-variable
CXXFLAGS += -Wno-unused-but-set-variable
else
UNAME = $(shell uname -s)
ifeq ($(UNAME), Darwin)
# MacOS Only Flags/Libs
CFLAGS += -Wno-unused-private-field
CXXFLAGS += -Wno-unused-private-field
ARFLAGS = rc
else
# *nix Only Flags/Libs
CFLAGS += -Wno-unused-but-set-variable
CXXFLAGS += -Wno-unused-but-set-variable
endif
endif
# Output
OUTPUT = $(shell basename $(CURDIR)).a
main: build
rebuild: clean build
build: $(OBJS)
ar $(ARFLAGS) $(OUTPUT) $(OBJS)
clean:
rm -rf $(OUTPUT) $(OBJS)

View 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;
}

View file

@ -1,3 +1,5 @@
#pragma once #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(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]))

View file

@ -56,6 +56,7 @@
<ClInclude Include="include\nx\NpdmHeader.h" /> <ClInclude Include="include\nx\NpdmHeader.h" />
<ClInclude Include="include\nx\NcaHeader.h" /> <ClInclude Include="include\nx\NcaHeader.h" />
<ClInclude Include="include\nx\nro.h" /> <ClInclude Include="include\nx\nro.h" />
<ClInclude Include="include\nx\NroHeader.h" />
<ClInclude Include="include\nx\nrr.h" /> <ClInclude Include="include\nx\nrr.h" />
<ClInclude Include="include\nx\nso.h" /> <ClInclude Include="include\nx\nso.h" />
<ClInclude Include="include\nx\NsoHeader.h" /> <ClInclude Include="include\nx\NsoHeader.h" />
@ -104,6 +105,7 @@
<ClCompile Include="source\NcaUtils.cpp" /> <ClCompile Include="source\NcaUtils.cpp" />
<ClCompile Include="source\NpdmBinary.cpp" /> <ClCompile Include="source\NpdmBinary.cpp" />
<ClCompile Include="source\NpdmHeader.cpp" /> <ClCompile Include="source\NpdmHeader.cpp" />
<ClCompile Include="source\NroHeader.cpp" />
<ClCompile Include="source\NsoHeader.cpp" /> <ClCompile Include="source\NsoHeader.cpp" />
<ClCompile Include="source\PfsHeader.cpp" /> <ClCompile Include="source\PfsHeader.cpp" />
<ClCompile Include="source\SacBinary.cpp" /> <ClCompile Include="source\SacBinary.cpp" />

View file

@ -174,6 +174,9 @@
<ClInclude Include="include\nx\nrr.h"> <ClInclude Include="include\nx\nrr.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="include\nx\NroHeader.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="makefile" /> <None Include="makefile" />
@ -260,6 +263,9 @@
<ClCompile Include="source\NpdmHeader.cpp"> <ClCompile Include="source\NpdmHeader.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="source\NroHeader.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\NsoHeader.cpp"> <ClCompile Include="source\NsoHeader.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>

View file

@ -22,7 +22,7 @@ bool nx::NroHeader::operator==(const NroHeader& other) const
bool nx::NroHeader::operator!=(const NroHeader& other) const bool nx::NroHeader::operator!=(const NroHeader& other) const
{ {
return !(*this != other); return !(*this == other);
} }
void nx::NroHeader::operator=(const NroHeader& other) void nx::NroHeader::operator=(const NroHeader& other)
@ -42,7 +42,49 @@ size_t nx::NroHeader::getSize() const
void nx::NroHeader::exportBinary() void nx::NroHeader::exportBinary()
{ {
throw fnd::Exception(kModuleName, "exportBinary() unsupported"); mBinaryBlob.alloc(sizeof(sNroHeader));
nx::sNroHeader* hdr = (nx::sNroHeader*)mBinaryBlob.getBytes();
// set header identifers
hdr->signature = nro::kNroSig;
hdr->format_version = nro::kDefaultFormatVersion;
hdr->flags = 0;
// set ro crt
memcpy(hdr->ro_crt, mRoCrt.data, nro::kRoCrtSize);
// set nro size
hdr->size = mNroSize;
// set text section
hdr->text.memory_offset = mTextInfo.memory_offset;
hdr->text.size = mTextInfo.size;
// set ro section
hdr->ro.memory_offset = mRoInfo.memory_offset;
hdr->ro.size = mRoInfo.size;
// set data section
hdr->data.memory_offset = mDataInfo.memory_offset;
hdr->data.size = mDataInfo.size;
// set bss size
hdr->bss_size = mBssSize;
// set moduleid
memcpy(hdr->module_id, mModuleId.data, nro::kModuleIdSize);
// set ro embedded info
hdr->embedded.memory_offset = mRoEmbeddedInfo.memory_offset;
hdr->embedded.size = mRoEmbeddedInfo.size;
// set ro dyn str info
hdr->dyn_str.memory_offset = mRoDynStrInfo.memory_offset;
hdr->dyn_str.size = mRoDynStrInfo.size;
// set ro dyn sym info
hdr->dyn_sym.memory_offset = mRoDynSymInfo.memory_offset;
hdr->dyn_sym.size = mRoDynSymInfo.size;
} }
void nx::NroHeader::importBinary(const byte_t* bytes, size_t len) void nx::NroHeader::importBinary(const byte_t* bytes, size_t len)

View file

@ -1,4 +1,4 @@
LIBS = libfnd libcrypto libcompress libes libnx LIBS = libfnd libcrypto libcompress libes libnx libnx-hb
main: build main: build
rebuild: clean build rebuild: clean build

View file

@ -3,7 +3,7 @@ SRC_DIR = source
OBJS = $(foreach dir,$(SRC_DIR),$(subst .cpp,.o,$(wildcard $(dir)/*.cpp))) $(foreach dir,$(SRC_DIR),$(subst .c,.o,$(wildcard $(dir)/*.c))) OBJS = $(foreach dir,$(SRC_DIR),$(subst .cpp,.o,$(wildcard $(dir)/*.cpp))) $(foreach dir,$(SRC_DIR),$(subst .c,.o,$(wildcard $(dir)/*.c)))
# External dependencies # External dependencies
DEPENDS = nx crypto compress fnd DEPENDS = nx-hb nx crypto compress fnd
LIB_DIR = ../../lib LIB_DIR = ../../lib
LIBS = $(foreach dep,$(DEPENDS), -L"$(LIB_DIR)/lib$(dep)" -l$(dep)) LIBS = $(foreach dep,$(DEPENDS), -L"$(LIB_DIR)/lib$(dep)" -l$(dep))
INCS = $(foreach dep,$(DEPENDS), -I"$(LIB_DIR)/lib$(dep)/include") INCS = $(foreach dep,$(DEPENDS), -I"$(LIB_DIR)/lib$(dep)/include")

View file

@ -90,7 +90,7 @@
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>..\..\lib\libfnd\include;..\..\lib\libcompress\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>..\..\lib\libfnd\include;..\..\lib\libcompress\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;..\..\lib\libnx-hb\include</AdditionalIncludeDirectories>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
@ -105,7 +105,7 @@
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>..\..\lib\libfnd\include;..\..\lib\libcompress\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>..\..\lib\libfnd\include;..\..\lib\libcompress\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;..\..\lib\libnx-hb\include</AdditionalIncludeDirectories>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
@ -122,7 +122,7 @@
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>..\..\lib\libfnd\include;..\..\lib\libcompress\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>..\..\lib\libfnd\include;..\..\lib\libcompress\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;..\..\lib\libnx-hb\include</AdditionalIncludeDirectories>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
@ -141,7 +141,7 @@
<SDLCheck>true</SDLCheck> <SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode> <ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>..\..\lib\libfnd\include;..\..\lib\libcompress\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include</AdditionalIncludeDirectories> <AdditionalIncludeDirectories>..\..\lib\libfnd\include;..\..\lib\libcompress\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;..\..\lib\libnx-hb\include</AdditionalIncludeDirectories>
</ClCompile> </ClCompile>
<Link> <Link>
<SubSystem>Console</SubSystem> <SubSystem>Console</SubSystem>
@ -160,29 +160,36 @@
<ProjectReference Include="..\..\lib\libfnd\fnd.vcxproj"> <ProjectReference Include="..\..\lib\libfnd\fnd.vcxproj">
<Project>{4d27edb9-5110-44fe-8ce2-d46c5ad3c55b}</Project> <Project>{4d27edb9-5110-44fe-8ce2-d46c5ad3c55b}</Project>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\..\lib\libnx-hb\libnx-hb.vcxproj">
<Project>{738cb4fc-cd9e-4b81-a04b-deadbfa71c63}</Project>
</ProjectReference>
<ProjectReference Include="..\..\lib\libnx\nx.vcxproj"> <ProjectReference Include="..\..\lib\libnx\nx.vcxproj">
<Project>{91ba9e79-8242-4f7d-b997-0dfec95ea22b}</Project> <Project>{91ba9e79-8242-4f7d-b997-0dfec95ea22b}</Project>
</ProjectReference> </ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="source\AesCtrWrappedIFile.h" /> <ClInclude Include="source\AesCtrWrappedIFile.h" />
<ClInclude Include="source\AssetProcess.h" />
<ClInclude Include="source\CnmtProcess.h" /> <ClInclude Include="source\CnmtProcess.h" />
<ClInclude Include="source\DynamicSymbolParser.h" /> <ClInclude Include="source\DynamicSymbolParser.h" />
<ClInclude Include="source\HashTreeMeta.h" /> <ClInclude Include="source\HashTreeMeta.h" />
<ClInclude Include="source\HashTreeWrappedIFile.h" /> <ClInclude Include="source\HashTreeWrappedIFile.h" />
<ClInclude Include="source\NcaProcess.h" /> <ClInclude Include="source\NcaProcess.h" />
<ClInclude Include="source\NpdmProcess.h" /> <ClInclude Include="source\NpdmProcess.h" />
<ClInclude Include="source\NroProcess.h" />
<ClInclude Include="source\NsoProcess.h" /> <ClInclude Include="source\NsoProcess.h" />
<ClInclude Include="source\nstool.h" /> <ClInclude Include="source\nstool.h" />
<ClInclude Include="source\OffsetAdjustedIFile.h" /> <ClInclude Include="source\OffsetAdjustedIFile.h" />
<ClInclude Include="source\PfsProcess.h" /> <ClInclude Include="source\PfsProcess.h" />
<ClInclude Include="source\RomfsProcess.h" /> <ClInclude Include="source\RomfsProcess.h" />
<ClInclude Include="source\SdkApiString.h" />
<ClInclude Include="source\UserSettings.h" /> <ClInclude Include="source\UserSettings.h" />
<ClInclude Include="source\version.h" /> <ClInclude Include="source\version.h" />
<ClInclude Include="source\XciProcess.h" /> <ClInclude Include="source\XciProcess.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="source\AesCtrWrappedIFile.cpp" /> <ClCompile Include="source\AesCtrWrappedIFile.cpp" />
<ClCompile Include="source\AssetProcess.cpp" />
<ClCompile Include="source\CnmtProcess.cpp" /> <ClCompile Include="source\CnmtProcess.cpp" />
<ClCompile Include="source\DynamicSymbolParser.cpp" /> <ClCompile Include="source\DynamicSymbolParser.cpp" />
<ClCompile Include="source\HashTreeMeta.cpp" /> <ClCompile Include="source\HashTreeMeta.cpp" />
@ -190,10 +197,12 @@
<ClCompile Include="source\main.cpp" /> <ClCompile Include="source\main.cpp" />
<ClCompile Include="source\NcaProcess.cpp" /> <ClCompile Include="source\NcaProcess.cpp" />
<ClCompile Include="source\NpdmProcess.cpp" /> <ClCompile Include="source\NpdmProcess.cpp" />
<ClCompile Include="source\NroProcess.cpp" />
<ClCompile Include="source\NsoProcess.cpp" /> <ClCompile Include="source\NsoProcess.cpp" />
<ClCompile Include="source\OffsetAdjustedIFile.cpp" /> <ClCompile Include="source\OffsetAdjustedIFile.cpp" />
<ClCompile Include="source\PfsProcess.cpp" /> <ClCompile Include="source\PfsProcess.cpp" />
<ClCompile Include="source\RomfsProcess.cpp" /> <ClCompile Include="source\RomfsProcess.cpp" />
<ClCompile Include="source\SdkApiString.cpp" />
<ClCompile Include="source\UserSettings.cpp" /> <ClCompile Include="source\UserSettings.cpp" />
<ClCompile Include="source\XciProcess.cpp" /> <ClCompile Include="source\XciProcess.cpp" />
</ItemGroup> </ItemGroup>

View file

@ -60,6 +60,15 @@
<ClInclude Include="source\DynamicSymbolParser.h"> <ClInclude Include="source\DynamicSymbolParser.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="source\AssetProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="source\NroProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="source\SdkApiString.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="source\main.cpp"> <ClCompile Include="source\main.cpp">
@ -104,6 +113,15 @@
<ClCompile Include="source\DynamicSymbolParser.cpp"> <ClCompile Include="source\DynamicSymbolParser.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="source\AssetProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\NroProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\SdkApiString.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="makefile" /> <None Include="makefile" />

View 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);
}
}

View 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();
};

View file

@ -1,518 +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();
}
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());
}
}
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::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;
}

View 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;
}

View 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;
};

View 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(), (uint32_t)scratch.getSize(), mTextBlob.getBytes(), (uint32_t)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(), (uint32_t)scratch.getSize(), mRoBlob.getBytes(), (uint32_t)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(), (uint32_t)scratch.getSize(), mDataBlob.getBytes(), (uint32_t)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;
}

View file

@ -5,24 +5,16 @@
#include <fnd/IFile.h> #include <fnd/IFile.h>
#include <nx/npdm.h> #include <nx/npdm.h>
#include <nx/NsoHeader.h> #include <nx/NsoHeader.h>
#include <nx/NroHeader.h>
#include "nstool.h" #include "nstool.h"
#include "SdkApiString.h" #include "SdkApiString.h"
#include "DynamicSymbolParser.h" #include "DynamicSymbolParser.h"
class CodeObjectProcess class NsoProcess
{ {
public: public:
enum CodeObjectType NsoProcess();
{ ~NsoProcess();
OBJ_NSO,
OBJ_NRO,
OBJ_INVALID
};
CodeObjectProcess();
~CodeObjectProcess();
void process(); void process();
@ -30,33 +22,22 @@ public:
void setCliOutputMode(CliOutputType type); void setCliOutputMode(CliOutputType type);
void setVerifyMode(bool verify); void setVerifyMode(bool verify);
void setCodeObjectType(CodeObjectType type);
void setInstructionType(nx::npdm::InstructionType type); void setInstructionType(nx::npdm::InstructionType type);
void setListApi(bool listApi); void setListApi(bool listApi);
void setListSymbols(bool listSymbols); 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: private:
const std::string kModuleName = "CodeObjectProcess"; const std::string kModuleName = "NsoProcess";
fnd::IFile* mFile; fnd::IFile* mFile;
bool mOwnIFile; bool mOwnIFile;
CliOutputType mCliOutputType; CliOutputType mCliOutputType;
bool mVerify; bool mVerify;
CodeObjectType mObjType;
nx::npdm::InstructionType mInstructionType; nx::npdm::InstructionType mInstructionType;
bool mListApi; bool mListApi;
bool mListSymbols; bool mListSymbols;
nx::NsoHeader mNsoHdr; nx::NsoHeader mNsoHdr;
nx::NroHeader mNroHdr;
fnd::MemoryBlob mTextBlob, mRoBlob, mDataBlob; fnd::MemoryBlob mTextBlob, mRoBlob, mDataBlob;
std::vector<SdkApiString> mApiList; std::vector<SdkApiString> mApiList;
DynamicSymbolParser mDynSymbolList; DynamicSymbolParser mDynSymbolList;
@ -65,7 +46,6 @@ private:
void importCodeSegments(); void importCodeSegments();
void importApiList(); void importApiList();
void displayNsoHeader(); void displayNsoHeader();
void displayNroHeader();
void displayRoMetaData(); void displayRoMetaData();
const char* getApiTypeStr(SdkApiString::ApiType type) const; const char* getApiTypeStr(SdkApiString::ApiType type) const;

View file

@ -19,6 +19,7 @@
#include <nx/romfs.h> #include <nx/romfs.h>
#include <nx/nso.h> #include <nx/nso.h>
#include <nx/nro.h> #include <nx/nro.h>
#include <nx/aset.h>
UserSettings::UserSettings() UserSettings::UserSettings()
{} {}
@ -40,7 +41,7 @@ void UserSettings::showHelp()
printf("\n General Options:\n"); printf("\n General Options:\n");
printf(" -d, --dev Use devkit keyset\n"); printf(" -d, --dev Use devkit keyset\n");
printf(" -k, --keyset Specify keyset file\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(" -y, --verify Verify file\n");
printf(" -v, --verbose Verbose output\n"); printf(" -v, --verbose Verbose output\n");
printf(" -q, --quiet Minimal output\n"); printf(" -q, --quiet Minimal output\n");
@ -69,6 +70,12 @@ void UserSettings::showHelp()
printf(" --listapi Print SDK API List.\n"); printf(" --listapi Print SDK API List.\n");
printf(" --listsym Print Dynamic Symbols.\n"); printf(" --listsym Print Dynamic Symbols.\n");
printf(" --insttype Specify instruction type [64bit|32bit] (64bit is assumed).\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; 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) 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]; 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 else
{ {
throw fnd::Exception(kModuleName, args[i] + " is not recognised."); throw fnd::Exception(kModuleName, args[i] + " is not recognised.");
@ -585,6 +613,9 @@ void UserSettings::populateUserSettings(sCmdArgs& args)
mListApi = args.list_api.isSet; mListApi = args.list_api.isSet;
mListSymbols = args.list_sym.isSet; mListSymbols = args.list_sym.isSet;
mAssetIconPath = args.asset_icon_path;
mAssetNacpPath = args.asset_nacp_path;
// determine output path // determine output path
if (args.verbose_output.isSet) if (args.verbose_output.isSet)
mOutputType = OUTPUT_VERBOSE; mOutputType = OUTPUT_VERBOSE;
@ -645,6 +676,8 @@ FileType UserSettings::getFileTypeFromString(const std::string& type_str)
type = FILE_NSO; type = FILE_NSO;
else if (str == "nro") else if (str == "nro")
type = FILE_NRO; type = FILE_NRO;
else if (str == "aset" || str == "asset")
type = FILE_HB_ASSET;
else else
type = FILE_INVALID; type = FILE_INVALID;
@ -667,7 +700,7 @@ FileType UserSettings::determineFileTypeFromFile(const std::string& path)
// close file // close file
file.close(); file.close();
fnd::SimpleTextOutput::hxdStyleDump(scratch.getBytes(), scratch.getSize()); //fnd::SimpleTextOutput::hxdStyleDump(scratch.getBytes(), scratch.getSize());
// prepare decrypted NCA data // prepare decrypted NCA data
byte_t nca_raw[nx::nca::kHeaderSize]; byte_t nca_raw[nx::nca::kHeaderSize];
@ -709,6 +742,9 @@ FileType UserSettings::determineFileTypeFromFile(const std::string& path)
// test nso // test nso
else if (_ASSERT_SIZE(sizeof(nx::sNroHeader)) && _QUICK_CAST(nx::sNroHeader, 0)->signature.get() == nx::nro::kNroSig) else if (_ASSERT_SIZE(sizeof(nx::sNroHeader)) && _QUICK_CAST(nx::sNroHeader, 0)->signature.get() == nx::nro::kNroSig)
file_type = FILE_NRO; 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 unrecognised
else else
file_type = FILE_INVALID; file_type = FILE_INVALID;

View file

@ -35,6 +35,8 @@ public:
const sOptional<std::string>& getNcaPart1Path() const; const sOptional<std::string>& getNcaPart1Path() const;
const sOptional<std::string>& getNcaPart2Path() const; const sOptional<std::string>& getNcaPart2Path() const;
const sOptional<std::string>& getNcaPart3Path() const; const sOptional<std::string>& getNcaPart3Path() const;
const sOptional<std::string>& getAssetIconPath() const;
const sOptional<std::string>& getAssetNacpPath() const;
private: private:
const std::string kModuleName = "UserSettings"; const std::string kModuleName = "UserSettings";
@ -64,6 +66,8 @@ private:
sOptional<bool> list_api; sOptional<bool> list_api;
sOptional<bool> list_sym; sOptional<bool> list_sym;
sOptional<std::string> inst_type; sOptional<std::string> inst_type;
sOptional<std::string> asset_icon_path;
sOptional<std::string> asset_nacp_path;
}; };
std::string mInputPath; std::string mInputPath;
@ -84,6 +88,9 @@ private:
sOptional<std::string> mNcaPart2Path; sOptional<std::string> mNcaPart2Path;
sOptional<std::string> mNcaPart3Path; sOptional<std::string> mNcaPart3Path;
sOptional<std::string> mAssetIconPath;
sOptional<std::string> mAssetNacpPath;
bool mListApi; bool mListApi;
bool mListSymbols; bool mListSymbols;
nx::npdm::InstructionType mInstructionType; nx::npdm::InstructionType mInstructionType;

View file

@ -179,7 +179,7 @@ void XciProcess::displayHeader()
printf(" SelT1Key: 0x%x\n", mHdr.getSelT1Key()); printf(" SelT1Key: 0x%x\n", mHdr.getSelT1Key());
printf(" SelKey: 0x%x\n", mHdr.getSelKey()); printf(" SelKey: 0x%x\n", mHdr.getSelKey());
printf(" LimArea: 0x%x", mHdr.getLimAreaPage()); printf(" LimArea: 0x%x", mHdr.getLimAreaPage());
if (mHdr.getLimAreaPage() != -1) if (mHdr.getLimAreaPage() != (uint32_t)(-1))
printf(" (0x%" PRIx64 ")", nx::XciUtils::blockToAddr(mHdr.getLimAreaPage())); printf(" (0x%" PRIx64 ")", nx::XciUtils::blockToAddr(mHdr.getLimAreaPage()));
printf("\n"); printf("\n");

View file

@ -7,7 +7,9 @@
#include "NcaProcess.h" #include "NcaProcess.h"
#include "NpdmProcess.h" #include "NpdmProcess.h"
#include "CnmtProcess.h" #include "CnmtProcess.h"
#include "CodeObjectProcess.h" #include "NsoProcess.h"
#include "NroProcess.h"
#include "AssetProcess.h"
int main(int argc, char** argv) int main(int argc, char** argv)
{ {
@ -108,19 +110,60 @@ int main(int argc, char** argv)
cnmt.process(); 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.setInputFile(new fnd::SimpleFile(user_set.getInputPath(), fnd::SimpleFile::Read), OWN_IFILE);
obj.setCliOutputMode(user_set.getCliOutputType()); obj.setCliOutputMode(user_set.getCliOutputType());
obj.setVerifyMode(user_set.isVerifyFile()); obj.setVerifyMode(user_set.isVerifyFile());
obj.setCodeObjectType(user_set.getFileType() == FILE_NSO ? obj.OBJ_NSO : obj.OBJ_NRO);
obj.setInstructionType(user_set.getInstType()); obj.setInstructionType(user_set.getInstType());
obj.setListApi(user_set.isListApi()); obj.setListApi(user_set.isListApi());
obj.setListSymbols(user_set.isListSymbols()); 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(); obj.process();
} }
} }

View file

@ -26,6 +26,7 @@ enum FileType
FILE_CNMT, FILE_CNMT,
FILE_NSO, FILE_NSO,
FILE_NRO, FILE_NRO,
FILE_HB_ASSET,
FILE_INVALID = -1, FILE_INVALID = -1,
}; };