diff --git a/build/visualstudio/nstool.sln b/build/visualstudio/nstool.sln
index ad181a1..202b2b9 100644
--- a/build/visualstudio/nstool.sln
+++ b/build/visualstudio/nstool.sln
@@ -1,129 +1,151 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 15
-VisualStudioVersion = 15.0.28010.2036
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nstool", "nstool\nstool.vcxproj", "{775EF5EB-CA49-4994-8AC4-47B4A5385266}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "deps", "deps", "{05929EAE-4471-4E8E-A6F3-793A81623D7F}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libfnd", "..\..\deps\libfnd\build\visualstudio\libfnd\libfnd.vcxproj", "{4E578016-34BA-4A1E-B8EC-37A48780B6CA}"
- ProjectSection(ProjectDependencies) = postProject
- {E741ADED-7900-4E07-8DB0-D008C336C3FB} = {E741ADED-7900-4E07-8DB0-D008C336C3FB}
- {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C} = {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}
- EndProjectSection
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "liblz4", "..\..\deps\liblz4\build\visualstudio\liblz4\liblz4.vcxproj", "{E741ADED-7900-4E07-8DB0-D008C336C3FB}"
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libnintendo-es", "..\..\deps\libnintendo-es\build\visualstudio\libnintendo-es\libnintendo-es.vcxproj", "{8616D6C9-C8DE-4C3F-AFC2-625636664C2B}"
- ProjectSection(ProjectDependencies) = postProject
- {4E578016-34BA-4A1E-B8EC-37A48780B6CA} = {4E578016-34BA-4A1E-B8EC-37A48780B6CA}
- EndProjectSection
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libnintendo-hac", "..\..\deps\libnintendo-hac\build\visualstudio\libnintendo-hac\libnintendo-hac.vcxproj", "{8885C125-83FB-4F73-A93A-C712B1434D54}"
- ProjectSection(ProjectDependencies) = postProject
- {4E578016-34BA-4A1E-B8EC-37A48780B6CA} = {4E578016-34BA-4A1E-B8EC-37A48780B6CA}
- EndProjectSection
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libnintendo-hac-hb", "..\..\deps\libnintendo-hac-hb\build\visualstudio\libnintendo-hac-hb\libnintendo-hac-hb.vcxproj", "{24D001B4-D439-4967-9371-DC3E0523EB19}"
- ProjectSection(ProjectDependencies) = postProject
- {4E578016-34BA-4A1E-B8EC-37A48780B6CA} = {4E578016-34BA-4A1E-B8EC-37A48780B6CA}
- {8885C125-83FB-4F73-A93A-C712B1434D54} = {8885C125-83FB-4F73-A93A-C712B1434D54}
- EndProjectSection
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libnintendo-pki", "..\..\deps\libnintendo-pki\build\visualstudio\libnintendo-pki\libnintendo-pki.vcxproj", "{0BEF63A0-2801-4563-AB65-1E2FD881C3AF}"
- ProjectSection(ProjectDependencies) = postProject
- {4E578016-34BA-4A1E-B8EC-37A48780B6CA} = {4E578016-34BA-4A1E-B8EC-37A48780B6CA}
- EndProjectSection
-EndProject
-Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmbedtls", "..\..\deps\libmbedtls\build\visualstudio\libmbedtls\libmbedtls.vcxproj", "{7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|x64 = Debug|x64
- Debug|x86 = Debug|x86
- Release|x64 = Release|x64
- Release|x86 = Release|x86
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {775EF5EB-CA49-4994-8AC4-47B4A5385266}.Debug|x64.ActiveCfg = Debug|x64
- {775EF5EB-CA49-4994-8AC4-47B4A5385266}.Debug|x64.Build.0 = Debug|x64
- {775EF5EB-CA49-4994-8AC4-47B4A5385266}.Debug|x86.ActiveCfg = Debug|Win32
- {775EF5EB-CA49-4994-8AC4-47B4A5385266}.Debug|x86.Build.0 = Debug|Win32
- {775EF5EB-CA49-4994-8AC4-47B4A5385266}.Release|x64.ActiveCfg = Release|x64
- {775EF5EB-CA49-4994-8AC4-47B4A5385266}.Release|x64.Build.0 = Release|x64
- {775EF5EB-CA49-4994-8AC4-47B4A5385266}.Release|x86.ActiveCfg = Release|Win32
- {775EF5EB-CA49-4994-8AC4-47B4A5385266}.Release|x86.Build.0 = Release|Win32
- {4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Debug|x64.ActiveCfg = Debug|x64
- {4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Debug|x64.Build.0 = Debug|x64
- {4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Debug|x86.ActiveCfg = Debug|Win32
- {4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Debug|x86.Build.0 = Debug|Win32
- {4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Release|x64.ActiveCfg = Release|x64
- {4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Release|x64.Build.0 = Release|x64
- {4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Release|x86.ActiveCfg = Release|Win32
- {4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Release|x86.Build.0 = Release|Win32
- {E741ADED-7900-4E07-8DB0-D008C336C3FB}.Debug|x64.ActiveCfg = Debug|x64
- {E741ADED-7900-4E07-8DB0-D008C336C3FB}.Debug|x64.Build.0 = Debug|x64
- {E741ADED-7900-4E07-8DB0-D008C336C3FB}.Debug|x86.ActiveCfg = Debug|Win32
- {E741ADED-7900-4E07-8DB0-D008C336C3FB}.Debug|x86.Build.0 = Debug|Win32
- {E741ADED-7900-4E07-8DB0-D008C336C3FB}.Release|x64.ActiveCfg = Release|x64
- {E741ADED-7900-4E07-8DB0-D008C336C3FB}.Release|x64.Build.0 = Release|x64
- {E741ADED-7900-4E07-8DB0-D008C336C3FB}.Release|x86.ActiveCfg = Release|Win32
- {E741ADED-7900-4E07-8DB0-D008C336C3FB}.Release|x86.Build.0 = Release|Win32
- {8616D6C9-C8DE-4C3F-AFC2-625636664C2B}.Debug|x64.ActiveCfg = Debug|x64
- {8616D6C9-C8DE-4C3F-AFC2-625636664C2B}.Debug|x64.Build.0 = Debug|x64
- {8616D6C9-C8DE-4C3F-AFC2-625636664C2B}.Debug|x86.ActiveCfg = Debug|Win32
- {8616D6C9-C8DE-4C3F-AFC2-625636664C2B}.Debug|x86.Build.0 = Debug|Win32
- {8616D6C9-C8DE-4C3F-AFC2-625636664C2B}.Release|x64.ActiveCfg = Release|x64
- {8616D6C9-C8DE-4C3F-AFC2-625636664C2B}.Release|x64.Build.0 = Release|x64
- {8616D6C9-C8DE-4C3F-AFC2-625636664C2B}.Release|x86.ActiveCfg = Release|Win32
- {8616D6C9-C8DE-4C3F-AFC2-625636664C2B}.Release|x86.Build.0 = Release|Win32
- {8885C125-83FB-4F73-A93A-C712B1434D54}.Debug|x64.ActiveCfg = Debug|x64
- {8885C125-83FB-4F73-A93A-C712B1434D54}.Debug|x64.Build.0 = Debug|x64
- {8885C125-83FB-4F73-A93A-C712B1434D54}.Debug|x86.ActiveCfg = Debug|Win32
- {8885C125-83FB-4F73-A93A-C712B1434D54}.Debug|x86.Build.0 = Debug|Win32
- {8885C125-83FB-4F73-A93A-C712B1434D54}.Release|x64.ActiveCfg = Release|x64
- {8885C125-83FB-4F73-A93A-C712B1434D54}.Release|x64.Build.0 = Release|x64
- {8885C125-83FB-4F73-A93A-C712B1434D54}.Release|x86.ActiveCfg = Release|Win32
- {8885C125-83FB-4F73-A93A-C712B1434D54}.Release|x86.Build.0 = Release|Win32
- {24D001B4-D439-4967-9371-DC3E0523EB19}.Debug|x64.ActiveCfg = Debug|x64
- {24D001B4-D439-4967-9371-DC3E0523EB19}.Debug|x64.Build.0 = Debug|x64
- {24D001B4-D439-4967-9371-DC3E0523EB19}.Debug|x86.ActiveCfg = Debug|Win32
- {24D001B4-D439-4967-9371-DC3E0523EB19}.Debug|x86.Build.0 = Debug|Win32
- {24D001B4-D439-4967-9371-DC3E0523EB19}.Release|x64.ActiveCfg = Release|x64
- {24D001B4-D439-4967-9371-DC3E0523EB19}.Release|x64.Build.0 = Release|x64
- {24D001B4-D439-4967-9371-DC3E0523EB19}.Release|x86.ActiveCfg = Release|Win32
- {24D001B4-D439-4967-9371-DC3E0523EB19}.Release|x86.Build.0 = Release|Win32
- {0BEF63A0-2801-4563-AB65-1E2FD881C3AF}.Debug|x64.ActiveCfg = Debug|x64
- {0BEF63A0-2801-4563-AB65-1E2FD881C3AF}.Debug|x64.Build.0 = Debug|x64
- {0BEF63A0-2801-4563-AB65-1E2FD881C3AF}.Debug|x86.ActiveCfg = Debug|Win32
- {0BEF63A0-2801-4563-AB65-1E2FD881C3AF}.Debug|x86.Build.0 = Debug|Win32
- {0BEF63A0-2801-4563-AB65-1E2FD881C3AF}.Release|x64.ActiveCfg = Release|x64
- {0BEF63A0-2801-4563-AB65-1E2FD881C3AF}.Release|x64.Build.0 = Release|x64
- {0BEF63A0-2801-4563-AB65-1E2FD881C3AF}.Release|x86.ActiveCfg = Release|Win32
- {0BEF63A0-2801-4563-AB65-1E2FD881C3AF}.Release|x86.Build.0 = Release|Win32
- {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Debug|x64.ActiveCfg = Debug|x64
- {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Debug|x64.Build.0 = Debug|x64
- {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Debug|x86.ActiveCfg = Debug|Win32
- {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Debug|x86.Build.0 = Debug|Win32
- {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Release|x64.ActiveCfg = Release|x64
- {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Release|x64.Build.0 = Release|x64
- {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Release|x86.ActiveCfg = Release|Win32
- {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Release|x86.Build.0 = Release|Win32
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
- GlobalSection(NestedProjects) = preSolution
- {4E578016-34BA-4A1E-B8EC-37A48780B6CA} = {05929EAE-4471-4E8E-A6F3-793A81623D7F}
- {E741ADED-7900-4E07-8DB0-D008C336C3FB} = {05929EAE-4471-4E8E-A6F3-793A81623D7F}
- {8616D6C9-C8DE-4C3F-AFC2-625636664C2B} = {05929EAE-4471-4E8E-A6F3-793A81623D7F}
- {8885C125-83FB-4F73-A93A-C712B1434D54} = {05929EAE-4471-4E8E-A6F3-793A81623D7F}
- {24D001B4-D439-4967-9371-DC3E0523EB19} = {05929EAE-4471-4E8E-A6F3-793A81623D7F}
- {0BEF63A0-2801-4563-AB65-1E2FD881C3AF} = {05929EAE-4471-4E8E-A6F3-793A81623D7F}
- {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C} = {05929EAE-4471-4E8E-A6F3-793A81623D7F}
- EndGlobalSection
- GlobalSection(ExtensibilityGlobals) = postSolution
- SolutionGuid = {ABDCFB40-D6B3-44A9-92B5-0D7AB38D9FB8}
- EndGlobalSection
-EndGlobal
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.31229.75
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nstool", "nstool\nstool.vcxproj", "{775EF5EB-CA49-4994-8AC4-47B4A5385266}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "deps", "deps", "{05929EAE-4471-4E8E-A6F3-793A81623D7F}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libfnd", "..\..\deps\libfnd\build\visualstudio\libfnd\libfnd.vcxproj", "{4E578016-34BA-4A1E-B8EC-37A48780B6CA}"
+ ProjectSection(ProjectDependencies) = postProject
+ {E741ADED-7900-4E07-8DB0-D008C336C3FB} = {E741ADED-7900-4E07-8DB0-D008C336C3FB}
+ {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C} = {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "liblz4", "..\..\deps\liblz4\build\visualstudio\liblz4\liblz4.vcxproj", "{E741ADED-7900-4E07-8DB0-D008C336C3FB}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libnintendo-es", "..\..\deps\libnintendo-es\build\visualstudio\libnintendo-es\libnintendo-es.vcxproj", "{8616D6C9-C8DE-4C3F-AFC2-625636664C2B}"
+ ProjectSection(ProjectDependencies) = postProject
+ {4E578016-34BA-4A1E-B8EC-37A48780B6CA} = {4E578016-34BA-4A1E-B8EC-37A48780B6CA}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libnintendo-hac", "..\..\deps\libnintendo-hac\build\visualstudio\libnintendo-hac\libnintendo-hac.vcxproj", "{8885C125-83FB-4F73-A93A-C712B1434D54}"
+ ProjectSection(ProjectDependencies) = postProject
+ {4E578016-34BA-4A1E-B8EC-37A48780B6CA} = {4E578016-34BA-4A1E-B8EC-37A48780B6CA}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libnintendo-hac-hb", "..\..\deps\libnintendo-hac-hb\build\visualstudio\libnintendo-hac-hb\libnintendo-hac-hb.vcxproj", "{24D001B4-D439-4967-9371-DC3E0523EB19}"
+ ProjectSection(ProjectDependencies) = postProject
+ {4E578016-34BA-4A1E-B8EC-37A48780B6CA} = {4E578016-34BA-4A1E-B8EC-37A48780B6CA}
+ {8885C125-83FB-4F73-A93A-C712B1434D54} = {8885C125-83FB-4F73-A93A-C712B1434D54}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libnintendo-pki", "..\..\deps\libnintendo-pki\build\visualstudio\libnintendo-pki\libnintendo-pki.vcxproj", "{0BEF63A0-2801-4563-AB65-1E2FD881C3AF}"
+ ProjectSection(ProjectDependencies) = postProject
+ {4E578016-34BA-4A1E-B8EC-37A48780B6CA} = {4E578016-34BA-4A1E-B8EC-37A48780B6CA}
+ EndProjectSection
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmbedtls", "..\..\deps\libmbedtls\build\visualstudio\libmbedtls\libmbedtls.vcxproj", "{7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libtoolchain", "..\..\deps\libtoolchain\build\visualstudio\libtoolchain\libtoolchain.vcxproj", "{E194E4B8-1482-40A2-901B-75D4387822E9}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libfmt", "..\..\deps\libfmt\build\visualstudio\libfmt\libfmt.vcxproj", "{F4B0540E-0AAE-4006-944B-356944EF61FA}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {775EF5EB-CA49-4994-8AC4-47B4A5385266}.Debug|x64.ActiveCfg = Debug|x64
+ {775EF5EB-CA49-4994-8AC4-47B4A5385266}.Debug|x64.Build.0 = Debug|x64
+ {775EF5EB-CA49-4994-8AC4-47B4A5385266}.Debug|x86.ActiveCfg = Debug|Win32
+ {775EF5EB-CA49-4994-8AC4-47B4A5385266}.Debug|x86.Build.0 = Debug|Win32
+ {775EF5EB-CA49-4994-8AC4-47B4A5385266}.Release|x64.ActiveCfg = Release|x64
+ {775EF5EB-CA49-4994-8AC4-47B4A5385266}.Release|x64.Build.0 = Release|x64
+ {775EF5EB-CA49-4994-8AC4-47B4A5385266}.Release|x86.ActiveCfg = Release|Win32
+ {775EF5EB-CA49-4994-8AC4-47B4A5385266}.Release|x86.Build.0 = Release|Win32
+ {4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Debug|x64.ActiveCfg = Debug|x64
+ {4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Debug|x64.Build.0 = Debug|x64
+ {4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Debug|x86.ActiveCfg = Debug|Win32
+ {4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Debug|x86.Build.0 = Debug|Win32
+ {4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Release|x64.ActiveCfg = Release|x64
+ {4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Release|x64.Build.0 = Release|x64
+ {4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Release|x86.ActiveCfg = Release|Win32
+ {4E578016-34BA-4A1E-B8EC-37A48780B6CA}.Release|x86.Build.0 = Release|Win32
+ {E741ADED-7900-4E07-8DB0-D008C336C3FB}.Debug|x64.ActiveCfg = Debug|x64
+ {E741ADED-7900-4E07-8DB0-D008C336C3FB}.Debug|x64.Build.0 = Debug|x64
+ {E741ADED-7900-4E07-8DB0-D008C336C3FB}.Debug|x86.ActiveCfg = Debug|Win32
+ {E741ADED-7900-4E07-8DB0-D008C336C3FB}.Debug|x86.Build.0 = Debug|Win32
+ {E741ADED-7900-4E07-8DB0-D008C336C3FB}.Release|x64.ActiveCfg = Release|x64
+ {E741ADED-7900-4E07-8DB0-D008C336C3FB}.Release|x64.Build.0 = Release|x64
+ {E741ADED-7900-4E07-8DB0-D008C336C3FB}.Release|x86.ActiveCfg = Release|Win32
+ {E741ADED-7900-4E07-8DB0-D008C336C3FB}.Release|x86.Build.0 = Release|Win32
+ {8616D6C9-C8DE-4C3F-AFC2-625636664C2B}.Debug|x64.ActiveCfg = Debug|x64
+ {8616D6C9-C8DE-4C3F-AFC2-625636664C2B}.Debug|x64.Build.0 = Debug|x64
+ {8616D6C9-C8DE-4C3F-AFC2-625636664C2B}.Debug|x86.ActiveCfg = Debug|Win32
+ {8616D6C9-C8DE-4C3F-AFC2-625636664C2B}.Debug|x86.Build.0 = Debug|Win32
+ {8616D6C9-C8DE-4C3F-AFC2-625636664C2B}.Release|x64.ActiveCfg = Release|x64
+ {8616D6C9-C8DE-4C3F-AFC2-625636664C2B}.Release|x64.Build.0 = Release|x64
+ {8616D6C9-C8DE-4C3F-AFC2-625636664C2B}.Release|x86.ActiveCfg = Release|Win32
+ {8616D6C9-C8DE-4C3F-AFC2-625636664C2B}.Release|x86.Build.0 = Release|Win32
+ {8885C125-83FB-4F73-A93A-C712B1434D54}.Debug|x64.ActiveCfg = Debug|x64
+ {8885C125-83FB-4F73-A93A-C712B1434D54}.Debug|x64.Build.0 = Debug|x64
+ {8885C125-83FB-4F73-A93A-C712B1434D54}.Debug|x86.ActiveCfg = Debug|Win32
+ {8885C125-83FB-4F73-A93A-C712B1434D54}.Debug|x86.Build.0 = Debug|Win32
+ {8885C125-83FB-4F73-A93A-C712B1434D54}.Release|x64.ActiveCfg = Release|x64
+ {8885C125-83FB-4F73-A93A-C712B1434D54}.Release|x64.Build.0 = Release|x64
+ {8885C125-83FB-4F73-A93A-C712B1434D54}.Release|x86.ActiveCfg = Release|Win32
+ {8885C125-83FB-4F73-A93A-C712B1434D54}.Release|x86.Build.0 = Release|Win32
+ {24D001B4-D439-4967-9371-DC3E0523EB19}.Debug|x64.ActiveCfg = Debug|x64
+ {24D001B4-D439-4967-9371-DC3E0523EB19}.Debug|x64.Build.0 = Debug|x64
+ {24D001B4-D439-4967-9371-DC3E0523EB19}.Debug|x86.ActiveCfg = Debug|Win32
+ {24D001B4-D439-4967-9371-DC3E0523EB19}.Debug|x86.Build.0 = Debug|Win32
+ {24D001B4-D439-4967-9371-DC3E0523EB19}.Release|x64.ActiveCfg = Release|x64
+ {24D001B4-D439-4967-9371-DC3E0523EB19}.Release|x64.Build.0 = Release|x64
+ {24D001B4-D439-4967-9371-DC3E0523EB19}.Release|x86.ActiveCfg = Release|Win32
+ {24D001B4-D439-4967-9371-DC3E0523EB19}.Release|x86.Build.0 = Release|Win32
+ {0BEF63A0-2801-4563-AB65-1E2FD881C3AF}.Debug|x64.ActiveCfg = Debug|x64
+ {0BEF63A0-2801-4563-AB65-1E2FD881C3AF}.Debug|x64.Build.0 = Debug|x64
+ {0BEF63A0-2801-4563-AB65-1E2FD881C3AF}.Debug|x86.ActiveCfg = Debug|Win32
+ {0BEF63A0-2801-4563-AB65-1E2FD881C3AF}.Debug|x86.Build.0 = Debug|Win32
+ {0BEF63A0-2801-4563-AB65-1E2FD881C3AF}.Release|x64.ActiveCfg = Release|x64
+ {0BEF63A0-2801-4563-AB65-1E2FD881C3AF}.Release|x64.Build.0 = Release|x64
+ {0BEF63A0-2801-4563-AB65-1E2FD881C3AF}.Release|x86.ActiveCfg = Release|Win32
+ {0BEF63A0-2801-4563-AB65-1E2FD881C3AF}.Release|x86.Build.0 = Release|Win32
+ {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Debug|x64.ActiveCfg = Debug|x64
+ {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Debug|x64.Build.0 = Debug|x64
+ {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Debug|x86.ActiveCfg = Debug|Win32
+ {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Debug|x86.Build.0 = Debug|Win32
+ {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Release|x64.ActiveCfg = Release|x64
+ {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Release|x64.Build.0 = Release|x64
+ {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Release|x86.ActiveCfg = Release|Win32
+ {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C}.Release|x86.Build.0 = Release|Win32
+ {E194E4B8-1482-40A2-901B-75D4387822E9}.Debug|x64.ActiveCfg = Debug|x64
+ {E194E4B8-1482-40A2-901B-75D4387822E9}.Debug|x64.Build.0 = Debug|x64
+ {E194E4B8-1482-40A2-901B-75D4387822E9}.Debug|x86.ActiveCfg = Debug|Win32
+ {E194E4B8-1482-40A2-901B-75D4387822E9}.Debug|x86.Build.0 = Debug|Win32
+ {E194E4B8-1482-40A2-901B-75D4387822E9}.Release|x64.ActiveCfg = Release|x64
+ {E194E4B8-1482-40A2-901B-75D4387822E9}.Release|x64.Build.0 = Release|x64
+ {E194E4B8-1482-40A2-901B-75D4387822E9}.Release|x86.ActiveCfg = Release|Win32
+ {E194E4B8-1482-40A2-901B-75D4387822E9}.Release|x86.Build.0 = Release|Win32
+ {F4B0540E-0AAE-4006-944B-356944EF61FA}.Debug|x64.ActiveCfg = Debug|x64
+ {F4B0540E-0AAE-4006-944B-356944EF61FA}.Debug|x64.Build.0 = Debug|x64
+ {F4B0540E-0AAE-4006-944B-356944EF61FA}.Debug|x86.ActiveCfg = Debug|Win32
+ {F4B0540E-0AAE-4006-944B-356944EF61FA}.Debug|x86.Build.0 = Debug|Win32
+ {F4B0540E-0AAE-4006-944B-356944EF61FA}.Release|x64.ActiveCfg = Release|x64
+ {F4B0540E-0AAE-4006-944B-356944EF61FA}.Release|x64.Build.0 = Release|x64
+ {F4B0540E-0AAE-4006-944B-356944EF61FA}.Release|x86.ActiveCfg = Release|Win32
+ {F4B0540E-0AAE-4006-944B-356944EF61FA}.Release|x86.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {4E578016-34BA-4A1E-B8EC-37A48780B6CA} = {05929EAE-4471-4E8E-A6F3-793A81623D7F}
+ {E741ADED-7900-4E07-8DB0-D008C336C3FB} = {05929EAE-4471-4E8E-A6F3-793A81623D7F}
+ {8616D6C9-C8DE-4C3F-AFC2-625636664C2B} = {05929EAE-4471-4E8E-A6F3-793A81623D7F}
+ {8885C125-83FB-4F73-A93A-C712B1434D54} = {05929EAE-4471-4E8E-A6F3-793A81623D7F}
+ {24D001B4-D439-4967-9371-DC3E0523EB19} = {05929EAE-4471-4E8E-A6F3-793A81623D7F}
+ {0BEF63A0-2801-4563-AB65-1E2FD881C3AF} = {05929EAE-4471-4E8E-A6F3-793A81623D7F}
+ {7A7C66F3-2B5B-4E23-85D8-2A74FEDAD92C} = {05929EAE-4471-4E8E-A6F3-793A81623D7F}
+ {E194E4B8-1482-40A2-901B-75D4387822E9} = {05929EAE-4471-4E8E-A6F3-793A81623D7F}
+ {F4B0540E-0AAE-4006-944B-356944EF61FA} = {05929EAE-4471-4E8E-A6F3-793A81623D7F}
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {ABDCFB40-D6B3-44A9-92B5-0D7AB38D9FB8}
+ EndGlobalSection
+EndGlobal
diff --git a/build/visualstudio/nstool/nstool.vcxproj b/build/visualstudio/nstool/nstool.vcxproj
index e07dcf0..e4db0c3 100644
--- a/build/visualstudio/nstool/nstool.vcxproj
+++ b/build/visualstudio/nstool/nstool.vcxproj
@@ -76,7 +76,8 @@
Disabled
true
true
- $(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include
+ $(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\libtoolchain\include;$(SolutionDir)..\..\deps\libfmt\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include
+ MultiThreadedDebug
@@ -85,7 +86,8 @@
Disabled
true
true
- $(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include
+ $(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\libtoolchain\include;$(SolutionDir)..\..\deps\libfmt\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include
+ MultiThreadedDebug
@@ -96,7 +98,8 @@
true
true
true
- $(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include
+ $(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\libtoolchain\include;$(SolutionDir)..\..\deps\libfmt\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include
+ MultiThreaded
true
@@ -111,7 +114,8 @@
true
true
true
- $(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include
+ $(ProjectDir)..\..\..\include;$(SolutionDir)..\..\deps\libfnd\include;$(SolutionDir)..\..\deps\libtoolchain\include;$(SolutionDir)..\..\deps\libfmt\include;$(SolutionDir)..\..\deps\libnintendo-es\include;$(SolutionDir)..\..\deps\libnintendo-pki\include;$(SolutionDir)..\..\deps\libnintendo-hac\include;$(SolutionDir)..\..\deps\libnintendo-hac-hb\include
+ MultiThreaded
true
@@ -119,76 +123,46 @@
-
+
+ {f4b0540e-0aae-4006-944b-356944ef61fa}
+
+
{4e578016-34ba-4a1e-b8ec-37a48780b6ca}
-
+
{e741aded-7900-4e07-8db0-d008c336c3fb}
-
+
{8616d6c9-c8de-4c3f-afc2-625636664c2b}
-
+
{24d001b4-d439-4967-9371-dc3e0523eb19}
-
+
{8885c125-83fb-4f73-a93a-c712b1434d54}
-
+
{0bef63a0-2801-4563-ab65-1e2fd881c3af}
-
+
{7a7c66f3-2b5b-4e23-85d8-2a74fedad92c}
+
+ {e194e4b8-1482-40a2-901b-75d4387822e9}
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
diff --git a/build/visualstudio/nstool/nstool.vcxproj.filters b/build/visualstudio/nstool/nstool.vcxproj.filters
index 82a1d26..44261e8 100644
--- a/build/visualstudio/nstool/nstool.vcxproj.filters
+++ b/build/visualstudio/nstool/nstool.vcxproj.filters
@@ -1,156 +1,48 @@
-
-
-
-
- {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
- cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
-
-
- {93995380-89BD-4b04-88EB-625FBE52EBFB}
- h;hh;hpp;hxx;hm;inl;inc;ipp;xsd
-
-
- {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
- rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
-
-
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;hm;inl;inc;ipp;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
\ No newline at end of file
diff --git a/deps/libfnd b/deps/libfnd
index b6a74e0..27705ae 160000
--- a/deps/libfnd
+++ b/deps/libfnd
@@ -1 +1 @@
-Subproject commit b6a74e03d2c3fb272e900f32554b2cc0d18e7d26
+Subproject commit 27705aeb2394dbe21f580cc742792537e3a0cbed
diff --git a/deps/liblz4 b/deps/liblz4
index 298f2ec..555e430 160000
--- a/deps/liblz4
+++ b/deps/liblz4
@@ -1 +1 @@
-Subproject commit 298f2ecfa6c966472eda9a4f2b93121a060d1867
+Subproject commit 555e43086b348a640179ddf2ecd86d8503c12b85
diff --git a/deps/libmbedtls b/deps/libmbedtls
index 30ef43f..597bb57 160000
--- a/deps/libmbedtls
+++ b/deps/libmbedtls
@@ -1 +1 @@
-Subproject commit 30ef43f1d6ef00d9d94e19270ae8b646e821ecc7
+Subproject commit 597bb57271177d776682104dc7c8ea8b62670d1d
diff --git a/deps/libnintendo-es b/deps/libnintendo-es
index f96b4f9..7572682 160000
--- a/deps/libnintendo-es
+++ b/deps/libnintendo-es
@@ -1 +1 @@
-Subproject commit f96b4f96552da4258f4212427137d6ff40c45392
+Subproject commit 7572682f1fa819fd7ef1a4b639cb14fa67128f0d
diff --git a/deps/libnintendo-hac b/deps/libnintendo-hac
index 33cfca7..02c6703 160000
--- a/deps/libnintendo-hac
+++ b/deps/libnintendo-hac
@@ -1 +1 @@
-Subproject commit 33cfca799ccdcc4c34f73696da9fe31f104dd23f
+Subproject commit 02c6703c9720d98197b3bb697fdab12008a9829e
diff --git a/deps/libnintendo-hac-hb b/deps/libnintendo-hac-hb
index db9af77..e52fb0e 160000
--- a/deps/libnintendo-hac-hb
+++ b/deps/libnintendo-hac-hb
@@ -1 +1 @@
-Subproject commit db9af77ec23d170ec703a13cd5cd9b67cd47d3a3
+Subproject commit e52fb0e1ebc558f68bb438bd278b05704c5aaeb3
diff --git a/deps/libnintendo-pki b/deps/libnintendo-pki
index 040405b..b8a1066 160000
--- a/deps/libnintendo-pki
+++ b/deps/libnintendo-pki
@@ -1 +1 @@
-Subproject commit 040405bc6f9936e4e61c80b5ab384b3d95c395cd
+Subproject commit b8a10663a99f7b5fedf11139549e8bf1b289c0e0
diff --git a/deps/libtoolchain b/deps/libtoolchain
index 0be82cf..7948f58 160000
--- a/deps/libtoolchain
+++ b/deps/libtoolchain
@@ -1 +1 @@
-Subproject commit 0be82cf582b4af7539f57a261e17e5769f4f8cfa
+Subproject commit 7948f581d32ebbfdf5a872b96c37938690f665e7
diff --git a/makefile b/makefile
index 4f4ca61..f8c302a 100644
--- a/makefile
+++ b/makefile
@@ -1,6 +1,6 @@
# C++/C Recursive Project Makefile
# (c) Jack
-# Version 3
+# Version 4
# Project Name
PROJECT_NAME = nstool
@@ -13,7 +13,7 @@ PROJECT_SRC_SUBDIRS = $(PROJECT_SRC_PATH)
#PROJECT_TESTSRC_PATH = test
#PROJECT_TESTSRC_SUBDIRS = $(PROJECT_TESTSRC_PATH)
PROJECT_BIN_PATH = bin
-#PROJECT_DOCS_PATH = docs
+#PROJECT_DOCS_PATH = docs
#PROJECT_DOXYFILE_PATH = Doxyfile
# Determine if the root makefile has been established, and if not establish this makefile as the root makefile
@@ -31,8 +31,8 @@ PROJECT_SONAME = $(PROJECT_NAME).so.$(PROJECT_SO_VER_MAJOR)
PROJECT_SO_FILENAME = $(PROJECT_SONAME).$(PROJECT_SO_VER_MINOR).$(PROJECT_SO_VER_PATCH)
# Project Dependencies
-PROJECT_DEPEND_LOCAL = nintendo-hac-hb nintendo-hac nintendo-es nintendo-pki fnd mbedtls lz4
-PROJECT_DEPEND_EXTERNAL =
+PROJECT_DEPEND = mbedtls lz4 toolchain fmt nintendo-hac nintendo-hac-hb nintendo-es nintendo-pki
+PROJECT_DEPEND_LOCAL_DIR = libmbedtls liblz4 libtoolchain libfmt libnintendo-hac libnintendo-hac-hb libnintendo-es libnintendo-pki
# Generate compiler flags for including project include path
ifneq ($(PROJECT_INCLUDE_PATH),)
@@ -40,14 +40,14 @@ ifneq ($(PROJECT_INCLUDE_PATH),)
endif
# Generate compiler flags for local included dependencies
-ifneq ($(PROJECT_DEPEND_LOCAL),)
- LIB += $(foreach dep,$(PROJECT_DEPEND_LOCAL), -L"$(ROOT_PROJECT_DEPENDENCY_PATH)/lib$(dep)/bin" -l$(dep))
- INC += $(foreach dep,$(PROJECT_DEPEND_LOCAL), -I"$(ROOT_PROJECT_DEPENDENCY_PATH)/lib$(dep)/include")
+ifneq ($(PROJECT_DEPEND_LOCAL_DIR),)
+ LIB += $(foreach dep,$(PROJECT_DEPEND_LOCAL_DIR), -L"$(ROOT_PROJECT_DEPENDENCY_PATH)/$(dep)/bin")
+ INC += $(foreach dep,$(PROJECT_DEPEND_LOCAL_DIR), -I"$(ROOT_PROJECT_DEPENDENCY_PATH)/$(dep)/include")
endif
# Generate compiler flags for external dependencies
-ifneq ($(PROJECT_DEPEND_EXTERNAL),)
- LIB += $(foreach dep,$(PROJECT_DEPEND_EXTERNAL), -l$(dep))
+ifneq ($(PROJECT_DEPEND),)
+ LIB += $(foreach dep,$(PROJECT_DEPEND), -l$(dep))
endif
# Detect Platform
@@ -170,8 +170,8 @@ endif
# Dependencies
.PHONY: deps
deps:
- @$(foreach lib,$(PROJECT_DEPEND_LOCAL), cd "$(ROOT_PROJECT_DEPENDENCY_PATH)/lib$(lib)" && $(MAKE) static_lib && cd "$(PROJECT_PATH)";)
+ @$(foreach lib,$(PROJECT_DEPEND_LOCAL_DIR), cd "$(ROOT_PROJECT_DEPENDENCY_PATH)/$(lib)" && $(MAKE) static_lib && cd "$(PROJECT_PATH)";)
.PHONY: clean_deps
clean_deps:
- @$(foreach lib,$(PROJECT_DEPEND_LOCAL), cd "$(ROOT_PROJECT_DEPENDENCY_PATH)/lib$(lib)" && $(MAKE) clean && cd "$(PROJECT_PATH)";)
\ No newline at end of file
+ @$(foreach lib,$(PROJECT_DEPEND_LOCAL_DIR), cd "$(ROOT_PROJECT_DEPENDENCY_PATH)/$(lib)" && $(MAKE) clean && cd "$(PROJECT_PATH)";)
\ No newline at end of file
diff --git a/src/AssetProcess.cpp b/src/AssetProcess.cpp
index fcca0ce..1a9bf53 100644
--- a/src/AssetProcess.cpp
+++ b/src/AssetProcess.cpp
@@ -1,115 +1,103 @@
-#include
-#include
-#include
-#include
-#include
#include "AssetProcess.h"
+#include "utils.h"
-AssetProcess::AssetProcess() :
+nstool::AssetProcess::AssetProcess() :
mFile(),
- mCliOutputMode(_BIT(OUTPUT_BASIC)),
+ mCliOutputMode(true, false, false, false),
mVerify(false)
{
}
-void AssetProcess::process()
+void nstool::AssetProcess::process()
{
importHeader();
- if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
+ if (mCliOutputMode.show_basic_info)
displayHeader();
processSections();
-}
+}
-void AssetProcess::setInputFile(const fnd::SharedPtr& file)
+void nstool::AssetProcess::setInputFile(const std::shared_ptr& file)
{
mFile = file;
}
-void AssetProcess::setCliOutputMode(CliOutputMode type)
+void nstool::AssetProcess::setCliOutputMode(CliOutputMode type)
{
mCliOutputMode = type;
}
-void AssetProcess::setVerifyMode(bool verify)
+void nstool::AssetProcess::setVerifyMode(bool verify)
{
mVerify = verify;
}
-void AssetProcess::setListFs(bool list)
+void nstool::AssetProcess::setListFs(bool list)
{
mRomfs.setListFs(list);
}
-void AssetProcess::setIconExtractPath(const std::string& path)
+void nstool::AssetProcess::setIconExtractPath(const std::string& path)
{
mIconExtractPath = path;
}
-void AssetProcess::setNacpExtractPath(const std::string& path)
+void nstool::AssetProcess::setNacpExtractPath(const std::string& path)
{
mNacpExtractPath = path;
}
-void AssetProcess::setRomfsExtractPath(const std::string& path)
+void nstool::AssetProcess::setRomfsExtractPath(const std::string& path)
{
mRomfs.setExtractPath(path);
}
-void AssetProcess::importHeader()
+void nstool::AssetProcess::importHeader()
{
- fnd::Vec scratch;
+ tc::ByteData scratch;
- if (*mFile == nullptr)
+ if (mFile == nullptr)
{
- throw fnd::Exception(kModuleName, "No file reader set.");
+ throw tc::Exception(kModuleName, "No file reader set.");
}
- if ((*mFile)->size() < sizeof(nn::hac::sAssetHeader))
+ size_t file_size = tc::io::IOUtil::castInt64ToSize(mFile->length());
+
+ if (file_size < sizeof(nn::hac::sAssetHeader))
{
- throw fnd::Exception(kModuleName, "Corrupt ASET: file too small");
+ throw tc::Exception(kModuleName, "Corrupt ASET: file too small");
}
scratch.alloc(sizeof(nn::hac::sAssetHeader));
- (*mFile)->read(scratch.data(), 0, scratch.size());
+ mFile->read(scratch.data(), 0, scratch.size());
mHdr.fromBytes(scratch.data(), scratch.size());
}
-void AssetProcess::processSections()
+void nstool::AssetProcess::processSections()
{
- if (mHdr.getIconInfo().size > 0 && mIconExtractPath.isSet)
+ size_t file_size = tc::io::IOUtil::castInt64ToSize(mFile->length());
+
+ 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");
+ if ((mHdr.getIconInfo().size + mHdr.getIconInfo().offset) > file_size)
+ throw tc::Exception(kModuleName, "ASET geometry for icon beyond file size");
- fnd::SimpleFile outfile(mIconExtractPath.var, fnd::SimpleFile::Create);
- fnd::Vec cache;
-
- cache.alloc(mHdr.getIconInfo().size);
- (*mFile)->read(cache.data(), mHdr.getIconInfo().offset, cache.size());
- outfile.write(cache.data(), cache.size());
- outfile.close();
+ writeSubStreamToFile(mFile, tc::io::IOUtil::castSizeToInt64(mHdr.getIconInfo().offset), tc::io::IOUtil::castSizeToInt64(mHdr.getIconInfo().size), mIconExtractPath.get());
}
if (mHdr.getNacpInfo().size > 0)
{
- if ((mHdr.getNacpInfo().size + mHdr.getNacpInfo().offset) > (*mFile)->size())
- throw fnd::Exception(kModuleName, "ASET geometry for nacp beyond file size");
+ if ((mHdr.getNacpInfo().size + mHdr.getNacpInfo().offset) > file_size)
+ throw tc::Exception(kModuleName, "ASET geometry for nacp beyond file size");
- if (mNacpExtractPath.isSet)
+ if (mNacpExtractPath.isSet())
{
- fnd::SimpleFile outfile(mNacpExtractPath.var, fnd::SimpleFile::Create);
- fnd::Vec cache;
-
- cache.alloc(mHdr.getNacpInfo().size);
- (*mFile)->read(cache.data(), mHdr.getNacpInfo().offset, cache.size());
- outfile.write(cache.data(), cache.size());
- outfile.close();
+ writeSubStreamToFile(mFile, tc::io::IOUtil::castSizeToInt64(mHdr.getNacpInfo().offset), tc::io::IOUtil::castSizeToInt64(mHdr.getNacpInfo().size), mNacpExtractPath.get());
}
- mNacp.setInputFile(new fnd::OffsetAdjustedIFile(mFile, mHdr.getNacpInfo().offset, mHdr.getNacpInfo().size));
+ mNacp.setInputFile(std::make_shared(mFile, tc::io::IOUtil::castSizeToInt64(mHdr.getNacpInfo().offset), tc::io::IOUtil::castSizeToInt64(mHdr.getNacpInfo().size)));
mNacp.setCliOutputMode(mCliOutputMode);
mNacp.setVerifyMode(mVerify);
@@ -118,10 +106,10 @@ void AssetProcess::processSections()
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");
+ if ((mHdr.getRomfsInfo().size + mHdr.getRomfsInfo().offset) > file_size)
+ throw tc::Exception(kModuleName, "ASET geometry for romfs beyond file size");
- mRomfs.setInputFile(new fnd::OffsetAdjustedIFile(mFile, mHdr.getRomfsInfo().offset, mHdr.getRomfsInfo().size));
+ mRomfs.setInputFile(std::make_shared(mFile, tc::io::IOUtil::castSizeToInt64(mHdr.getRomfsInfo().offset), tc::io::IOUtil::castSizeToInt64(mHdr.getRomfsInfo().size)));
mRomfs.setCliOutputMode(mCliOutputMode);
mRomfs.setVerifyMode(mVerify);
@@ -129,9 +117,9 @@ void AssetProcess::processSections()
}
}
-void AssetProcess::displayHeader()
+void nstool::AssetProcess::displayHeader()
{
- if (_HAS_BIT(mCliOutputMode, OUTPUT_LAYOUT))
+ if (mCliOutputMode.show_layout)
{
std::cout << "[ASET Header]" << std::endl;
std::cout << " Icon:" << std::endl;
diff --git a/src/AssetProcess.h b/src/AssetProcess.h
index 7364d15..262de5a 100644
--- a/src/AssetProcess.h
+++ b/src/AssetProcess.h
@@ -1,13 +1,11 @@
#pragma once
-#include
-#include
-#include
-#include
-#include
+#include "types.h"
#include "NacpProcess.h"
#include "RomfsProcess.h"
-#include "common.h"
+#include
+
+namespace nstool {
class AssetProcess
{
@@ -16,7 +14,7 @@ public:
void process();
- void setInputFile(const fnd::SharedPtr& file);
+ void setInputFile(const std::shared_ptr& file);
void setCliOutputMode(CliOutputMode type);
void setVerifyMode(bool verify);
@@ -30,12 +28,12 @@ public:
private:
const std::string kModuleName = "AssetProcess";
- fnd::SharedPtr mFile;
+ std::shared_ptr mFile;
CliOutputMode mCliOutputMode;
bool mVerify;
- sOptional mIconExtractPath;
- sOptional mNacpExtractPath;
+ tc::Optional mIconExtractPath;
+ tc::Optional mNacpExtractPath;
nn::hac::AssetHeader mHdr;
NacpProcess mNacp;
@@ -44,4 +42,6 @@ private:
void importHeader();
void processSections();
void displayHeader();
-};
\ No newline at end of file
+};
+
+}
\ No newline at end of file
diff --git a/src/CnmtProcess.cpp b/src/CnmtProcess.cpp
index 8e8c4ed..5849c16 100644
--- a/src/CnmtProcess.cpp
+++ b/src/CnmtProcess.cpp
@@ -3,53 +3,50 @@
#include
#include
-#include
-#include
-
#include
-CnmtProcess::CnmtProcess() :
+nstool::CnmtProcess::CnmtProcess() :
mFile(),
- mCliOutputMode(_BIT(OUTPUT_BASIC)),
+ mCliOutputMode(true, false, false, false),
mVerify(false)
{
}
-void CnmtProcess::process()
+void nstool::CnmtProcess::process()
{
importCnmt();
- if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
+ if (mCliOutputMode.show_basic_info)
displayCnmt();
}
-void CnmtProcess::setInputFile(const fnd::SharedPtr& file)
+void nstool::CnmtProcess::setInputFile(const std::shared_ptr& file)
{
mFile = file;
}
-void CnmtProcess::setCliOutputMode(CliOutputMode type)
+void nstool::CnmtProcess::setCliOutputMode(CliOutputMode type)
{
mCliOutputMode = type;
}
-void CnmtProcess::setVerifyMode(bool verify)
+void nstool::CnmtProcess::setVerifyMode(bool verify)
{
mVerify = verify;
}
-const nn::hac::ContentMeta& CnmtProcess::getContentMeta() const
+const nn::hac::ContentMeta& nstool::CnmtProcess::getContentMeta() const
{
return mCnmt;
}
-void CnmtProcess::importCnmt()
+void nstool::CnmtProcess::importCnmt()
{
- fnd::Vec scratch;
+ tc::ByteData scratch;
if (*mFile == nullptr)
{
- throw fnd::Exception(kModuleName, "No file reader set.");
+ throw tc::Exception(kModuleName, "No file reader set.");
}
scratch.alloc((*mFile)->size());
@@ -58,7 +55,7 @@ void CnmtProcess::importCnmt()
mCnmt.fromBytes(scratch.data(), scratch.size());
}
-void CnmtProcess::displayCnmt()
+void nstool::CnmtProcess::displayCnmt()
{
std::cout << "[ContentMeta]" << std::endl;
std::cout << " TitleId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mCnmt.getTitleId() << std::endl;
@@ -178,7 +175,7 @@ void CnmtProcess::displayCnmt()
std::cout << " Digest: " << fnd::SimpleTextOutput::arrayToString(mCnmt.getDigest().data(), mCnmt.getDigest().size(), false, "") << std::endl;
}
-void CnmtProcess::displayContentMetaInfo(const nn::hac::ContentMetaInfo& content_meta_info, const std::string& prefix)
+void nstool::CnmtProcess::displayContentMetaInfo(const nn::hac::ContentMetaInfo& content_meta_info, const std::string& prefix)
{
std::cout << prefix << "Id: 0x" << std::hex << std::setw(16) << std::setfill('0') << content_meta_info.getTitleId() << std::endl;
std::cout << prefix << "Version: " << nn::hac::ContentMetaUtil::getVersionAsString(content_meta_info.getTitleVersion()) << " (v" << std::dec << content_meta_info.getTitleVersion() << ")"<< std::endl;
@@ -210,7 +207,7 @@ void CnmtProcess::displayContentMetaInfo(const nn::hac::ContentMetaInfo& content
}
}
-void CnmtProcess::displayContentMetaInfoList(const std::vector& content_meta_info_list, const std::string& prefix)
+void nstool::CnmtProcess::displayContentMetaInfoList(const std::vector& content_meta_info_list, const std::string& prefix)
{
for (size_t i = 0; i < content_meta_info_list.size(); i++)
{
diff --git a/src/CnmtProcess.h b/src/CnmtProcess.h
index 2353f68..99c9462 100644
--- a/src/CnmtProcess.h
+++ b/src/CnmtProcess.h
@@ -1,11 +1,9 @@
#pragma once
-#include
-#include
-#include
-#include
+#include "types.h"
+
#include
-#include "common.h"
+namespace nstool {
class CnmtProcess
{
@@ -14,7 +12,7 @@ public:
void process();
- void setInputFile(const fnd::SharedPtr& file);
+ void setInputFile(const std::shared_ptr& file);
void setCliOutputMode(CliOutputMode type);
void setVerifyMode(bool verify);
@@ -23,7 +21,7 @@ public:
private:
const std::string kModuleName = "CnmtProcess";
- fnd::SharedPtr mFile;
+ std::shared_ptr mFile;
CliOutputMode mCliOutputMode;
bool mVerify;
@@ -34,4 +32,6 @@ private:
void displayContentMetaInfo(const nn::hac::ContentMetaInfo& content_meta_info, const std::string& prefix);
void displayContentMetaInfoList(const std::vector& content_meta_info_list, const std::string& prefix);
-};
\ No newline at end of file
+};
+
+}
\ No newline at end of file
diff --git a/src/CompressedArchiveIFile.cpp b/src/CompressedArchiveIFile.cpp
deleted file mode 100644
index 4467bfc..0000000
--- a/src/CompressedArchiveIFile.cpp
+++ /dev/null
@@ -1,195 +0,0 @@
-#include "CompressedArchiveIFile.h"
-#include
-
-#include
-
-CompressedArchiveIFile::CompressedArchiveIFile(const fnd::SharedPtr& base_file, size_t compression_meta_offset) :
- mFile(base_file),
- mCompEntries(),
- mLogicalFileSize(0),
- mCacheCapacity(nn::hac::compression::kRomfsBlockSize),
- mCurrentCacheDataSize(0),
- mCache(std::shared_ptr(new byte_t[mCacheCapacity])),
- mScratch(std::shared_ptr(new byte_t[mCacheCapacity]))
-{
- // determine and check the compression metadata size
- size_t compression_meta_size = (*mFile)->size() - compression_meta_offset;
- if (compression_meta_size % sizeof(nn::hac::sCompressionEntry))
- {
- fnd::Exception(kModuleName, "Invalid compression meta size");
- }
-
- // import raw metadata
- std::shared_ptr entries_raw = std::shared_ptr(new byte_t[compression_meta_size]);
- (*mFile)->read(entries_raw.get(), compression_meta_offset, compression_meta_size);
-
- // process metadata entries
- nn::hac::sCompressionEntry* entries = (nn::hac::sCompressionEntry*)entries_raw.get();
- for (size_t idx = 0, num = compression_meta_size / sizeof(nn::hac::sCompressionEntry); idx < num; idx++)
- {
- if (idx == 0)
- {
- if (entries[idx].physical_offset.get() != 0x0)
- throw fnd::Exception(kModuleName, "Entry 0 had a non-zero physical offset");
- if (entries[idx].virtual_offset.get() != 0x0)
- throw fnd::Exception(kModuleName, "Entry 0 had a non-zero virtual offset");
- }
- else
- {
- if (entries[idx].physical_offset.get() != align(entries[idx - 1].physical_offset.get() + entries[idx - 1].physical_size.get(), nn::hac::compression::kRomfsBlockAlign))
- throw fnd::Exception(kModuleName, "Entry was not physically aligned with previous entry");
- if (entries[idx].virtual_offset.get() <= entries[idx - 1].virtual_offset.get())
- throw fnd::Exception(kModuleName, "Entry was not virtually aligned with previous entry");
-
- // set previous entry virtual_size = this->virtual_offset - prev->virtual_offset;
- mCompEntries[mCompEntries.size() - 1].virtual_size = uint32_t(entries[idx].virtual_offset.get() - mCompEntries[mCompEntries.size() - 1].virtual_offset);
- }
-
- if (entries[idx].physical_size.get() > nn::hac::compression::kRomfsBlockSize)
- throw fnd::Exception(kModuleName, "Entry physical size was too large");
-
- switch ((nn::hac::compression::CompressionType)entries[idx].compression_type)
- {
- case (nn::hac::compression::CompressionType::None):
- case (nn::hac::compression::CompressionType::Lz4):
- break;
- default:
- throw fnd::Exception(kModuleName, "Unsupported CompressionType");
- }
-
- mCompEntries.push_back({(nn::hac::compression::CompressionType)entries[idx].compression_type, entries[idx].virtual_offset.get(), 0, entries[idx].physical_offset.get(), entries[idx].physical_size.get()});
- }
-
- // determine logical file size and final entry size
- importEntryDataToCache(mCompEntries.size() - 1);
- mCompEntries[mCurrentEntryIndex].virtual_size = mCurrentCacheDataSize;
- mLogicalFileSize = mCompEntries[mCurrentEntryIndex].virtual_offset + mCompEntries[mCurrentEntryIndex].virtual_size;
-
- /*
- for (auto itr = mCompEntries.begin(); itr != mCompEntries.end(); itr++)
- {
- std::cout << "entry " << std::endl;
- std::cout << " type: " << (uint32_t)itr->compression_type << std::endl;
- std::cout << " phys_addr: 0x" << std::hex << itr->physical_offset << std::endl;
- std::cout << " phys_size: 0x" << std::hex << itr->physical_size << std::endl;
- std::cout << " virt_addr: 0x" << std::hex << itr->virtual_offset << std::endl;
- std::cout << " virt_size: 0x" << std::hex << itr->virtual_size << std::endl;
- }
-
- std::cout << "logical size: 0x" << std::hex << mLogicalFileSize << std::endl;
- */
-}
-
-size_t CompressedArchiveIFile::size()
-{
- return mLogicalFileSize;
-}
-
-void CompressedArchiveIFile::seek(size_t offset)
-{
- mLogicalOffset = std::min(offset, mLogicalFileSize);
-}
-
-void CompressedArchiveIFile::read(byte_t* out, size_t len)
-{
- // limit len to the end of the logical file
- len = std::min(len, mLogicalFileSize - mLogicalOffset);
-
- for (size_t pos = 0, entry_index = getEntryIndexForLogicalOffset(mLogicalOffset); pos < len; entry_index++)
- {
- // importing entry into cache (this does nothing if the entry is already imported)
- importEntryDataToCache(entry_index);
-
- // write padding if required
- if (mCompEntries[entry_index].virtual_size > mCurrentCacheDataSize)
- {
- memset(mCache.get() + mCurrentCacheDataSize, 0, mCompEntries[entry_index].virtual_size - mCurrentCacheDataSize);
- }
-
- // determine subset of cache to copy out
- size_t read_offset = mLogicalOffset - (size_t)mCompEntries[entry_index].virtual_offset;
- size_t read_size = std::min(len, (size_t)mCompEntries[entry_index].virtual_size - read_offset);
-
- memcpy(out + pos, mCache.get() + read_offset, read_size);
-
- // update position/logical offset
- pos += read_size;
- mLogicalOffset += read_size;
- }
-}
-
-void CompressedArchiveIFile::read(byte_t* out, size_t offset, size_t len)
-{
- seek(offset);
- read(out, len);
-}
-
-void CompressedArchiveIFile::write(const byte_t* out, size_t len)
-{
- throw fnd::Exception(kModuleName, "write() not supported");
-}
-
-void CompressedArchiveIFile::write(const byte_t* out, size_t offset, size_t len)
-{
- throw fnd::Exception(kModuleName, "write() not supported");
-}
-
-void CompressedArchiveIFile::importEntryDataToCache(size_t entry_index)
-{
- // return if entry already imported
- if (mCurrentEntryIndex == entry_index && mCurrentCacheDataSize != 0)
- return;
-
- // save index
- mCurrentEntryIndex = entry_index;
-
- // reference entry
- CompressionEntry& entry = mCompEntries[mCurrentEntryIndex];
-
- if (entry.compression_type == nn::hac::compression::CompressionType::None)
- {
- (*mFile)->read(mCache.get(), entry.physical_offset, entry.physical_size);
- mCurrentCacheDataSize = entry.physical_size;
- }
- else if (entry.compression_type == nn::hac::compression::CompressionType::Lz4)
- {
- (*mFile)->read(mScratch.get(), entry.physical_offset, entry.physical_size);
-
- mCurrentCacheDataSize = 0;
- fnd::lz4::decompressData(mScratch.get(), entry.physical_size, mCache.get(), uint32_t(mCacheCapacity), mCurrentCacheDataSize);
-
- if (mCurrentCacheDataSize == 0)
- {
- throw fnd::Exception(kModuleName, "Decompression of final block failed");
- }
- }
-}
-
-size_t CompressedArchiveIFile::getEntryIndexForLogicalOffset(size_t logical_offset)
-{
- // rule out bad offset
- if (logical_offset > mLogicalFileSize)
- throw fnd::Exception(kModuleName, "illegal logical offset");
-
- size_t entry_index = 0;
-
- // try the current comp entry
- if (mCompEntries[mCurrentEntryIndex].virtual_offset <= logical_offset && \
- mCompEntries[mCurrentEntryIndex].virtual_offset + mCompEntries[mCurrentEntryIndex].virtual_size >= logical_offset)
- {
- entry_index = mCurrentEntryIndex;
- }
- else
- {
- for (size_t index = 0; index < mCompEntries.size(); index++)
- {
- if (mCompEntries[index].virtual_offset <= logical_offset && \
- mCompEntries[index].virtual_offset + mCompEntries[index].virtual_size >= logical_offset)
- {
- entry_index = index;
- }
- }
- }
-
- return entry_index;
-}
\ No newline at end of file
diff --git a/src/CompressedArchiveIFile.h b/src/CompressedArchiveIFile.h
deleted file mode 100644
index 795bb49..0000000
--- a/src/CompressedArchiveIFile.h
+++ /dev/null
@@ -1,51 +0,0 @@
-#pragma once
-#include
-#include
-#include
-#include
-#include
-#include
-
-class CompressedArchiveIFile : public fnd::IFile
-{
-public:
- CompressedArchiveIFile(const fnd::SharedPtr& file, size_t compression_meta_offset);
-
- size_t size();
- void seek(size_t offset);
- void read(byte_t* out, size_t len);
- void read(byte_t* out, size_t offset, size_t len);
- void write(const byte_t* out, size_t len);
- void write(const byte_t* out, size_t offset, size_t len);
-private:
- const std::string kModuleName = "CompressedArchiveIFile";
- std::stringstream mErrorSs;
-
- struct CompressionEntry
- {
- nn::hac::compression::CompressionType compression_type;
- uint64_t virtual_offset;
- uint32_t virtual_size;
- uint64_t physical_offset;
- uint32_t physical_size;
- };
-
- // raw data
- fnd::SharedPtr mFile;
-
- // compression metadata
- std::vector mCompEntries;
- size_t mLogicalFileSize;
- size_t mLogicalOffset;
-
- // cached decompressed entry
- size_t mCacheCapacity; // capacity
- size_t mCurrentEntryIndex; // index of entry currently associated with the cache
- uint32_t mCurrentCacheDataSize; // size of data currently in cache
- std::shared_ptr mCache; // where decompressed data resides
- std::shared_ptr mScratch; // same size as cache, but is used for storing data pre-compression
-
- // this will import entry to cache
- void importEntryDataToCache(size_t entry_index);
- size_t getEntryIndexForLogicalOffset(size_t logical_offset);
-};
\ No newline at end of file
diff --git a/src/ElfSymbolParser.cpp b/src/ElfSymbolParser.cpp
index b57c9f2..8bb66dd 100644
--- a/src/ElfSymbolParser.cpp
+++ b/src/ElfSymbolParser.cpp
@@ -1,26 +1,26 @@
#include "ElfSymbolParser.h"
-ElfSymbolParser::ElfSymbolParser()
+nstool::ElfSymbolParser::ElfSymbolParser()
{
mSymbolList.clear();
}
-void ElfSymbolParser::operator=(const ElfSymbolParser& other)
+void nstool::ElfSymbolParser::operator=(const ElfSymbolParser& other)
{
mSymbolList = other.mSymbolList;
}
-bool ElfSymbolParser::operator==(const ElfSymbolParser& other) const
+bool nstool::ElfSymbolParser::operator==(const ElfSymbolParser& other) const
{
return mSymbolList == other.mSymbolList;
}
-bool ElfSymbolParser::operator!=(const ElfSymbolParser& other) const
+bool nstool::ElfSymbolParser::operator!=(const ElfSymbolParser& other) const
{
return !(*this == other);
}
-void ElfSymbolParser::parseData(const byte_t *dyn_sym, size_t dyn_sym_size, const byte_t *dyn_str, size_t dyn_str_size, bool is64Bit)
+void nstool::ElfSymbolParser::parseData(const byte_t *dyn_sym, size_t dyn_sym_size, const byte_t *dyn_str, size_t dyn_str_size, bool is64Bit)
{
size_t dynSymSize = is64Bit ? sizeof(fnd::Elf64_Sym) : sizeof(fnd::Elf32_Sym);
@@ -46,17 +46,17 @@ void ElfSymbolParser::parseData(const byte_t *dyn_sym, size_t dyn_sym_size, cons
if (name_pos >= dyn_str_size)
{
- throw fnd::Exception(kModuleName, "Out of bounds symbol name offset");
+ throw tc::Exception(kModuleName, "Out of bounds symbol name offset");
}
//for (; dyn_str[name_pos] == 0x00 && name_pos < dyn_str_size; name_pos++);
symbol.name = std::string((char*)&dyn_str[name_pos]);
- mSymbolList.addElement(symbol);
+ mSymbolList.push_back(symbol);
}
}
-const fnd::List& ElfSymbolParser::getSymbolList() const
+const std::vector& nstool::ElfSymbolParser::getSymbolList() const
{
return mSymbolList;
}
\ No newline at end of file
diff --git a/src/ElfSymbolParser.h b/src/ElfSymbolParser.h
index e06bdf3..ea8bedb 100644
--- a/src/ElfSymbolParser.h
+++ b/src/ElfSymbolParser.h
@@ -1,7 +1,8 @@
#pragma once
-#include
-#include
-#include
+#include "types.h"
+#include "elf.h"
+
+namespace nstool {
class ElfSymbolParser
{
@@ -40,10 +41,12 @@ public:
void parseData(const byte_t *dyn_sym, size_t dyn_sym_size, const byte_t *dyn_str, size_t dyn_str_size, bool is64Bit);
- const fnd::List& getSymbolList() const;
+ const std::vector& getSymbolList() const;
private:
const std::string kModuleName = "ElfSymbolParser";
// data
- fnd::List mSymbolList;
-};
\ No newline at end of file
+ std::vector mSymbolList;
+};
+
+}
\ No newline at end of file
diff --git a/src/EsTikProcess.cpp b/src/EsTikProcess.cpp
index 41da96c..561b6fb 100644
--- a/src/EsTikProcess.cpp
+++ b/src/EsTikProcess.cpp
@@ -8,57 +8,57 @@
-EsTikProcess::EsTikProcess() :
+nstool::EsTikProcess::EsTikProcess() :
mFile(),
- mCliOutputMode(_BIT(OUTPUT_BASIC)),
+ mCliOutputMode(true, false, false, false),
mVerify(false)
{
}
-void EsTikProcess::process()
+void nstool::EsTikProcess::process()
{
importTicket();
if (mVerify)
verifyTicket();
- if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
+ if (mCliOutputMode.show_basic_info)
displayTicket();
}
-void EsTikProcess::setInputFile(const fnd::SharedPtr& file)
+void nstool::EsTikProcess::setInputFile(const std::shared_ptr& file)
{
mFile = file;
}
-void EsTikProcess::setKeyCfg(const KeyConfiguration& keycfg)
+void nstool::EsTikProcess::setKeyCfg(const KeyBag& keycfg)
{
mKeyCfg = keycfg;
}
-void EsTikProcess::setCertificateChain(const fnd::List>& certs)
+void nstool::EsTikProcess::setCertificateChain(const std::vector>& certs)
{
mCerts = certs;
}
-void EsTikProcess::setCliOutputMode(CliOutputMode mode)
+void nstool::EsTikProcess::setCliOutputMode(CliOutputMode mode)
{
mCliOutputMode = mode;
}
-void EsTikProcess::setVerifyMode(bool verify)
+void nstool::EsTikProcess::setVerifyMode(bool verify)
{
mVerify = verify;
}
-void EsTikProcess::importTicket()
+void nstool::EsTikProcess::importTicket()
{
- fnd::Vec scratch;
+ tc::ByteData scratch;
if (*mFile == nullptr)
{
- throw fnd::Exception(kModuleName, "No file reader set.");
+ throw tc::Exception(kModuleName, "No file reader set.");
}
scratch.alloc((*mFile)->size());
@@ -66,10 +66,10 @@ void EsTikProcess::importTicket()
mTik.fromBytes(scratch.data(), scratch.size());
}
-void EsTikProcess::verifyTicket()
+void nstool::EsTikProcess::verifyTicket()
{
PkiValidator pki_validator;
- fnd::Vec tik_hash;
+ tc::ByteData tik_hash;
switch (nn::pki::sign::getHashAlgo(mTik.getSignature().getSignType()))
{
@@ -89,13 +89,13 @@ void EsTikProcess::verifyTicket()
pki_validator.addCertificates(mCerts);
pki_validator.validateSignature(mTik.getBody().getIssuer(), mTik.getSignature().getSignType(), mTik.getSignature().getSignature(), tik_hash);
}
- catch (const fnd::Exception& e)
+ catch (const tc::Exception& e)
{
std::cout << "[WARNING] Ticket signature could not be validated (" << e.error() << ")" << std::endl;
}
}
-void EsTikProcess::displayTicket()
+void nstool::EsTikProcess::displayTicket()
{
#define _SPLIT_VER(ver) (uint32_t)((ver>>10) & 0x3f) << "." << (uint32_t)((ver>>4) & 0x3f) << "." << (uint32_t)((ver>>0) & 0xf)
@@ -104,7 +104,7 @@ void EsTikProcess::displayTicket()
std::cout << "[ES Ticket]" << std::endl;
std::cout << " SignType: " << getSignTypeStr(mTik.getSignature().getSignType());
- if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
+ if (mCliOutputMode.show_extended_info)
std::cout << " (0x" << std::hex << mTik.getSignature().getSignType() << ")";
std::cout << std::endl;
@@ -129,7 +129,7 @@ void EsTikProcess::displayTicket()
}
std::cout << " Version: v" << _SPLIT_VER(body.getTicketVersion());
- if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
+ if (mCliOutputMode.show_extended_info)
std::cout << " (" << (uint32_t)body.getTicketVersion() << ")";
std::cout << std::endl;
@@ -144,16 +144,16 @@ void EsTikProcess::displayTicket()
}
}
- if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
+ if (mCliOutputMode.show_extended_info)
{
std::cout << " Reserved Region:" << std::endl;
fnd::SimpleTextOutput::hexDump(body.getReservedRegion(), 8, 0x10, 4);
}
- if (body.getTicketId() != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
+ if (body.getTicketId() != 0 || mCliOutputMode.show_extended_info)
std::cout << " TicketId: 0x" << std::hex << std::setw(16) << std::setfill('0') << body.getTicketId() << std::endl;
- if (body.getDeviceId() != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
+ if (body.getDeviceId() != 0 || mCliOutputMode.show_extended_info)
std::cout << " DeviceId: 0x" << std::hex << std::setw(16) << std::setfill('0') << body.getDeviceId() << std::endl;
std::cout << " RightsId: " << std::endl << " ";
@@ -167,7 +167,7 @@ void EsTikProcess::displayTicket()
#undef _SPLIT_VER
}
-const char* EsTikProcess::getSignTypeStr(uint32_t type) const
+const char* nstool::EsTikProcess::getSignTypeStr(uint32_t type) const
{
const char* str = nullptr;
switch(type)
@@ -197,7 +197,7 @@ const char* EsTikProcess::getSignTypeStr(uint32_t type) const
return str;
}
-const char* EsTikProcess::getTitleKeyPersonalisationStr(byte_t flag) const
+const char* nstool::EsTikProcess::getTitleKeyPersonalisationStr(byte_t flag) const
{
const char* str = nullptr;
switch(flag)
@@ -215,7 +215,7 @@ const char* EsTikProcess::getTitleKeyPersonalisationStr(byte_t flag) const
return str;
}
-const char* EsTikProcess::getLicenseTypeStr(byte_t flag) const
+const char* nstool::EsTikProcess::getLicenseTypeStr(byte_t flag) const
{
const char* str = nullptr;
switch(flag)
@@ -245,7 +245,7 @@ const char* EsTikProcess::getLicenseTypeStr(byte_t flag) const
return str;
}
-const char* EsTikProcess::getPropertyFlagStr(byte_t flag) const
+const char* nstool::EsTikProcess::getPropertyFlagStr(byte_t flag) const
{
const char* str = nullptr;
switch(flag)
diff --git a/src/EsTikProcess.h b/src/EsTikProcess.h
index cf3e36e..075759c 100644
--- a/src/EsTikProcess.h
+++ b/src/EsTikProcess.h
@@ -1,14 +1,12 @@
#pragma once
-#include
-#include
-#include
-#include
-#include
+#include "types.h"
+#include "KeyBag.h"
+
#include
#include
#include
-#include "KeyConfiguration.h"
-#include "common.h"
+
+namespace nstool {
class EsTikProcess
{
@@ -17,21 +15,21 @@ public:
void process();
- void setInputFile(const fnd::SharedPtr& file);
- void setKeyCfg(const KeyConfiguration& keycfg);
- void setCertificateChain(const fnd::List>& certs);
+ void setInputFile(const std::shared_ptr& file);
+ void setKeyCfg(const KeyBag& keycfg);
+ void setCertificateChain(const std::vector>& certs);
void setCliOutputMode(CliOutputMode mode);
void setVerifyMode(bool verify);
private:
const std::string kModuleName = "EsTikProcess";
- fnd::SharedPtr mFile;
- KeyConfiguration mKeyCfg;
+ std::shared_ptr mFile;
+ KeyBag mKeyCfg;
CliOutputMode mCliOutputMode;
bool mVerify;
- fnd::List> mCerts;
+ std::vector> mCerts;
nn::pki::SignedData mTik;
@@ -42,4 +40,6 @@ private:
const char* getTitleKeyPersonalisationStr(byte_t flag) const;
const char* getLicenseTypeStr(byte_t flag) const;
const char* getPropertyFlagStr(byte_t flag) const;
-};
\ No newline at end of file
+};
+
+}
\ No newline at end of file
diff --git a/src/GameCardProcess.cpp b/src/GameCardProcess.cpp
index b0cfb4a..d06a6ce 100644
--- a/src/GameCardProcess.cpp
+++ b/src/GameCardProcess.cpp
@@ -7,9 +7,9 @@
#include
#include "GameCardProcess.h"
-GameCardProcess::GameCardProcess() :
+nstool::GameCardProcess::GameCardProcess() :
mFile(),
- mCliOutputMode(_BIT(OUTPUT_BASIC)),
+ mCliOutputMode(true, false, false, false),
mVerify(false),
mListFs(false),
mProccessExtendedHeader(false),
@@ -18,7 +18,7 @@ GameCardProcess::GameCardProcess() :
{
}
-void GameCardProcess::process()
+void nstool::GameCardProcess::process()
{
importHeader();
@@ -27,7 +27,7 @@ void GameCardProcess::process()
validateXciSignature();
// display header
- if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
+ if (mCliOutputMode.show_basic_info)
displayHeader();
// process root partition
@@ -37,43 +37,43 @@ void GameCardProcess::process()
processPartitionPfs();
}
-void GameCardProcess::setInputFile(const fnd::SharedPtr& file)
+void nstool::GameCardProcess::setInputFile(const std::shared_ptr& file)
{
mFile = file;
}
-void GameCardProcess::setKeyCfg(const KeyConfiguration& keycfg)
+void nstool::GameCardProcess::setKeyCfg(const KeyBag& keycfg)
{
mKeyCfg = keycfg;
}
-void GameCardProcess::setCliOutputMode(CliOutputMode type)
+void nstool::GameCardProcess::setCliOutputMode(CliOutputMode type)
{
mCliOutputMode = type;
}
-void GameCardProcess::setVerifyMode(bool verify)
+void nstool::GameCardProcess::setVerifyMode(bool verify)
{
mVerify = verify;
}
-void GameCardProcess::setPartitionForExtract(const std::string& partition_name, const std::string& extract_path)
+void nstool::GameCardProcess::setPartitionForExtract(const std::string& partition_name, const std::string& extract_path)
{
- mExtractInfo.addElement({partition_name, extract_path});
+ mExtractInfo.push_back({partition_name, extract_path});
}
-void GameCardProcess::setListFs(bool list_fs)
+void nstool::GameCardProcess::setListFs(bool list_fs)
{
mListFs = list_fs;
}
-void GameCardProcess::importHeader()
+void nstool::GameCardProcess::importHeader()
{
- fnd::Vec scratch;
+ tc::ByteData scratch;
if (*mFile == nullptr)
{
- throw fnd::Exception(kModuleName, "No file reader set.");
+ throw tc::Exception(kModuleName, "No file reader set.");
}
// allocate memory for header
@@ -95,7 +95,7 @@ void GameCardProcess::importHeader()
}
else
{
- throw fnd::Exception(kModuleName, "GameCard image did not have expected magic bytes");
+ throw tc::Exception(kModuleName, "GameCard image did not have expected magic bytes");
}
nn::hac::sGcHeader_Rsa2048Signed* hdr_ptr = (nn::hac::sGcHeader_Rsa2048Signed*)(scratch.data() + mGcHeaderOffset);
@@ -107,7 +107,7 @@ void GameCardProcess::importHeader()
memcpy(mHdrSignature, hdr_ptr->signature, fnd::rsa::kRsa2048Size);
// decrypt extended header
- fnd::aes::sAes128Key header_key;
+ KeyBag::aes128_key_t header_key;
if (mKeyCfg.getXciHeaderKey(header_key))
{
nn::hac::GameCardUtil::decryptXciHeader(&hdr_ptr->header, header_key.key);
@@ -118,12 +118,12 @@ void GameCardProcess::importHeader()
mHdr.fromBytes((byte_t*)&hdr_ptr->header, sizeof(nn::hac::sGcHeader));
}
-void GameCardProcess::displayHeader()
+void nstool::GameCardProcess::displayHeader()
{
std::cout << "[GameCard Header]" << std::endl;
std::cout << " CardHeaderVersion: " << std::dec << (uint32_t)mHdr.getCardHeaderVersion() << std::endl;
std::cout << " RomSize: " << nn::hac::GameCardUtil::getRomSizeAsString((nn::hac::gc::RomSize)mHdr.getRomSizeType());
- if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
+ if (mCliOutputMode.show_extended_info)
std::cout << " (0x" << std::hex << (uint32_t)mHdr.getRomSizeType() << ")";
std::cout << std::endl;
std::cout << " PackageId: 0x" << std::hex << std::setw(16) << std::setfill('0') << mHdr.getPackageId() << std::endl;
@@ -138,7 +138,7 @@ void GameCardProcess::displayHeader()
}
}
}
- if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
+ if (mCliOutputMode.show_extended_info)
{
std::cout << " KekIndex: " << nn::hac::GameCardUtil::getKekIndexAsString((nn::hac::gc::KekIndex)mHdr.getKekIndex()) << " (" << std::dec << (uint32_t)mHdr.getKekIndex() << ")" << std::endl;
std::cout << " TitleKeyDecIndex: " << std::dec << (uint32_t)mHdr.getTitleKeyDecIndex() << std::endl;
@@ -147,7 +147,7 @@ void GameCardProcess::displayHeader()
std::cout << " " << fnd::SimpleTextOutput::arrayToString(mHdr.getInitialDataHash().bytes, 0x10, true, ":") << std::endl;
std::cout << " " << fnd::SimpleTextOutput::arrayToString(mHdr.getInitialDataHash().bytes+0x10, 0x10, true, ":") << std::endl;
}
- if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
+ if (mCliOutputMode.show_extended_info)
{
std::cout << " Extended Header AesCbc IV:" << std::endl;
std::cout << " " << fnd::SimpleTextOutput::arrayToString(mHdr.getAesCbcIv().iv, sizeof(mHdr.getAesCbcIv().iv), true, ":") << std::endl;
@@ -155,7 +155,7 @@ void GameCardProcess::displayHeader()
std::cout << " SelSec: 0x" << std::hex << mHdr.getSelSec() << std::endl;
std::cout << " SelT1Key: 0x" << std::hex << mHdr.getSelT1Key() << std::endl;
std::cout << " SelKey: 0x" << std::hex << mHdr.getSelKey() << std::endl;
- if (_HAS_BIT(mCliOutputMode, OUTPUT_LAYOUT))
+ if (mCliOutputMode.show_layout)
{
std::cout << " RomAreaStartPage: 0x" << std::hex << mHdr.getRomAreaStartPage();
if (mHdr.getRomAreaStartPage() != (uint32_t)(-1))
@@ -180,7 +180,7 @@ void GameCardProcess::displayHeader()
std::cout << " PartitionFs Header:" << std::endl;
std::cout << " Offset: 0x" << std::hex << mHdr.getPartitionFsAddress() << std::endl;
std::cout << " Size: 0x" << std::hex << mHdr.getPartitionFsSize() << std::endl;
- if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
+ if (mCliOutputMode.show_extended_info)
{
std::cout << " Hash:" << std::endl;
std::cout << " " << fnd::SimpleTextOutput::arrayToString(mHdr.getPartitionFsHash().bytes, 0x10, true, ":") << std::endl;
@@ -208,9 +208,9 @@ void GameCardProcess::displayHeader()
}
}
-bool GameCardProcess::validateRegionOfFile(size_t offset, size_t len, const byte_t* test_hash, bool use_salt, byte_t salt)
+bool nstool::GameCardProcess::validateRegionOfFile(size_t offset, size_t len, const byte_t* test_hash, bool use_salt, byte_t salt)
{
- fnd::Vec scratch;
+ tc::ByteData scratch;
fnd::sha::sSha256Hash calc_hash;
if (use_salt)
{
@@ -228,12 +228,12 @@ bool GameCardProcess::validateRegionOfFile(size_t offset, size_t len, const byte
return calc_hash.compare(test_hash);
}
-bool GameCardProcess::validateRegionOfFile(size_t offset, size_t len, const byte_t* test_hash)
+bool nstool::GameCardProcess::validateRegionOfFile(size_t offset, size_t len, const byte_t* test_hash)
{
return validateRegionOfFile(offset, len, test_hash, false, 0);
}
-void GameCardProcess::validateXciSignature()
+void nstool::GameCardProcess::validateXciSignature()
{
fnd::rsa::sRsa2048Key header_sign_key;
@@ -244,7 +244,7 @@ void GameCardProcess::validateXciSignature()
}
}
-void GameCardProcess::processRootPfs()
+void nstool::GameCardProcess::processRootPfs()
{
if (mVerify && validateRegionOfFile(mHdr.getPartitionFsAddress(), mHdr.getPartitionFsSize(), mHdr.getPartitionFsHash().bytes, mHdr.getCompatibilityType() != nn::hac::gc::COMPAT_GLOBAL, mHdr.getCompatibilityType()) == false)
{
@@ -258,9 +258,9 @@ void GameCardProcess::processRootPfs()
mRootPfs.process();
}
-void GameCardProcess::processPartitionPfs()
+void nstool::GameCardProcess::processPartitionPfs()
{
- const fnd::List& rootPartitions = mRootPfs.getPfsHeader().getFileList();
+ const std::vector& rootPartitions = mRootPfs.getPfsHeader().getFileList();
for (size_t i = 0; i < rootPartitions.size(); i++)
{
// this must be validated here because only the size of the root partiton header is known at verification time
diff --git a/src/GameCardProcess.h b/src/GameCardProcess.h
index b32ec0f..61243b7 100644
--- a/src/GameCardProcess.h
+++ b/src/GameCardProcess.h
@@ -1,14 +1,11 @@
#pragma once
-#include
-#include
-#include
-#include
-#include
-#include
-#include "KeyConfiguration.h"
+#include "types.h"
+#include "KeyBag.h"
#include "PfsProcess.h"
-#include "common.h"
+#include
+
+namespace nstool {
class GameCardProcess
{
@@ -18,8 +15,8 @@ public:
void process();
// generic
- void setInputFile(const fnd::SharedPtr& file);
- void setKeyCfg(const KeyConfiguration& keycfg);
+ void setInputFile(const std::shared_ptr& file);
+ void setKeyCfg(const KeyBag& keycfg);
void setCliOutputMode(CliOutputMode type);
void setVerifyMode(bool verify);
@@ -31,8 +28,8 @@ private:
const std::string kModuleName = "GameCardProcess";
const std::string kXciMountPointName = "gamecard:/";
- fnd::SharedPtr mFile;
- KeyConfiguration mKeyCfg;
+ std::shared_ptr mFile;
+ KeyBag mKeyCfg;
CliOutputMode mCliOutputMode;
bool mVerify;
bool mListFs;
@@ -65,7 +62,7 @@ private:
nn::hac::GameCardHeader mHdr;
PfsProcess mRootPfs;
- fnd::List mExtractInfo;
+ std::vector mExtractInfo;
void importHeader();
void displayHeader();
@@ -74,4 +71,6 @@ private:
void validateXciSignature();
void processRootPfs();
void processPartitionPfs();
-};
\ No newline at end of file
+};
+
+}
\ No newline at end of file
diff --git a/src/IniProcess.cpp b/src/IniProcess.cpp
index d6b6af1..d976309 100644
--- a/src/IniProcess.cpp
+++ b/src/IniProcess.cpp
@@ -9,20 +9,20 @@
#include "KipProcess.h"
-IniProcess::IniProcess() :
+nstool::IniProcess::IniProcess() :
mFile(),
- mCliOutputMode(_BIT(OUTPUT_BASIC)),
+ mCliOutputMode(true, false, false, false),
mVerify(false),
mDoExtractKip(false),
mKipExtractPath()
{
}
-void IniProcess::process()
+void nstool::IniProcess::process()
{
importHeader();
importKipList();
- if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
+ if (mCliOutputMode.show_basic_info)
{
displayHeader();
displayKipList();
@@ -33,39 +33,39 @@ void IniProcess::process()
}
}
-void IniProcess::setInputFile(const fnd::SharedPtr& file)
+void nstool::IniProcess::setInputFile(const std::shared_ptr& file)
{
mFile = file;
}
-void IniProcess::setCliOutputMode(CliOutputMode type)
+void nstool::IniProcess::setCliOutputMode(CliOutputMode type)
{
mCliOutputMode = type;
}
-void IniProcess::setVerifyMode(bool verify)
+void nstool::IniProcess::setVerifyMode(bool verify)
{
mVerify = verify;
}
-void IniProcess::setKipExtractPath(const std::string& path)
+void nstool::IniProcess::setKipExtractPath(const std::string& path)
{
mDoExtractKip = true;
mKipExtractPath = path;
}
-void IniProcess::importHeader()
+void nstool::IniProcess::importHeader()
{
- fnd::Vec scratch;
+ tc::ByteData scratch;
if (*mFile == nullptr)
{
- throw fnd::Exception(kModuleName, "No file reader set.");
+ throw tc::Exception(kModuleName, "No file reader set.");
}
if ((*mFile)->size() < sizeof(nn::hac::sIniHeader))
{
- throw fnd::Exception(kModuleName, "Corrupt INI: file too small");
+ throw tc::Exception(kModuleName, "Corrupt INI: file too small");
}
scratch.alloc(sizeof(nn::hac::sIniHeader));
@@ -74,14 +74,14 @@ void IniProcess::importHeader()
mHdr.fromBytes(scratch.data(), scratch.size());
}
-void IniProcess::importKipList()
+void nstool::IniProcess::importKipList()
{
// kip pos info
size_t kip_pos = sizeof(nn::hac::sIniHeader);
size_t kip_size = 0;
// tmp data to determine size
- fnd::Vec hdr_raw;
+ tc::ByteData hdr_raw;
nn::hac::KernelInitialProcessHeader hdr;
hdr_raw.alloc(sizeof(nn::hac::sKipHeader));
@@ -90,19 +90,19 @@ void IniProcess::importKipList()
(*mFile)->read(hdr_raw.data(), kip_pos, hdr_raw.size());
hdr.fromBytes(hdr_raw.data(), hdr_raw.size());
kip_size = getKipSizeFromHeader(hdr);
- mKipList.addElement(new fnd::OffsetAdjustedIFile(mFile, kip_pos, kip_size));
+ mKipList.push_back(new fnd::OffsetAdjustedIFile(mFile, kip_pos, kip_size));
kip_pos += kip_size;
}
}
-void IniProcess::displayHeader()
+void nstool::IniProcess::displayHeader()
{
std::cout << "[INI Header]" << std::endl;
std::cout << " Size: 0x" << std::hex << mHdr.getSize() << std::endl;
std::cout << " KIP Num: " << std::dec << (uint32_t)mHdr.getKipNum() << std::endl;
}
-void IniProcess::displayKipList()
+void nstool::IniProcess::displayKipList()
{
for (size_t i = 0; i < mKipList.size(); i++)
{
@@ -116,9 +116,9 @@ void IniProcess::displayKipList()
}
}
-void IniProcess::extractKipList()
+void nstool::IniProcess::extractKipList()
{
- fnd::Vec cache;
+ tc::ByteData cache;
nn::hac::KernelInitialProcessHeader hdr;
@@ -151,7 +151,7 @@ void IniProcess::extractKipList()
// get kip file size
out_size = (*mKipList[i])->size();
// extract kip
- if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
+ if (mCliOutputMode.show_basic_info)
printf("extract=[%s]\n", out_path.c_str());
(*mKipList[i])->seek(0);
@@ -164,7 +164,7 @@ void IniProcess::extractKipList()
}
}
-size_t IniProcess::getKipSizeFromHeader(const nn::hac::KernelInitialProcessHeader& hdr) const
+size_t nstool::IniProcess::getKipSizeFromHeader(const nn::hac::KernelInitialProcessHeader& hdr) const
{
return sizeof(nn::hac::sKipHeader) + hdr.getTextSegmentInfo().file_layout.size + hdr.getRoSegmentInfo().file_layout.size + hdr.getDataSegmentInfo().file_layout.size;
}
\ No newline at end of file
diff --git a/src/IniProcess.h b/src/IniProcess.h
index dbfad5e..365f715 100644
--- a/src/IniProcess.h
+++ b/src/IniProcess.h
@@ -1,14 +1,10 @@
#pragma once
-#include
-#include
-#include
-#include
-#include
-#include
+#include "types.h"
+
#include
#include
-#include "common.h"
+namespace nstool {
class IniProcess
{
@@ -17,7 +13,7 @@ public:
void process();
- void setInputFile(const fnd::SharedPtr& file);
+ void setInputFile(const std::shared_ptr& file);
void setCliOutputMode(CliOutputMode type);
void setVerifyMode(bool verify);
@@ -27,7 +23,7 @@ private:
const std::string kKipExtention = ".kip";
const size_t kCacheSize = 0x10000;
- fnd::SharedPtr mFile;
+ std::shared_ptr mFile;
CliOutputMode mCliOutputMode;
bool mVerify;
@@ -35,7 +31,7 @@ private:
std::string mKipExtractPath;
nn::hac::IniHeader mHdr;
- fnd::List> mKipList;
+ std::vector> mKipList;
void importHeader();
void importKipList();
@@ -44,4 +40,6 @@ private:
void extractKipList();
size_t getKipSizeFromHeader(const nn::hac::KernelInitialProcessHeader& hdr) const;
-};
\ No newline at end of file
+};
+
+}
\ No newline at end of file
diff --git a/src/KeyBag.cpp b/src/KeyBag.cpp
new file mode 100644
index 0000000..6e00cf0
--- /dev/null
+++ b/src/KeyBag.cpp
@@ -0,0 +1,568 @@
+#include "KeyBag.h"
+
+#include "util.h"
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+
+nstool::KeyBagInitializer::KeyBagInitializer(bool isDev, const tc::Optional& keyfile_path, const tc::Optional& tik_path, const tc::Optional& cert_path)
+{
+ if (keyfile_path.isSet())
+ {
+ importBaseKeyFile(keyfile_path.get(), isDev);
+ }
+ if (cert_path.isSet())
+ {
+ importCertificateChain(cert_path.get());
+ }
+ if (tik_path.isSet())
+ {
+ importTicket(tik_path.get());
+ }
+}
+
+void nstool::KeyBagInitializer::importBaseKeyFile(const tc::io::Path& keyfile_path, bool isDev)
+{
+ std::shared_ptr keyfile_stream = std::make_shared(tc::io::FileStream(keyfile_path, tc::io::FileMode::Open, tc::io::FileAccess::Read));
+
+ // import keyfile into a dictionary
+ std::map keyfile_dict;
+ processResFile(keyfile_stream, keyfile_dict);
+
+ // sources for key derivation
+ std::map master_key;
+ tc::Optional package2_key_source;
+ tc::Optional ticket_titlekek_source;
+ std::array, 3> key_area_key_source;
+ tc::Optional aes_kek_generation_source;
+ tc::Optional aes_key_generation_source;
+ tc::Optional nca_header_kek_source;
+ tc::Optional nca_header_key_source;
+ tc::Optional pki_root_sign_key;
+
+ // macros for importing
+
+#define _SAVE_AES128KEY(key_name, dst) \
+ { \
+ std::string key,val; \
+ tc::ByteData dec_val; \
+ aes128_key_t tmp_aes128_key; \
+ key = (key_name); \
+ val = keyfile_dict[key]; \
+ if (val.empty() == false) { \
+ dec_val = tc::cli::FormatUtil::hexStringToBytes(val); \
+ if (dec_val.size() != tmp_aes128_key.size()) \
+ throw tc::ArgumentException("nstool::KeyBagInitializer", "Key: \"" + key_name + "\" has incorrect length"); \
+ memcpy(tmp_aes128_key.data(), dec_val.data(), tmp_aes128_key.size()); \
+ (dst) = tmp_aes128_key; \
+ } \
+ }
+
+#define _SAVE_AES128XTSKEY(key_name, dst) \
+ { \
+ std::string key,val; \
+ tc::ByteData dec_val; \
+ aes128_xtskey_t tmp_aes128_xtskey; \
+ key = (key_name); \
+ val = keyfile_dict[key]; \
+ if (val.empty() == false) { \
+ dec_val = tc::cli::FormatUtil::hexStringToBytes(val); \
+ if (dec_val.size() != sizeof(tmp_aes128_xtskey)) \
+ throw tc::ArgumentException("nstool::KeyBagInitializer", "Key: \"" + key_name + "\" has incorrect length"); \
+ memcpy(tmp_aes128_xtskey[0].data(), dec_val.data(), tmp_aes128_xtskey[0].size()); \
+ memcpy(tmp_aes128_xtskey[1].data(), dec_val.data()+tmp_aes128_xtskey[0].size(), tmp_aes128_xtskey[1].size()); \
+ (dst) = tmp_aes128_xtskey; \
+ } \
+ }
+
+#define _SAVE_RSAKEY(key_name, dst, bitsize) \
+ { \
+ std::string key_mod,key_prv,val_mod,val_prv; \
+ tc::ByteData dec_val; \
+ rsa_key_t tmp_rsa_key; \
+ key_mod = fmt::format("{:s}_modulus", (key_name)); \
+ key_prv = fmt::format("{:s}_private", (key_name)); \
+ val_mod = keyfile_dict[key_mod]; \
+ val_prv = keyfile_dict[key_prv]; \
+ if (val_mod.empty() == false) { \
+ dec_val = tc::cli::FormatUtil::hexStringToBytes(val_mod); \
+ if (dec_val.size() == (bitsize) >> 3) { \
+ tmp_rsa_key.n = dec_val; \
+ if (val_prv.empty() == false) { \
+ dec_val = tc::cli::FormatUtil::hexStringToBytes(val_prv); \
+ if (dec_val.size() == (bitsize) >> 3) { \
+ tmp_rsa_key.d = dec_val; \
+ (dst) = tc::crypto::RsaPrivateKey(tmp_rsa_key.n.data(), tmp_rsa_key.n.size(), tmp_rsa_key.d.data(), tmp_rsa_key.d.size()); \
+ } \
+ else { \
+ fmt::print("[WARNING] Key: \"{:s}\" has incorrect length (was: {:d}, expected {:d})\n", key_prv, val_prv.size(), ((bitsize) >> 3)*2); \
+ } \
+ } \
+ else { \
+ (dst) = tc::crypto::RsaPublicKey(tmp_rsa_key.n.data(), tmp_rsa_key.n.size()); \
+ } \
+ } \
+ else {\
+ fmt::print("[WARNING] Key: \"{:s}\" has incorrect length (was: {:d}, expected {:d})\n", key_mod, val_mod.size(), ((bitsize) >> 3)*2); \
+ } \
+ } \
+ }
+
+ // keynames
+ enum NameVariantIndex
+ {
+ NNTOOLS,
+ LEGACY_HACTOOL,
+ LEGACY_0
+ };
+
+ static const size_t kNameVariantNum = 3;
+
+ std::vector kMasterBase = { "master" };
+ std::vector kPkg1Base = { "package1" };
+ std::vector kPkg2Base = { "package2" };
+ std::vector kXciHeaderBase = { "xci_header" };
+ std::vector kContentArchiveHeaderBase = { "nca_header", "header" };
+ std::vector kAcidBase = { "acid" };
+ std::vector kNrrCertBase = { "nrr_certificate" };
+ std::vector kPkiRootBase = { "pki_root" };
+ std::vector kTicketCommonKeyBase = { "ticket_commonkey", "titlekek" };
+ std::vector kNcaKeyAreaEncKeyBase = { "nca_key_area_key", "key_area_key", "nca_body_keak" };
+ std::vector kNcaKeyAreaEncKeyHwBase = { "nca_key_area_key_hw", "key_area_hw_key" };
+ std::vector kKekGenBase = { "aes_kek_generation" };
+ std::vector kKeyGenBase = { "aes_key_generation" };
+
+ // misc str
+ const std::string kKeyStr = "key";
+ const std::string kKekStr = "kek";
+ const std::string kSourceStr = "source";
+ const std::string kSignKey = "sign_key";
+ const std::string kModulusStr = "modulus";
+ const std::string kPrivateStr = "private";
+ std::vector kNcaKeyAreaKeyIndexStr = { "application", "ocean", "system" };
+
+ static const size_t kMasterKeyMax = 0x20;
+ /**/
+
+ // import key data
+ for (size_t name_idx = 0; name_idx < kNameVariantNum; name_idx++)
+ {
+ /* internal key sources */
+ if (name_idx < kMasterBase.size())
+ {
+ for (size_t mkey_rev = 0; mkey_rev < kMasterKeyMax; mkey_rev++)
+ {
+ // std::map master_key;
+ //fmt::print("{:s}_key_{:02x}\n", kMasterBase[name_idx], mkey_rev);
+ _SAVE_AES128KEY(fmt::format("{:s}_{:s}_{:02x}", kMasterBase[name_idx], kKeyStr, mkey_rev), master_key[mkey_rev]);
+ }
+ }
+
+ if (name_idx < kPkg2Base.size())
+ {
+ // tc::Optional package2_key_source;
+ //fmt::print("{:s}_key_source\n", kPkg2Base[name_idx]);
+ _SAVE_AES128KEY(fmt::format("{:s}_{:s}_{:s}", kPkg2Base[name_idx], kKeyStr, kSourceStr), package2_key_source);
+ }
+
+ if (name_idx < kTicketCommonKeyBase.size())
+ {
+ // tc::Optional ticket_titlekek_source;
+ //fmt::print("{:s}_source\n", kTicketCommonKeyBase[name_idx]);
+ _SAVE_AES128KEY(fmt::format("{:s}_{:s}", kTicketCommonKeyBase[name_idx], kSourceStr), ticket_titlekek_source);
+ }
+
+ if (name_idx < kNcaKeyAreaEncKeyBase.size())
+ {
+ // std::array, 3> key_area_key_source;
+
+ for (size_t keak_idx = 0; keak_idx < kNcaKeyAreaKeyIndexStr.size(); keak_idx++)
+ {
+ //fmt::print("{:s}_{:s}_source\n", kNcaKeyAreaEncKeyBase[name_idx], kNcaKeyAreaKeyIndexStr[keak_idx]);
+ _SAVE_AES128KEY(fmt::format("{:s}_{:s}_{:s}", kNcaKeyAreaEncKeyBase[name_idx], kNcaKeyAreaKeyIndexStr[keak_idx], kSourceStr), key_area_key_source[keak_idx]);
+ }
+ }
+
+ if (name_idx < kKekGenBase.size())
+ {
+ // tc::Optional aes_kek_generation_source;
+ //fmt::print("{:s}_source\n", kKekGenBase[name_idx]);
+ _SAVE_AES128KEY(fmt::format("{:s}_{:s}", kKekGenBase[name_idx], kSourceStr), aes_kek_generation_source);
+ }
+
+ if (name_idx < kKeyGenBase.size())
+ {
+ // tc::Optional aes_key_generation_source;
+ //fmt::print("{:s}_source\n", kKeyGenBase[name_idx]);
+ _SAVE_AES128KEY(fmt::format("{:s}_{:s}", kKeyGenBase[name_idx], kSourceStr), aes_key_generation_source);
+ }
+
+ if (name_idx < kContentArchiveHeaderBase.size())
+ {
+ // tc::Optional nca_header_kek_source;
+ //fmt::print("{:s}_kek_source\n", kContentArchiveHeaderBase[name_idx]);
+ _SAVE_AES128KEY(fmt::format("{:s}_{:s}_{:s}", kContentArchiveHeaderBase[name_idx], kKekStr, kSourceStr), nca_header_kek_source);
+ }
+
+ if (name_idx < kContentArchiveHeaderBase.size())
+ {
+ // tc::Optional nca_header_key_source;
+ //fmt::print("{:s}_key_source\n", kContentArchiveHeaderBase[name_idx]);
+ _SAVE_AES128XTSKEY(fmt::format("{:s}_{:s}_{:s}", kContentArchiveHeaderBase[name_idx], kKeyStr, kSourceStr), nca_header_key_source);
+ }
+
+ /* package1 */
+ // package1_key_xx
+ if (name_idx < kPkg1Base.size())
+ {
+ for (size_t mkey_rev = 0; mkey_rev < kMasterKeyMax; mkey_rev++)
+ {
+ //fmt::print("{:s}_key_{:02x}\n", kPkg1Base[name_idx], mkey_rev);
+ _SAVE_AES128KEY(fmt::format("{:s}_{:s}_{:02x}", kPkg1Base[name_idx], kKeyStr, mkey_rev), pkg1_key[mkey_rev]);
+ }
+ }
+
+ /* package2 */
+ if (name_idx < kPkg2Base.size())
+ {
+ // package2_key_xx
+ for (size_t mkey_rev = 0; mkey_rev < kMasterKeyMax; mkey_rev++)
+ {
+ //fmt::print("{:s}_key_{:02x}\n", kPkg2Base[name_idx], mkey_rev);
+ _SAVE_AES128KEY(fmt::format("{:s}_{:s}_{:02x}", kPkg2Base[name_idx], kKeyStr, mkey_rev), pkg2_key[mkey_rev]);
+ }
+
+ // package2_sign_key
+ //fmt::print("{:s}_{:s}_{:s}\n", kPkg2Base[name_idx], kSignKey, kPrivateStr);
+ //fmt::print("{:s}_{:s}_{:s}\n", kPkg2Base[name_idx], kSignKey, kModulusStr);
+ _SAVE_RSAKEY(fmt::format("{:s}_{:s}", kPkg2Base[name_idx], kSignKey), pkg2_sign_key, 2048);
+ }
+
+ /* eticket */
+ // ticket common key
+ if (name_idx < kTicketCommonKeyBase.size())
+ {
+ for (size_t mkey_rev = 0; mkey_rev < kMasterKeyMax; mkey_rev++)
+ {
+ //fmt::print("{:s}_{:02x}\n", kTicketCommonKeyBase[name_idx], mkey_rev);
+ _SAVE_AES128KEY(fmt::format("{:s}_{:02x}", kTicketCommonKeyBase[name_idx], mkey_rev), etik_common_key[mkey_rev]);
+ }
+ }
+
+ /* NCA keys */
+ if (name_idx < kContentArchiveHeaderBase.size())
+ {
+ // nca header key
+ //fmt::print("{:s}_{:s}\n", kContentArchiveHeaderBase[name_idx], kKeyStr);
+ //_SAVE_AES128XTSKEY(fmt::format("{:s}_{:s}", kContentArchiveHeaderBase[name_idx], kKeyStr), nca_header_key);
+
+ // nca header sign0 key (generations)
+ for (size_t mkey_rev = 0; mkey_rev < kMasterKeyMax; mkey_rev++)
+ {
+ //fmt::print("{:s}_{:s}_{:02x}_{:s}\n", kContentArchiveHeaderBase[name_idx], kSignKey, mkey_rev, kPrivateStr);
+ //fmt::print("{:s}_{:s}_{:02x}_{:s}\n", kContentArchiveHeaderBase[name_idx], kSignKey, mkey_rev, kModulusStr);
+ _SAVE_RSAKEY(fmt::format("{:s}_{:s}_{:02x}", kContentArchiveHeaderBase[name_idx], kSignKey, mkey_rev), nca_header_sign0_key[mkey_rev], 2048);
+ }
+ // nca header sign0 key (generation 0)
+ //fmt::print("{:s}_{:s}_{:s}\n", kContentArchiveHeaderBase[name_idx], kSignKey, kPrivateStr);
+ //fmt::print("{:s}_{:s}_{:s}\n", kContentArchiveHeaderBase[name_idx], kSignKey, kModulusStr);
+ _SAVE_RSAKEY(fmt::format("{:s}_{:s}", kContentArchiveHeaderBase[name_idx], kSignKey), nca_header_sign0_key[0], 2048);
+
+ }
+
+ // nca body key (unused since prototype format)
+
+ // nca key area encryption keys
+ if (name_idx < kNcaKeyAreaEncKeyBase.size())
+ {
+ for (size_t mkey_rev = 0; mkey_rev < kMasterKeyMax; mkey_rev++)
+ {
+ for (size_t keak_idx = 0; keak_idx < kNcaKeyAreaKeyIndexStr.size(); keak_idx++)
+ {
+ //fmt::print("{:s}_{:s}_{:02x}\n", kNcaKeyAreaEncKeyBase[name_idx], kNcaKeyAreaKeyIndexStr[keak_idx], mkey_rev);
+ _SAVE_AES128KEY(fmt::format("{:s}_{:s}_{:02x}", kNcaKeyAreaEncKeyBase[name_idx], kNcaKeyAreaKeyIndexStr[keak_idx], mkey_rev), nca_key_area_encryption_key[keak_idx][mkey_rev]);
+ }
+ }
+ }
+ // nca key area "hw" encryption keys
+ if (name_idx < kNcaKeyAreaEncKeyHwBase.size())
+ {
+ for (size_t mkey_rev = 0; mkey_rev < kMasterKeyMax; mkey_rev++)
+ {
+ for (size_t keak_idx = 0; keak_idx < kNcaKeyAreaKeyIndexStr.size(); keak_idx++)
+ {
+ //fmt::print("{:s}_{:s}_{:02x}\n", kNcaKeyAreaEncKeyHwBase[name_idx], kNcaKeyAreaKeyIndexStr[keak_idx], mkey_rev);
+ _SAVE_AES128KEY(fmt::format("{:s}_{:s}_{:02x}", kNcaKeyAreaEncKeyHwBase[name_idx], kNcaKeyAreaKeyIndexStr[keak_idx], mkey_rev), nca_key_area_encryption_key_hw[keak_idx][mkey_rev]);
+ }
+ }
+ }
+
+ /* ACID */
+ if (name_idx < kAcidBase.size())
+ {
+ // acid sign key (generations)
+ for (size_t mkey_rev = 0; mkey_rev < kMasterKeyMax; mkey_rev++)
+ {
+ //fmt::print("{:s}_{:s}_{:02x}_{:s}\n", kAcidBase[name_idx], kSignKey, mkey_rev, kPrivateStr);
+ //fmt::print("{:s}_{:s}_{:02x}_{:s}\n", kAcidBase[name_idx], kSignKey, mkey_rev, kModulusStr);
+ _SAVE_RSAKEY(fmt::format("{:s}_{:s}_{:02x}", kAcidBase[name_idx], kSignKey, mkey_rev), acid_sign_key[mkey_rev], 2048);
+ }
+ // acid sign key (generation 0)
+ //fmt::print("{:s}_{:s}_{:s}\n", kAcidBase[name_idx], kSignKey, kPrivateStr);
+ //fmt::print("{:s}_{:s}_{:s}\n", kAcidBase[name_idx], kSignKey, kModulusStr);
+ _SAVE_RSAKEY(fmt::format("{:s}_{:s}", kAcidBase[name_idx], kSignKey), acid_sign_key[0], 2048);
+ }
+
+ /* NRR certificate */
+ if (name_idx < kNrrCertBase.size())
+ {
+ // nrr certificate sign key (generations)
+ for (size_t mkey_rev = 0; mkey_rev < kMasterKeyMax; mkey_rev++)
+ {
+ //fmt::print("{:s}_{:s}_{:02x}_{:s}\n", kNrrCertBase[name_idx], kSignKey, mkey_rev, kPrivateStr);
+ //fmt::print("{:s}_{:s}_{:02x}_{:s}\n", kNrrCertBase[name_idx], kSignKey, mkey_rev, kModulusStr);
+ _SAVE_RSAKEY(fmt::format("{:s}_{:s}_{:02x}", kNrrCertBase[name_idx], kSignKey, mkey_rev), nrr_certificate_sign_key[mkey_rev], 2048);
+ }
+ // nrr certificate sign key (generation 0)
+ //fmt::print("{:s}_{:s}_{:s}\n", kNrrCertBase[name_idx], kSignKey, kPrivateStr);
+ //fmt::print("{:s}_{:s}_{:s}\n", kNrrCertBase[name_idx], kSignKey, kModulusStr);
+ _SAVE_RSAKEY(fmt::format("{:s}_{:s}", kNrrCertBase[name_idx], kSignKey), nrr_certificate_sign_key[0], 2048);
+ }
+
+ /* XCI header */
+ if (name_idx < kXciHeaderBase.size())
+ {
+ // xci header key (based on index)
+ for (size_t kek_index = 0; kek_index < 8; kek_index++)
+ {
+ //fmt::print("{:s}_{:s}_{:02x}\n", kXciHeaderBase[name_idx], kKeyStr, kek_index);
+ _SAVE_AES128KEY(fmt::format("{:s}_{:s}_{:02x}", kXciHeaderBase[name_idx], kKeyStr, kek_index), xci_header_key[kek_index]);
+ }
+ // xci header key (old lable)
+ //fmt::print("{:s}_{:s}\n", kXciHeaderBase[name_idx], kKeyStr);
+ _SAVE_AES128KEY(fmt::format("{:s}_{:s}", kXciHeaderBase[name_idx], kKeyStr), xci_header_key[isDev ? nn::hac::gc::KEK_DEV : nn::hac::gc::KEK_PROD]);
+
+ // xci header sign key
+ //fmt::print("{:s}_{:s}_{:s}\n", kXciHeaderBase[name_idx], kSignKey, kPrivateStr);
+ //fmt::print("{:s}_{:s}_{:s}\n", kXciHeaderBase[name_idx], kSignKey, kModulusStr);
+ _SAVE_RSAKEY(fmt::format("{:s}_{:s}", kXciHeaderBase[name_idx], kSignKey), xci_header_sign_key, 2048);
+ }
+
+ /* PKI */
+ if (name_idx < kPkiRootBase.size())
+ {
+ // tc::Optional pki_root_sign_key;
+ //fmt::print("{:s}_{:s}_{:s}\n", kPkiRootBase[name_idx], kSignKey, kPrivateStr);
+ //fmt::print("{:s}_{:s}_{:s}\n", kPkiRootBase[name_idx], kSignKey, kModulusStr);
+ _SAVE_RSAKEY(fmt::format("{:s}_{:s}", kPkiRootBase[name_idx], kSignKey), pki_root_sign_key, 4096);
+ }
+
+
+ }
+
+#undef _SAVE_RSAKEY
+#undef _SAVE_AES128XTSKEY
+#undef _SAVE_AES128KEY
+
+ // Derive Keys
+ for (auto itr = master_key.begin(); itr != master_key.end(); itr++)
+ {
+ if (aes_kek_generation_source.isSet() && aes_key_generation_source.isSet())
+ {
+ if (itr->first == 0 && nca_header_kek_source.isSet() && nca_header_key_source.isSet())
+ {
+ if (nca_header_key.isNull())
+ {
+ aes128_key_t nca_header_kek_tmp;
+ nn::hac::AesKeygen::generateKey(nca_header_kek_tmp.data(), aes_kek_generation_source.get().data(), nca_header_kek_source.get().data(), aes_key_generation_source.get().data(), itr->second.data());
+
+ aes128_xtskey_t nca_header_key_tmp;
+ nn::hac::AesKeygen::generateKey(nca_header_key_tmp[0].data(), nca_header_key_source.get()[0].data(), nca_header_kek_tmp.data());
+ nn::hac::AesKeygen::generateKey(nca_header_key_tmp[1].data(), nca_header_key_source.get()[1].data(), nca_header_kek_tmp.data());
+
+ nca_header_key = nca_header_key_tmp;
+ }
+ }
+
+ for (size_t keak_idx = 0; keak_idx < nn::hac::nca::kKeyAreaEncryptionKeyNum; keak_idx++)
+ {
+ if (key_area_key_source[keak_idx].isSet() && nca_key_area_encryption_key[keak_idx].find(itr->first) != nca_key_area_encryption_key[keak_idx].end())
+ {
+ aes128_key_t nca_key_area_encryption_key_tmp;
+ nn::hac::AesKeygen::generateKey(nca_key_area_encryption_key_tmp.data(), aes_kek_generation_source.get().data(), key_area_key_source[keak_idx].get().data(), aes_key_generation_source.get().data(), itr->second.data());
+ nca_key_area_encryption_key[keak_idx][itr->first] = nca_key_area_encryption_key_tmp;
+ }
+ }
+ }
+ if (ticket_titlekek_source.isSet() && etik_common_key.find(itr->first) == etik_common_key.end())
+ {
+ aes128_key_t etik_common_key_tmp;
+ nn::hac::AesKeygen::generateKey(etik_common_key_tmp.data(), ticket_titlekek_source.get().data(), itr->second.data());
+ etik_common_key[itr->first] = etik_common_key_tmp;
+ }
+ if (package2_key_source.isSet() && pkg2_key.find(itr->first) == pkg2_key.end())
+ {
+ aes128_key_t pkg2_key_tmp;
+ nn::hac::AesKeygen::generateKey(pkg2_key_tmp.data(), package2_key_source.get().data(), itr->second.data());
+ pkg2_key[itr->first] = pkg2_key_tmp;
+ }
+ }
+
+ // Save PKI Root Key
+ if (pki_root_sign_key.isSet())
+ {
+ broadon_rsa_signer["Root"] = { tc::ByteData(), pki_root_sign_key.get() };
+ }
+}
+
+void nstool::KeyBagInitializer::importTitleKeyFile(const tc::io::Path& keyfile_path)
+{
+
+}
+
+void nstool::KeyBagInitializer::importCertificateChain(const tc::io::Path& cert_path)
+{
+ // save file path string for error messages
+ std::string cert_path_str;
+ tc::io::PathUtil::pathToUnixUTF8(cert_path, cert_path_str);
+
+ // open cert file
+ std::shared_ptr certfile_stream;
+ try {
+ certfile_stream = std::make_shared(tc::io::FileStream(cert_path, tc::io::FileMode::Open, tc::io::FileAccess::Read));
+ }
+ catch (tc::io::FileNotFoundException& e) {
+ fmt::print("[WARNING] Failed to open certificate file \"{:s}\" ({:s}).\n", cert_path_str, e.error());
+ return;
+ }
+
+ // check size
+ size_t cert_raw_size = tc::io::IOUtil::castInt64ToSize(certfile_stream->length());
+ if (cert_raw_size > 0x10000)
+ {
+ fmt::print("[WARNING] Certificate file \"{:s}\" was too large.\n", cert_path_str);
+ return;
+ }
+
+ // import cert data
+ tc::ByteData cert_raw = tc::ByteData(cert_raw_size);
+ certfile_stream->seek(0, tc::io::SeekOrigin::Begin);
+ certfile_stream->read(cert_raw.data(), cert_raw.size());
+
+ nn::pki::SignedData cert;
+ try {
+ for (size_t f_pos = 0; f_pos < cert_raw.size(); f_pos += cert.getBytes().size())
+ {
+ cert.fromBytes(cert_raw.data() + f_pos, cert_raw.size() - f_pos);
+
+ std::string cert_identity = fmt::format("{:s}-{:s}", cert.getBody().getIssuer(), cert.getBody().getSubject());
+
+ switch (cert.getBody().getPublicKeyType()) {
+ case nn::pki::cert::PublicKeyType::RSA2048:
+ broadon_rsa_signer[cert_identity] = { cert.getBytes(), cert.getBody().getRsa2048PublicKey() };
+ break;
+ case nn::pki::cert::PublicKeyType::RSA4096:
+ broadon_rsa_signer[cert_identity] = { cert.getBytes(), cert.getBody().getRsa4096PublicKey() };
+ break;
+ case nn::pki::cert::PublicKeyType::ECDSA240:
+ fmt::print("[WARNING] Certificate {:s} will not be imported. ecc233 public keys are not supported yet.\n", cert_identity);
+ break;
+ default:
+ fmt::print("[WARNING] Certificate {:s} will not be imported. Unknown public key type.\n", cert_identity);
+ }
+ }
+ }
+ catch (tc::Exception& e) {
+ fmt::print("[WARNING] Certificate file \"{:s}\" is corrupted ({:s}).\n", cert_path_str, e.error());
+ return;
+ }
+}
+
+void nstool::KeyBagInitializer::importTicket(const tc::io::Path& tik_path)
+{
+ // save file path string for error messages
+ std::string tik_path_str;
+ tc::io::PathUtil::pathToUnixUTF8(tik_path, tik_path_str);
+
+ // open cert file
+ std::shared_ptr tik_stream;
+ try {
+ tik_stream = std::make_shared(tc::io::FileStream(tik_path, tc::io::FileMode::Open, tc::io::FileAccess::Read));
+ }
+ catch (tc::io::FileNotFoundException& e) {
+ fmt::print("[WARNING] Failed to open ticket \"{:s}\" ({:s}).\n", tik_path_str, e.error());
+ return;
+ }
+
+ // check size
+ size_t tik_raw_size = tc::io::IOUtil::castInt64ToSize(tik_stream->length());
+ if (tik_raw_size > 0x10000)
+ {
+ fmt::print("[WARNING] Ticket \"{:s}\" was too large.\n", tik_path_str);
+ return;
+ }
+
+ // import cert data
+ tc::ByteData tik_raw = tc::ByteData(tik_raw_size);
+ tik_stream->seek(0, tc::io::SeekOrigin::Begin);
+ tik_stream->read(tik_raw.data(), tik_raw.size());
+
+ nn::pki::SignedData tik;
+ try {
+ // de serialise ticket
+ tik.fromBytes(tik_raw.data(), tik_raw.size());
+
+ // save rights id
+ rights_id_t rights_id;
+ memcpy(rights_id.data(), tik.getBody().getRightsId(), rights_id.size());
+
+ // check ticket is not personalised
+ if (tik.getBody().getTitleKeyEncType() != nn::es::ticket::AES128_CBC)
+ {
+ fmt::print("[WARNING] Ticket \"{:s}\" will not be imported. Personalised tickets are not supported.\n", tc::cli::FormatUtil::formatBytesAsString(rights_id.data(), rights_id.size(), true, ""));
+ return;
+ }
+
+ // save enc title key
+ aes128_key_t enc_title_key;
+ memcpy(enc_title_key.data(), tik.getBody().getEncTitleKey(), enc_title_key.size());
+
+ // save the encrypted title key as the fallback enc content key incase the ticket was malformed and workarounds to decrypt it in isolation fail
+ if (fallback_enc_content_key.isNull())
+ {
+ fallback_enc_content_key = enc_title_key;
+ }
+
+ // determine key to decrypt title key
+ byte_t common_key_index = tik.getBody().getCommonKeyId();
+
+ // work around for bad scene tickets where they don't set the commonkey id field (detect scene ticket with ffff.... signature)
+ if (common_key_index != rights_id[15] && tik.getSignature().getBytes()[0x00] == 0xff && tik.getSignature().getBytes()[0x01] == 0xff)
+ {
+ common_key_index = rights_id[15];
+ }
+ if (etik_common_key.find(tik.getBody().getCommonKeyId()) == etik_common_key.end())
+ {
+ fmt::print("[WARNING] Ticket \"{:s}\" will not be imported. Could not decrypt title key.\n", tc::cli::FormatUtil::formatBytesAsString(rights_id.data(), rights_id.size(), true, ""));
+ return;
+ }
+
+ // decrypt title key
+ aes128_key_t dec_title_key;
+ tc::crypto::DecryptAes128Ecb(dec_title_key.data(), enc_title_key.data(), sizeof(aes128_key_t), etik_common_key[common_key_index].data(), sizeof(aes128_key_t));
+
+ // add to key dict
+ external_content_keys[rights_id] = dec_title_key;
+
+ }
+ catch (tc::Exception& e) {
+ fmt::print("[WARNING] Ticket \"{:s}\" is corrupted ({:s}).\n", tik_path_str, e.error());
+ return;
+ }
+}
\ No newline at end of file
diff --git a/src/KeyBag.h b/src/KeyBag.h
new file mode 100644
index 0000000..e319fb6
--- /dev/null
+++ b/src/KeyBag.h
@@ -0,0 +1,76 @@
+#pragma once
+#include
+#include
+#include
+#include