Compare commits
No commits in common. "91ab6fd74bb3a1f32c08d863f8626523dd080b41" and "51398a6ea9a5483567c45531e7b3a5f50730db84" have entirely different histories.
91ab6fd74b
...
51398a6ea9
16 changed files with 41 additions and 1091 deletions
|
@ -1,4 +1,4 @@
|
||||||
<img src="misc/logo.svg" title="" alt="libKonogonka" data-align="center">
|
# libKonogonka
|
||||||
|
|
||||||
![License](https://img.shields.io/badge/License-GPLv3-blue.svg) [![Build Status](https://ci.redrise.ru/api/badges/desu/libKonogonka/status.svg)](https://ci.redrise.ru/desu/libKonogonka)
|
![License](https://img.shields.io/badge/License-GPLv3-blue.svg) [![Build Status](https://ci.redrise.ru/api/badges/desu/libKonogonka/status.svg)](https://ci.redrise.ru/desu/libKonogonka)
|
||||||
|
|
||||||
|
@ -13,13 +13,10 @@ You can get this application from independent source location: [https://git.redr
|
||||||
[GNU General Public License v3 or higher](https://git.redrise.ru/desu/libKonogonka/LICENSE)
|
[GNU General Public License v3 or higher](https://git.redrise.ru/desu/libKonogonka/LICENSE)
|
||||||
|
|
||||||
### Used libraries & resources
|
### Used libraries & resources
|
||||||
|
|
||||||
* [Bouncy Castle](https://www.bouncycastle.org/) for Java.
|
* [Bouncy Castle](https://www.bouncycastle.org/) for Java.
|
||||||
* [Java-XTS-AES](https://github.com/horrorho/Java-XTS-AES) by horrorho with minimal changes.
|
* [Java-XTS-AES](https://github.com/horrorho/Java-XTS-AES) by horrorho with minimal changes.
|
||||||
* [lz4-java](https://github.com/lz4/lz4-java)
|
|
||||||
|
|
||||||
#### Thanks
|
|
||||||
|
|
||||||
|
#### Thanks
|
||||||
* Switch brew wiki
|
* Switch brew wiki
|
||||||
* Original ScriesM software
|
* Original ScriesM software
|
||||||
* roothorick, [shchmue](https://github.com/shchmue/), He, other Team AtlasNX discord members for their advices, notes and examples!
|
* roothorick, [shchmue](https://github.com/shchmue/), He, other Team AtlasNX discord members for their advices, notes and examples!
|
||||||
|
|
197
misc/logo.svg
197
misc/logo.svg
|
@ -1,197 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
width="186.45644mm"
|
|
||||||
height="29.882706mm"
|
|
||||||
viewBox="0 0 186.45643 29.882706"
|
|
||||||
version="1.1"
|
|
||||||
id="svg5"
|
|
||||||
inkscape:version="1.2.1 (9c6d41e410, 2022-07-14)"
|
|
||||||
sodipodi:docname="logo.svg"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg">
|
|
||||||
<sodipodi:namedview
|
|
||||||
id="namedview7"
|
|
||||||
pagecolor="#505050"
|
|
||||||
bordercolor="#eeeeee"
|
|
||||||
borderopacity="1"
|
|
||||||
inkscape:showpageshadow="0"
|
|
||||||
inkscape:pageopacity="0"
|
|
||||||
inkscape:pagecheckerboard="0"
|
|
||||||
inkscape:deskcolor="#505050"
|
|
||||||
inkscape:document-units="mm"
|
|
||||||
showgrid="false"
|
|
||||||
inkscape:zoom="2.8284272"
|
|
||||||
inkscape:cx="378.30212"
|
|
||||||
inkscape:cy="70.5339"
|
|
||||||
inkscape:window-width="2266"
|
|
||||||
inkscape:window-height="1414"
|
|
||||||
inkscape:window-x="1305"
|
|
||||||
inkscape:window-y="546"
|
|
||||||
inkscape:window-maximized="0"
|
|
||||||
inkscape:current-layer="layer1"
|
|
||||||
showguides="false" />
|
|
||||||
<defs
|
|
||||||
id="defs2">
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient1032">
|
|
||||||
<stop
|
|
||||||
style="stop-color:#cc0000;stop-opacity:1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop1036" />
|
|
||||||
<stop
|
|
||||||
style="stop-color:#cc0000;stop-opacity:0;"
|
|
||||||
offset="1"
|
|
||||||
id="stop1038" />
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient
|
|
||||||
id="linearGradient1032-1">
|
|
||||||
<stop
|
|
||||||
style="stop-color:#4d0b0b;stop-opacity:1;"
|
|
||||||
offset="0"
|
|
||||||
id="stop1028" />
|
|
||||||
<stop
|
|
||||||
style="stop-color:#cc0000;stop-opacity:1;"
|
|
||||||
offset="0.11232118"
|
|
||||||
id="stop1030" />
|
|
||||||
<stop
|
|
||||||
style="stop-color:#cc0000;stop-opacity:1;"
|
|
||||||
offset="0.89785337"
|
|
||||||
id="stop1122" />
|
|
||||||
<stop
|
|
||||||
style="stop-color:#4c0b0b;stop-opacity:1;"
|
|
||||||
offset="1"
|
|
||||||
id="stop1041" />
|
|
||||||
</linearGradient>
|
|
||||||
<filter
|
|
||||||
style="color-interpolation-filters:sRGB"
|
|
||||||
inkscape:label="Drop Shadow"
|
|
||||||
id="filter3735"
|
|
||||||
x="-0.032139049"
|
|
||||||
y="-0.032139049"
|
|
||||||
width="1.0669564"
|
|
||||||
height="1.0682955">
|
|
||||||
<feFlood
|
|
||||||
flood-opacity="0.54902"
|
|
||||||
flood-color="rgb(0,0,0)"
|
|
||||||
result="flood"
|
|
||||||
id="feFlood3725" />
|
|
||||||
<feComposite
|
|
||||||
in="flood"
|
|
||||||
in2="SourceGraphic"
|
|
||||||
operator="in"
|
|
||||||
result="composite1"
|
|
||||||
id="feComposite3727" />
|
|
||||||
<feGaussianBlur
|
|
||||||
in="composite1"
|
|
||||||
stdDeviation="1"
|
|
||||||
result="blur"
|
|
||||||
id="feGaussianBlur3729" />
|
|
||||||
<feOffset
|
|
||||||
dx="0.2"
|
|
||||||
dy="0.3"
|
|
||||||
result="offset"
|
|
||||||
id="feOffset3731" />
|
|
||||||
<feComposite
|
|
||||||
in="SourceGraphic"
|
|
||||||
in2="offset"
|
|
||||||
operator="over"
|
|
||||||
result="composite2"
|
|
||||||
id="feComposite3733" />
|
|
||||||
</filter>
|
|
||||||
<filter
|
|
||||||
style="color-interpolation-filters:sRGB"
|
|
||||||
inkscape:label="Drop Shadow"
|
|
||||||
id="filter3747"
|
|
||||||
x="-0.015761159"
|
|
||||||
y="-0.10270455"
|
|
||||||
width="1.0328357"
|
|
||||||
height="1.2182472">
|
|
||||||
<feFlood
|
|
||||||
flood-opacity="0.54902"
|
|
||||||
flood-color="rgb(0,0,0)"
|
|
||||||
result="flood"
|
|
||||||
id="feFlood3737" />
|
|
||||||
<feComposite
|
|
||||||
in="flood"
|
|
||||||
in2="SourceGraphic"
|
|
||||||
operator="in"
|
|
||||||
result="composite1"
|
|
||||||
id="feComposite3739" />
|
|
||||||
<feGaussianBlur
|
|
||||||
in="composite1"
|
|
||||||
stdDeviation="1"
|
|
||||||
result="blur"
|
|
||||||
id="feGaussianBlur3741" />
|
|
||||||
<feOffset
|
|
||||||
dx="0.2"
|
|
||||||
dy="0.3"
|
|
||||||
result="offset"
|
|
||||||
id="feOffset3743" />
|
|
||||||
<feComposite
|
|
||||||
in="SourceGraphic"
|
|
||||||
in2="offset"
|
|
||||||
operator="over"
|
|
||||||
result="composite2"
|
|
||||||
id="feComposite3745" />
|
|
||||||
</filter>
|
|
||||||
</defs>
|
|
||||||
<g
|
|
||||||
inkscape:label="L1"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer1"
|
|
||||||
transform="translate(-14.404994,-126.83343)">
|
|
||||||
<g
|
|
||||||
id="g934">
|
|
||||||
<circle
|
|
||||||
style="display:inline;fill:#ffffff;fill-opacity:1;stroke-width:1.30808;filter:url(#filter3735)"
|
|
||||||
id="path1766-9"
|
|
||||||
cx="82.249115"
|
|
||||||
cy="240.27315"
|
|
||||||
r="37.337757"
|
|
||||||
transform="matrix(0.32697901,0,0,0.32697901,0.50467243,61.262568)" />
|
|
||||||
<circle
|
|
||||||
style="fill:#3a3a3a;fill-opacity:1;stroke-width:0.40632"
|
|
||||||
id="path1766"
|
|
||||||
cx="27.398407"
|
|
||||||
cy="139.82684"
|
|
||||||
r="11.598027" />
|
|
||||||
<path
|
|
||||||
id="rect1304"
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke-width:0.495873"
|
|
||||||
d="m 23.094714,131.98367 c -0.01603,0.002 -0.03159,0.009 -0.04392,0.0212 l -2.970361,2.97191 a 1.3654379,1.7456499 3.0525799 0 1 0.15658,0.77825 1.3654379,1.7456499 3.0525799 0 1 -0.405144,1.25574 l 0.882634,0.88212 c 0.02822,0.0282 0.0736,0.0282 0.101803,0 l 1.578714,-1.57924 4.230233,4.22765 -5.007963,5.76296 c -0.02618,0.0301 -0.02286,0.0751 0.0072,0.10128 l 0.638204,0.55449 c 0.03012,0.0262 0.07512,0.0234 0.101286,-0.007 l 4.963004,-5.71025 5.706629,5.70353 c 0.02822,0.0282 0.0736,0.0282 0.101803,0 l 0.597895,-0.59841 c 0.02822,-0.0282 0.02822,-0.0736 0,-0.10181 l -5.75572,-5.7521 3.962549,-4.55941 0.388606,0.33796 c 0.03011,0.0262 1.846533,0.7774 2.807063,0.87385 -0.112556,-1.06415 -1.083514,-2.79639 -1.113628,-2.82256 l -0.388605,-0.33797 0.06046,-0.0692 c 0.02619,-0.0301 0.02289,-0.0756 -0.0072,-0.10181 l -0.638719,-0.55449 c -0.03011,-0.0262 -0.07512,-0.0234 -0.101286,0.007 l -0.06046,0.0692 -1.531173,-1.33066 c -0.03011,-0.0262 -0.07564,-0.023 -0.101802,0.007 l -1.599386,1.8402 c -0.02618,0.0301 -0.02289,0.0756 0.0072,0.1018 l 1.531689,1.33067 -3.917073,4.5067 -4.18114,-4.17907 1.78232,-1.78336 c 0.0282,-0.0282 0.02821,-0.0736 0,-0.1018 l -1.724443,-1.72341 c -0.01411,-0.0141 -0.03227,-0.0212 -0.05064,-0.0212 -0.0023,0 -0.0049,-2.1e-4 -0.0072,0 z" />
|
|
||||||
</g>
|
|
||||||
<text
|
|
||||||
xml:space="preserve"
|
|
||||||
style="font-size:10.5833px;line-height:1.25;font-family:Terminus;-inkscape-font-specification:'Terminus, Normal';stroke-width:0.264583;filter:url(#filter3747)"
|
|
||||||
x="44.007172"
|
|
||||||
y="147.82162"
|
|
||||||
id="text2788"><tspan
|
|
||||||
sodipodi:role="line"
|
|
||||||
id="tspan2786"
|
|
||||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:25.4px;font-family:Play;-inkscape-font-specification:'Play, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#ffffff;stroke-width:0.264583"
|
|
||||||
x="44.007172"
|
|
||||||
y="147.82162">libKonogonka</tspan></text>
|
|
||||||
<g
|
|
||||||
id="g939">
|
|
||||||
<path
|
|
||||||
id="rect2890"
|
|
||||||
style="fill:#00ddff;fill-opacity:1;stroke-width:1.02576"
|
|
||||||
d="M 21.024968,155.29019 H 80.12021 l 0.03058,0.01 -5.965349,1.41604 -59.110532,4e-5 c -0.01693,0 -0.04392,-0.007 -0.03056,-0.01 l 5.950068,-1.40725 c 0.01336,-0.003 0.01363,-0.01 0.03056,-0.01 z"
|
|
||||||
sodipodi:nodetypes="scccssss" />
|
|
||||||
<path
|
|
||||||
id="rect2890-9"
|
|
||||||
style="fill:#0038a5;fill-opacity:1;stroke-width:1.02576"
|
|
||||||
d="m 80.13148,155.29019 h 59.09523 l 0.0306,0.01 -5.96535,1.41604 -59.110523,4e-5 c -0.01693,0 -0.04392,-0.007 -0.03056,-0.01 l 5.950062,-1.40725 c 0.01333,-0.003 0.01357,-0.01 0.03058,-0.01 z"
|
|
||||||
sodipodi:nodetypes="scccssss" />
|
|
||||||
<path
|
|
||||||
id="rect2890-9-6"
|
|
||||||
style="fill:#f00001;fill-opacity:1;stroke-width:1.02576"
|
|
||||||
d="m 139.23792,155.29019 h 59.09526 l 0.0306,0.01 -5.96535,1.41604 -59.11057,4e-5 c -0.0169,0 -0.0439,-0.007 -0.0306,-0.01 l 5.95007,-1.40725 c 0.0133,-0.003 0.0136,-0.01 0.0306,-0.01 z"
|
|
||||||
sodipodi:nodetypes="scccssss" />
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 7.5 KiB |
17
pom.xml
17
pom.xml
|
@ -15,7 +15,7 @@
|
||||||
<inceptionYear>2022</inceptionYear>
|
<inceptionYear>2022</inceptionYear>
|
||||||
<organization>
|
<organization>
|
||||||
<name>Dmitry Isaenko</name>
|
<name>Dmitry Isaenko</name>
|
||||||
<url>https://redrise.ru/</url>
|
<url>https://developersu.blogspot.com/</url>
|
||||||
</organization>
|
</organization>
|
||||||
|
|
||||||
<licenses>
|
<licenses>
|
||||||
|
@ -67,33 +67,28 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.logging.log4j</groupId>
|
<groupId>org.apache.logging.log4j</groupId>
|
||||||
<artifactId>log4j-core</artifactId>
|
<artifactId>log4j-core</artifactId>
|
||||||
<version>2.19.0</version>
|
<version>2.18.0</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- testing -->
|
<!-- testing -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<artifactId>junit-jupiter-engine</artifactId>
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
<version>5.9.0</version>
|
<version>5.5.2</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<artifactId>junit-jupiter-api</artifactId>
|
<artifactId>junit-jupiter-api</artifactId>
|
||||||
<version>5.9.0</version>
|
<version>5.5.2</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<artifactId>junit-jupiter-params</artifactId>
|
<artifactId>junit-jupiter-params</artifactId>
|
||||||
<version>5.9.0</version>
|
<version>5.5.2</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
|
||||||
<groupId>org.lz4</groupId>
|
|
||||||
<artifactId>lz4-pure-java</artifactId>
|
|
||||||
<version>1.8.0</version>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<build>
|
<build>
|
||||||
<finalName>${project.artifactId}-${project.version}-${maven.build.timestamp}</finalName>
|
<finalName>${project.artifactId}-${project.version}-${maven.build.timestamp}</finalName>
|
||||||
|
@ -116,7 +111,7 @@
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
<!-- UNCOMMENT ONCE LEARN HOW TO MAKE NORMAL DOCS
|
<!-- UNCOMMENT WHEN LEARN HOW TO MAKE NORMAL DOCS
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-javadoc-plugin</artifactId>
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
|
|
|
@ -56,10 +56,6 @@ public class Converter {
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String intToBinaryString(int value){
|
|
||||||
return String.format("%32s", Integer.toBinaryString( value )).replace(' ', '0')+" | "+value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String longToOctString(long value){
|
public static String longToOctString(long value){
|
||||||
return String.format("%64s", Long.toBinaryString( value )).replace(' ', '0');
|
return String.format("%64s", Long.toBinaryString( value )).replace(' ', '0');
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@ public class RainbowDump {
|
||||||
|
|
||||||
private static StringBuilder stringBuilder;
|
private static StringBuilder stringBuilder;
|
||||||
public static void hexDumpUTF8(byte[] byteArray){
|
public static void hexDumpUTF8(byte[] byteArray){
|
||||||
stringBuilder = new StringBuilder(" -- RainbowDump --\n");
|
stringBuilder = new StringBuilder();
|
||||||
if (byteArray == null || byteArray.length == 0)
|
if (byteArray == null || byteArray.length == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -106,11 +106,11 @@ public class RainbowDump {
|
||||||
System.out.print(new String(byteArray, StandardCharsets.UTF_8)+"\n");
|
System.out.print(new String(byteArray, StandardCharsets.UTF_8)+"\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void binDumpInt(int value){
|
public static void octDumpInt(int value){
|
||||||
log.debug(Converter.intToBinaryString(value));
|
System.out.println(String.format("%32s", Integer.toBinaryString( value )).replace(' ', '0')+" | "+value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void binDumpLong(long value){
|
public static void octDumpLong(long value){
|
||||||
System.out.println(String.format("%64s", Long.toBinaryString( value )).replace(' ', '0')+" | "+value);
|
System.out.println(String.format("%64s", Long.toBinaryString( value )).replace(' ', '0')+" | "+value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,14 +27,14 @@ import java.util.Arrays;
|
||||||
* */
|
* */
|
||||||
public class FSAccessHeaderProvider {
|
public class FSAccessHeaderProvider {
|
||||||
|
|
||||||
private final byte version;
|
private byte version;
|
||||||
private final byte[] padding;
|
private byte[] padding;
|
||||||
private final long permissionsBitmask;
|
private long permissionsBitmask;
|
||||||
private final int dataSize;
|
private int dataSize;
|
||||||
private final int contentOwnIdSectionSize;
|
private int contentOwnIdSectionSize;
|
||||||
private final int dataOwnerSizes;
|
private int dataNownerSizes;
|
||||||
private final int saveDataOwnSectionSize;
|
private int saveDataOwnSectionSize;
|
||||||
private final byte[] unknownData;
|
private byte[] unknownData;
|
||||||
|
|
||||||
public FSAccessHeaderProvider(byte[] bytes) {
|
public FSAccessHeaderProvider(byte[] bytes) {
|
||||||
version = bytes[0];
|
version = bytes[0];
|
||||||
|
@ -42,7 +42,7 @@ public class FSAccessHeaderProvider {
|
||||||
permissionsBitmask = Converter.getLElong(bytes, 0x4);
|
permissionsBitmask = Converter.getLElong(bytes, 0x4);
|
||||||
dataSize = Converter.getLEint(bytes, 0xC);
|
dataSize = Converter.getLEint(bytes, 0xC);
|
||||||
contentOwnIdSectionSize = Converter.getLEint(bytes, 0x10);
|
contentOwnIdSectionSize = Converter.getLEint(bytes, 0x10);
|
||||||
dataOwnerSizes = Converter.getLEint(bytes, 0x14);
|
dataNownerSizes = Converter.getLEint(bytes, 0x14);
|
||||||
saveDataOwnSectionSize = Converter.getLEint(bytes, 0x18);
|
saveDataOwnSectionSize = Converter.getLEint(bytes, 0x18);
|
||||||
unknownData = Arrays.copyOfRange(bytes, 0x1C, bytes.length);
|
unknownData = Arrays.copyOfRange(bytes, 0x1C, bytes.length);
|
||||||
}
|
}
|
||||||
|
@ -52,7 +52,7 @@ public class FSAccessHeaderProvider {
|
||||||
public long getPermissionsBitmask() { return permissionsBitmask; }
|
public long getPermissionsBitmask() { return permissionsBitmask; }
|
||||||
public int getDataSize() { return dataSize; }
|
public int getDataSize() { return dataSize; }
|
||||||
public int getContentOwnIdSectionSize() { return contentOwnIdSectionSize; }
|
public int getContentOwnIdSectionSize() { return contentOwnIdSectionSize; }
|
||||||
public int getDataNownerSizes() { return dataOwnerSizes; }
|
public int getDataNownerSizes() { return dataNownerSizes; }
|
||||||
public int getSaveDataOwnSectionSize() { return saveDataOwnSectionSize; }
|
public int getSaveDataOwnSectionSize() { return saveDataOwnSectionSize; }
|
||||||
public byte[] getUnknownData() { return unknownData; }
|
public byte[] getUnknownData() { return unknownData; }
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,26 +32,26 @@ import static libKonogonka.Converter.*;
|
||||||
|
|
||||||
public class NPDMProvider extends ASuperInFileProvider {
|
public class NPDMProvider extends ASuperInFileProvider {
|
||||||
|
|
||||||
private final String magicNum;
|
private String magicNum;
|
||||||
private final byte[] reserved1;
|
private byte[] reserved1;
|
||||||
private final byte MMUFlags;
|
private byte MMUFlags;
|
||||||
private final byte reserved2;
|
private byte reserved2;
|
||||||
private final byte mainThreadPrio;
|
private byte mainThreadPrio;
|
||||||
private final byte mainThreadCoreNum;
|
private byte mainThreadCoreNum;
|
||||||
private final byte[] reserved3;
|
private byte[] reserved3;
|
||||||
private final int personalMmHeapSize; // safe-to-store
|
private int personalMmHeapSize; // safe-to-store
|
||||||
private final int version; // safe?
|
private int version; // safe?
|
||||||
private final long mainThreadStackSize; // TODO: check if safe
|
private long mainThreadStackSize; // TODO: check if safe
|
||||||
private final String titleName;
|
private String titleName;
|
||||||
private final byte[] productCode;
|
private byte[] productCode;
|
||||||
private final byte[] reserved4;
|
private byte[] reserved4;
|
||||||
private final int aci0offset; // originally 4-bytes (u-int)
|
private int aci0offset; // originally 4-bytes (u-int)
|
||||||
private final int aci0size; // originally 4-bytes (u-int)
|
private int aci0size; // originally 4-bytes (u-int)
|
||||||
private final int acidOffset; // originally 4-bytes (u-int)
|
private int acidOffset; // originally 4-bytes (u-int)
|
||||||
private final int acidSize; // originally 4-bytes (u-int)
|
private int acidSize; // originally 4-bytes (u-int)
|
||||||
|
|
||||||
private final ACI0Provider aci0;
|
private ACI0Provider aci0;
|
||||||
private final ACIDProvider acid;
|
private ACIDProvider acid;
|
||||||
|
|
||||||
public NPDMProvider(PipedInputStream pis) throws Exception{
|
public NPDMProvider(PipedInputStream pis) throws Exception{
|
||||||
byte[] mainBuf = new byte[0x80];
|
byte[] mainBuf = new byte[0x80];
|
||||||
|
|
|
@ -1,204 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2018-2022 Dmitry Isaenko
|
|
||||||
|
|
||||||
This file is part of libKonogonka.
|
|
||||||
|
|
||||||
libKonogonka is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
libKonogonka is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with libKonogonka. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package libKonogonka.Tools.NSO;
|
|
||||||
|
|
||||||
import libKonogonka.Converter;
|
|
||||||
import libKonogonka.RainbowDump;
|
|
||||||
import libKonogonka.ctraes.InFileStreamProducer;
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import static libKonogonka.Converter.getLEint;
|
|
||||||
|
|
||||||
public class NSO0Provider {
|
|
||||||
private final static Logger log = LogManager.getLogger(NSO0Provider.class);
|
|
||||||
|
|
||||||
private final InFileStreamProducer producer;
|
|
||||||
|
|
||||||
private String magic;
|
|
||||||
private int version;
|
|
||||||
private byte[] upperReserved;
|
|
||||||
private int flags;
|
|
||||||
private SegmentHeader textSegmentHeader;
|
|
||||||
private int moduleNameOffset; // note: could be unsigned int; consider 'long'
|
|
||||||
private SegmentHeader rodataSegmentHeader;
|
|
||||||
private int moduleNameSize;
|
|
||||||
private SegmentHeader dataSegmentHeader;
|
|
||||||
private int bssSize; // note: could be unsigned int; consider 'long'
|
|
||||||
private byte[] moduleId;
|
|
||||||
private int textCompressedSize;
|
|
||||||
private int rodataCompressedSize;
|
|
||||||
private int dataCompressedSize;
|
|
||||||
private byte[] bottomReserved;
|
|
||||||
private SegmentHeaderRelative _api_infoRelative;
|
|
||||||
private SegmentHeaderRelative _dynstrRelative;
|
|
||||||
private SegmentHeaderRelative _dynsymRelative;
|
|
||||||
private byte[] textHash;
|
|
||||||
private byte[] rodataHash;
|
|
||||||
private byte[] dataHash;
|
|
||||||
|
|
||||||
/* Encrypted */
|
|
||||||
public NSO0Provider(InFileStreamProducer producer) throws Exception {
|
|
||||||
this.producer = producer;
|
|
||||||
try (BufferedInputStream stream = producer.produce()){
|
|
||||||
byte[] knownStartingBytes = new byte[0x100];
|
|
||||||
if (0x100 != stream.read(knownStartingBytes))
|
|
||||||
throw new Exception("Reading stream suddenly ended while trying to read starting 0x100 bytes");
|
|
||||||
parse(knownStartingBytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Not Encrypted */
|
|
||||||
public NSO0Provider(File file) throws Exception{
|
|
||||||
this(file, 0);
|
|
||||||
}
|
|
||||||
public NSO0Provider(File file, long offsetPosition) throws Exception {
|
|
||||||
this.producer = new InFileStreamProducer(file, offsetPosition);
|
|
||||||
try (BufferedInputStream stream = producer.produce()){
|
|
||||||
if (offsetPosition != stream.skip(offsetPosition))
|
|
||||||
throw new Exception("Can't skip bytes prior NSO0 offset");
|
|
||||||
|
|
||||||
byte[] knownStartingBytes = new byte[0x100];
|
|
||||||
if (0x100 != stream.read(knownStartingBytes))
|
|
||||||
throw new Exception("Reading stream suddenly ended while trying to read starting 0x100 bytes");
|
|
||||||
parse(knownStartingBytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private void parse(byte[] knownStartingBytes) throws Exception{
|
|
||||||
this.magic = new String(knownStartingBytes, 0x0, 0x4, StandardCharsets.US_ASCII);
|
|
||||||
if (! magic.equals("NSO0")){
|
|
||||||
throw new Exception("Bad magic");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.version = getLEint(knownStartingBytes, 0x4);
|
|
||||||
this.upperReserved = Arrays.copyOfRange(knownStartingBytes, 0x8, 0xC);
|
|
||||||
this.flags = getLEint(knownStartingBytes, 0xC);
|
|
||||||
this.textSegmentHeader = new SegmentHeader(knownStartingBytes, 0x10);
|
|
||||||
this.moduleNameOffset = Converter.getLEint(knownStartingBytes, 0x1C);
|
|
||||||
this.rodataSegmentHeader = new SegmentHeader(knownStartingBytes, 0x20);
|
|
||||||
this.moduleNameSize = Converter.getLEint(knownStartingBytes, 0x2C);
|
|
||||||
this.dataSegmentHeader = new SegmentHeader(knownStartingBytes, 0x30);
|
|
||||||
this.bssSize = Converter.getLEint(knownStartingBytes, 0x3C);
|
|
||||||
this.moduleId = Arrays.copyOfRange(knownStartingBytes, 0x40, 0x60);
|
|
||||||
this.textCompressedSize = Converter.getLEint(knownStartingBytes, 0x60);
|
|
||||||
this.rodataCompressedSize = Converter.getLEint(knownStartingBytes, 0x64);
|
|
||||||
this.dataCompressedSize = Converter.getLEint(knownStartingBytes, 0x68);
|
|
||||||
this.bottomReserved = Arrays.copyOfRange(knownStartingBytes, 0x6C, 0x88);
|
|
||||||
this._api_infoRelative = new SegmentHeaderRelative(knownStartingBytes, 0x88);
|
|
||||||
this._dynstrRelative = new SegmentHeaderRelative(knownStartingBytes, 0x90);
|
|
||||||
this._dynsymRelative = new SegmentHeaderRelative(knownStartingBytes, 0x98);
|
|
||||||
this.textHash = Arrays.copyOfRange(knownStartingBytes, 0xA0, 0xC0);
|
|
||||||
this.rodataHash = Arrays.copyOfRange(knownStartingBytes, 0xC0, 0xE0);
|
|
||||||
this.dataHash = Arrays.copyOfRange(knownStartingBytes, 0xE0, 0x100);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void exportAsDecompressedNSO0(String saveToLocation) throws Exception{
|
|
||||||
NSO0Unpacker.unpack(this, producer, saveToLocation);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* API */
|
|
||||||
public String getMagic() { return magic; }
|
|
||||||
public int getVersion() {return version; }
|
|
||||||
public byte[] getUpperReserved() { return upperReserved; }
|
|
||||||
public int getFlags() { return flags; }
|
|
||||||
public boolean isTextCompressFlag(){ return (flags & 0b000001) == 1; }
|
|
||||||
public boolean isRoCompressFlag(){ return (flags & 0b000010) >> 1 == 1; }
|
|
||||||
public boolean isDataCompressFlag(){ return (flags & 0b000100 ) >> 2 == 1; }
|
|
||||||
public boolean isTextHashFlag(){ return (flags & 0b001000 ) >> 3 == 1; }
|
|
||||||
public boolean isRoHashFlag(){ return (flags & 0b010000 ) >> 4 == 1; }
|
|
||||||
public boolean isDataHashFlag(){ return (flags & 0b100000 ) >> 5 == 1; }
|
|
||||||
public SegmentHeader getTextSegmentHeader() { return textSegmentHeader; }
|
|
||||||
public int getModuleNameOffset() { return moduleNameOffset; }
|
|
||||||
public SegmentHeader getRodataSegmentHeader() { return rodataSegmentHeader; }
|
|
||||||
public int getModuleNameSize() { return moduleNameSize; }
|
|
||||||
public SegmentHeader getDataSegmentHeader() { return dataSegmentHeader; }
|
|
||||||
public int getBssSize() { return bssSize; }
|
|
||||||
public byte[] getModuleId() { return moduleId; }
|
|
||||||
public int getTextCompressedSize() { return textCompressedSize; }
|
|
||||||
public int getRodataCompressedSize() { return rodataCompressedSize; }
|
|
||||||
public int getDataCompressedSize() { return dataCompressedSize; }
|
|
||||||
public byte[] getBottomReserved() { return bottomReserved; }
|
|
||||||
public SegmentHeaderRelative get_api_infoRelative() { return _api_infoRelative; }
|
|
||||||
public SegmentHeaderRelative get_dynstrRelative() { return _dynstrRelative; }
|
|
||||||
public SegmentHeaderRelative get_dynsymRelative() { return _dynsymRelative; }
|
|
||||||
public byte[] getTextHash() { return textHash; }
|
|
||||||
public byte[] getRodataHash() { return rodataHash; }
|
|
||||||
public byte[] getDataHash() { return dataHash; }
|
|
||||||
|
|
||||||
public void printDebug(){
|
|
||||||
log.debug(".:: NSO0 Provider ::.\n" +
|
|
||||||
" ============================================================= \n" +
|
|
||||||
"Magic \"NSO0\" " + magic + "\n" +
|
|
||||||
"Version (always 0) " + version + "\n" +
|
|
||||||
"Reserved " + Converter.byteArrToHexString(upperReserved) + "\n" +
|
|
||||||
"Flags " + Converter.intToBinaryString(flags) + "\n" +
|
|
||||||
" |- 0. .text Compress " + isTextCompressFlag() + "\n" +
|
|
||||||
" |- 1. .rodata Compress " + isRoCompressFlag() + "\n" +
|
|
||||||
" |- 2. .data Compress " + isDataCompressFlag() + "\n" +
|
|
||||||
" |- 3. .text Hash " + isTextHashFlag() + "\n" +
|
|
||||||
" |- 4. .rodata Hash " + isRoHashFlag() + "\n" +
|
|
||||||
" |- 5. .data Hash " + isDataHashFlag() + "\n" +
|
|
||||||
" +++\n"+
|
|
||||||
"SegmentHeader for .text\n" +
|
|
||||||
" |- File Offset - - - - - - - - - - - - - - "+ RainbowDump.formatDecHexString(textSegmentHeader.getSegmentOffset()) + "\n" +
|
|
||||||
" |- Memory Offset - - - - - - - - - - - - - "+ RainbowDump.formatDecHexString(textSegmentHeader.getMemoryOffset()) + "\n" +
|
|
||||||
" |- Size As Decompressed - - - - - - - - - "+ RainbowDump.formatDecHexString(textSegmentHeader.getSizeAsDecompressed()) + "\n" +
|
|
||||||
"ModuleNameOffset (calculated by sizeof(header)) " + RainbowDump.formatDecHexString(moduleNameOffset) + "\n" +
|
|
||||||
" +++\n"+
|
|
||||||
"SegmentHeader for .rodata\n" +
|
|
||||||
" |- File Offset - - - - - - - - - - - - - - " + RainbowDump.formatDecHexString(rodataSegmentHeader.getSegmentOffset()) + "\n" +
|
|
||||||
" |- Memory Offset - - - - - - - - - - - - - " + RainbowDump.formatDecHexString(rodataSegmentHeader.getMemoryOffset()) + "\n" +
|
|
||||||
" |- Size As Decompressed - - - - - - - - - " + RainbowDump.formatDecHexString(rodataSegmentHeader.getSizeAsDecompressed()) + "\n" +
|
|
||||||
"ModuleNameSize " + RainbowDump.formatDecHexString(moduleNameSize) + "\n" +
|
|
||||||
" +++\n"+
|
|
||||||
"SegmentHeader for .data\n" +
|
|
||||||
" |- File Offset - - - - - - - - - - - - - - " + RainbowDump.formatDecHexString(dataSegmentHeader.getSegmentOffset()) + "\n" +
|
|
||||||
" |- Memory Offset - - - - - - - - - - - - - " + RainbowDump.formatDecHexString(dataSegmentHeader.getMemoryOffset()) + "\n" +
|
|
||||||
" |- Size As Decompressed - - - - - - - - - " + RainbowDump.formatDecHexString(dataSegmentHeader.getSizeAsDecompressed()) + "\n" +
|
|
||||||
" .bss Size " + RainbowDump.formatDecHexString(bssSize) + "\n" + // Block Started by Symbol
|
|
||||||
"Module ID (aka Build ID) " + Converter.byteArrToHexString(moduleId) + "\n" +
|
|
||||||
" .text Size (compressed) " + RainbowDump.formatDecHexString(textCompressedSize) + "\n" +
|
|
||||||
" .rodata Size (compressed) " + RainbowDump.formatDecHexString(rodataCompressedSize) + "\n" +
|
|
||||||
" .data Size (compressed) " + RainbowDump.formatDecHexString(dataCompressedSize) + "\n" +
|
|
||||||
"Reserved " + Converter.byteArrToHexString(bottomReserved) + "\n" +
|
|
||||||
" xxx\n"+
|
|
||||||
"SegmentHeaderRelative for .api_info\n" +
|
|
||||||
" |- Offset - - - - - - - - - - - - - - - - " + RainbowDump.formatDecHexString(_api_infoRelative.getOffset()) + "\n" +
|
|
||||||
" |- Size - - - - - - - - - - - - - - - - - " + RainbowDump.formatDecHexString(_api_infoRelative.getSize()) + "\n" +
|
|
||||||
" xxx\n"+
|
|
||||||
"SegmentHeaderRelative for .dynstr\n" +
|
|
||||||
" |- Offset - - - - - - - - - - - - - - - - " + RainbowDump.formatDecHexString(_dynstrRelative.getOffset()) + "\n" +
|
|
||||||
" |- Size - - - - - - - - - - - - - - - - - " + RainbowDump.formatDecHexString(_dynstrRelative.getSize()) + "\n" +
|
|
||||||
" xxx\n"+
|
|
||||||
"SegmentHeaderRelative for .dynsym\n" +
|
|
||||||
" |- Offset - - - - - - - - - - - - - - - - " + RainbowDump.formatDecHexString(_dynsymRelative.getOffset()) + "\n" +
|
|
||||||
" |- Size - - - - - - - - - - - - - - - - - " + RainbowDump.formatDecHexString(_dynsymRelative.getSize()) + "\n" +
|
|
||||||
" xxx\n"+
|
|
||||||
".text decompressed' SHA-256 hash " + Converter.byteArrToHexString(textHash) + "\n" +
|
|
||||||
".rodata decompressed' SHA-256 hash " + Converter.byteArrToHexString(rodataHash) + "\n" +
|
|
||||||
".data decompressed' SHA-256 hash " + Converter.byteArrToHexString(dataHash) + "\n" +
|
|
||||||
" ============================================================= "
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,221 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2018-2022 Dmitry Isaenko
|
|
||||||
|
|
||||||
This file is part of libKonogonka.
|
|
||||||
|
|
||||||
libKonogonka is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
libKonogonka is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with libKonogonka. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package libKonogonka.Tools.NSO;
|
|
||||||
|
|
||||||
import libKonogonka.ctraes.InFileStreamProducer;
|
|
||||||
import net.jpountz.lz4.LZ4Factory;
|
|
||||||
import net.jpountz.lz4.LZ4SafeDecompressor;
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.RandomAccessFile;
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.ByteOrder;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
public class NSO0Unpacker {
|
|
||||||
private static final String DECOMPRESSED_FILE_NAME = "main_decompressed";
|
|
||||||
private final MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
|
||||||
|
|
||||||
private NSO0Provider provider;
|
|
||||||
private InFileStreamProducer producer;
|
|
||||||
|
|
||||||
private byte[] _textDecompressedSection;
|
|
||||||
private byte[] _rodataDecompressedSection;
|
|
||||||
private byte[] _dataDecompressedSection;
|
|
||||||
private byte[] header;
|
|
||||||
private int textFileOffsetNew;
|
|
||||||
private int rodataFileOffsetNew;
|
|
||||||
private int dataFileOffsetNew;
|
|
||||||
|
|
||||||
private NSO0Unpacker() throws NoSuchAlgorithmException { }
|
|
||||||
|
|
||||||
public static void unpack(NSO0Provider provider, InFileStreamProducer producer, String saveToLocation) throws Exception{
|
|
||||||
if (! provider.isTextCompressFlag() && ! provider.isRoCompressFlag() && ! provider.isDataCompressFlag())
|
|
||||||
throw new Exception("This file is not compressed");
|
|
||||||
|
|
||||||
NSO0Unpacker instance = new NSO0Unpacker();
|
|
||||||
instance.provider = provider;
|
|
||||||
instance.producer = producer;
|
|
||||||
|
|
||||||
instance.decompressSections();
|
|
||||||
instance.validateHashes();
|
|
||||||
instance.makeHeader();
|
|
||||||
instance.writeFile(saveToLocation);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void decompressSections() throws Exception{
|
|
||||||
decompressTextSection();
|
|
||||||
decompressRodataSection();
|
|
||||||
decompressDataSection();
|
|
||||||
}
|
|
||||||
private void decompressTextSection() throws Exception{
|
|
||||||
if (provider.isTextCompressFlag())
|
|
||||||
_textDecompressedSection = decompressSection(provider.getTextSegmentHeader(), provider.getTextCompressedSize());
|
|
||||||
else
|
|
||||||
_textDecompressedSection = duplicateSection(provider.getTextSegmentHeader());
|
|
||||||
}
|
|
||||||
private void decompressRodataSection() throws Exception{
|
|
||||||
if (provider.isRoCompressFlag())
|
|
||||||
_rodataDecompressedSection = decompressSection(provider.getRodataSegmentHeader(), provider.getRodataCompressedSize());
|
|
||||||
else
|
|
||||||
_rodataDecompressedSection = duplicateSection(provider.getRodataSegmentHeader());
|
|
||||||
}
|
|
||||||
private void decompressDataSection() throws Exception{
|
|
||||||
if (provider.isDataCompressFlag())
|
|
||||||
_dataDecompressedSection = decompressSection(provider.getDataSegmentHeader(), provider.getDataCompressedSize());
|
|
||||||
else
|
|
||||||
_dataDecompressedSection = duplicateSection(provider.getDataSegmentHeader());
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] decompressSection(SegmentHeader segmentHeader, int compressedSectionSize) throws Exception{
|
|
||||||
try (BufferedInputStream stream = producer.produce()) {
|
|
||||||
int sectionDecompressedSize = segmentHeader.getSizeAsDecompressed();
|
|
||||||
|
|
||||||
byte[] compressed = new byte[compressedSectionSize];
|
|
||||||
if (segmentHeader.getSegmentOffset() != stream.skip(segmentHeader.getSegmentOffset()))
|
|
||||||
throw new Exception("Failed to skip " + segmentHeader.getSegmentOffset() + " bytes till section");
|
|
||||||
|
|
||||||
if (compressedSectionSize != stream.read(compressed))
|
|
||||||
throw new Exception("Failed to read entire section");
|
|
||||||
|
|
||||||
LZ4Factory factory = LZ4Factory.fastestInstance();
|
|
||||||
LZ4SafeDecompressor decompressor = factory.safeDecompressor();
|
|
||||||
byte[] restored = new byte[sectionDecompressedSize];
|
|
||||||
int decompressedLength = decompressor.decompress(compressed, 0, compressedSectionSize, restored, 0);
|
|
||||||
|
|
||||||
if (decompressedLength != sectionDecompressedSize)
|
|
||||||
throw new Exception("Decompression failure. Expected vs. actual decompressed sizes mismatch: " +
|
|
||||||
decompressedLength + " / " + sectionDecompressedSize);
|
|
||||||
return restored;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] duplicateSection(SegmentHeader segmentHeader) throws Exception{
|
|
||||||
try (BufferedInputStream stream = producer.produce()) {
|
|
||||||
int size = segmentHeader.getSizeAsDecompressed();
|
|
||||||
|
|
||||||
byte[] sectionContent = new byte[size];
|
|
||||||
if (segmentHeader.getSegmentOffset() != stream.skip(segmentHeader.getSegmentOffset()))
|
|
||||||
throw new Exception("Failed to skip " + segmentHeader.getSegmentOffset() + " bytes till section");
|
|
||||||
|
|
||||||
if (size != stream.read(sectionContent))
|
|
||||||
throw new Exception("Failed to read entire section");
|
|
||||||
|
|
||||||
return sectionContent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void validateHashes() throws Exception{
|
|
||||||
if ( ! Arrays.equals(provider.getTextHash(), digest.digest(_textDecompressedSection)) ) {
|
|
||||||
throw new Exception(".text hash mismatch for .text section");
|
|
||||||
}
|
|
||||||
if ( ! Arrays.equals(provider.getRodataHash(), digest.digest(_rodataDecompressedSection)) ) {
|
|
||||||
throw new Exception(".rodata hash mismatch for .text section");
|
|
||||||
}
|
|
||||||
if ( ! Arrays.equals(provider.getDataHash(), digest.digest(_dataDecompressedSection)) ) {
|
|
||||||
throw new Exception(".data hash mismatch for .text section");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void makeHeader() throws Exception{
|
|
||||||
try (BufferedInputStream stream = producer.produce()) {
|
|
||||||
byte[] headerBytes = new byte[0x100];
|
|
||||||
|
|
||||||
if (0x100 != stream.read(headerBytes))
|
|
||||||
throw new Exception("Unable to read initial 0x100 bytes needed for export.");
|
|
||||||
/* Simplified for computing however made hard for updating. Consider removing.
|
|
||||||
ByteBuffer resultingHeader = ByteBuffer.wrap(headerBytes).order(ByteOrder.LITTLE_ENDIAN);
|
|
||||||
((Buffer) resultingHeader).position(0xC);
|
|
||||||
resultingHeader.putInt(provider.getFlags() & 0b111000)
|
|
||||||
.putInt(provider.getTextSegmentHeader().getMemoryOffset()+0x100);
|
|
||||||
((Buffer) resultingHeader).position(0x1C);
|
|
||||||
resultingHeader.putInt(0x100)
|
|
||||||
.putInt(provider.getRodataSegmentHeader().getMemoryOffset()+0x100);
|
|
||||||
((Buffer) resultingHeader).position(0x2C);
|
|
||||||
resultingHeader.putInt(0)
|
|
||||||
.putInt(provider.getDataSegmentHeader().getMemoryOffset()+0x100);
|
|
||||||
((Buffer) resultingHeader).position(0x60);
|
|
||||||
resultingHeader.putInt(provider.getTextSegmentHeader().getSizeAsDecompressed())
|
|
||||||
.putInt(provider.getRodataSegmentHeader().getSizeAsDecompressed())
|
|
||||||
.putInt(provider.getDataSegmentHeader().getSizeAsDecompressed());
|
|
||||||
((Buffer) resultingHeader).flip();
|
|
||||||
|
|
||||||
header = resultingHeader.array();
|
|
||||||
*/
|
|
||||||
|
|
||||||
textFileOffsetNew = provider.getTextSegmentHeader().getMemoryOffset()+0x100;
|
|
||||||
rodataFileOffsetNew = provider.getRodataSegmentHeader().getMemoryOffset()+0x100;
|
|
||||||
dataFileOffsetNew = provider.getDataSegmentHeader().getMemoryOffset()+0x100;
|
|
||||||
|
|
||||||
ByteBuffer resultingHeader = ByteBuffer.allocate(0x100).order(ByteOrder.LITTLE_ENDIAN);
|
|
||||||
resultingHeader.put("NSO0".getBytes(StandardCharsets.US_ASCII))
|
|
||||||
.putInt(provider.getVersion())
|
|
||||||
.put(provider.getUpperReserved())
|
|
||||||
.putInt(provider.getFlags() & 0b111000)
|
|
||||||
.putInt(textFileOffsetNew)
|
|
||||||
.putInt(provider.getTextSegmentHeader().getMemoryOffset())
|
|
||||||
.putInt(provider.getTextSegmentHeader().getSizeAsDecompressed())
|
|
||||||
.putInt(0x100)
|
|
||||||
.putInt(rodataFileOffsetNew)
|
|
||||||
.putInt(provider.getRodataSegmentHeader().getMemoryOffset())
|
|
||||||
.putInt(provider.getRodataSegmentHeader().getSizeAsDecompressed())
|
|
||||||
.putInt(0)
|
|
||||||
.putInt(dataFileOffsetNew)
|
|
||||||
.putInt(provider.getDataSegmentHeader().getMemoryOffset())
|
|
||||||
.putInt(provider.getDataSegmentHeader().getSizeAsDecompressed())
|
|
||||||
.putInt(provider.getBssSize())
|
|
||||||
.put(provider.getModuleId())
|
|
||||||
.putInt(provider.getTextSegmentHeader().getSizeAsDecompressed())
|
|
||||||
.putInt(provider.getRodataSegmentHeader().getSizeAsDecompressed())
|
|
||||||
.putInt(provider.getDataSegmentHeader().getSizeAsDecompressed())
|
|
||||||
.put(provider.getBottomReserved())
|
|
||||||
.putInt(provider.get_api_infoRelative().getOffset())
|
|
||||||
.putInt(provider.get_api_infoRelative().getSize())
|
|
||||||
.putInt(provider.get_dynstrRelative().getOffset())
|
|
||||||
.putInt(provider.get_dynstrRelative().getSize())
|
|
||||||
.putInt(provider.get_dynsymRelative().getOffset())
|
|
||||||
.putInt(provider.get_dynsymRelative().getSize())
|
|
||||||
.put(provider.getTextHash())
|
|
||||||
.put(provider.getRodataHash())
|
|
||||||
.put(provider.getDataHash());
|
|
||||||
|
|
||||||
header = resultingHeader.array();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeFile(String saveToLocation) throws Exception{
|
|
||||||
File location = new File(saveToLocation);
|
|
||||||
location.mkdirs();
|
|
||||||
|
|
||||||
try (RandomAccessFile raf = new RandomAccessFile(saveToLocation+File.separator+DECOMPRESSED_FILE_NAME,
|
|
||||||
"rw")){
|
|
||||||
raf.write(header);
|
|
||||||
raf.seek(textFileOffsetNew);
|
|
||||||
raf.write(_textDecompressedSection);
|
|
||||||
raf.seek(rodataFileOffsetNew);
|
|
||||||
raf.write(_rodataDecompressedSection);
|
|
||||||
raf.seek(dataFileOffsetNew);
|
|
||||||
raf.write(_dataDecompressedSection);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2018-2022 Dmitry Isaenko
|
|
||||||
|
|
||||||
This file is part of libKonogonka.
|
|
||||||
|
|
||||||
libKonogonka is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
libKonogonka is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with libKonogonka. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package libKonogonka.Tools.NSO;
|
|
||||||
|
|
||||||
import libKonogonka.Converter;
|
|
||||||
|
|
||||||
public class SegmentHeader {
|
|
||||||
private final int segmentOffset;
|
|
||||||
private final int memoryOffset;
|
|
||||||
private final int sizeAsDecompressed;
|
|
||||||
|
|
||||||
SegmentHeader(byte[] data){
|
|
||||||
this(data, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
SegmentHeader(byte[] data, int fromOffset){
|
|
||||||
this.segmentOffset = Converter.getLEint(data, fromOffset);
|
|
||||||
this.memoryOffset = Converter.getLEint(data, fromOffset+4);
|
|
||||||
this.sizeAsDecompressed = Converter.getLEint(data, fromOffset+8);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getSegmentOffset() {
|
|
||||||
return segmentOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMemoryOffset() {
|
|
||||||
return memoryOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getSizeAsDecompressed() {
|
|
||||||
return sizeAsDecompressed;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2018-2022 Dmitry Isaenko
|
|
||||||
|
|
||||||
This file is part of libKonogonka.
|
|
||||||
|
|
||||||
libKonogonka is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
libKonogonka is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with libKonogonka. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package libKonogonka.Tools.NSO;
|
|
||||||
|
|
||||||
import libKonogonka.Converter;
|
|
||||||
|
|
||||||
public class SegmentHeaderRelative {
|
|
||||||
private final int offset;
|
|
||||||
private final int size;
|
|
||||||
|
|
||||||
SegmentHeaderRelative(byte[] data){
|
|
||||||
this(data, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
SegmentHeaderRelative(byte[] data, int fromOffset){
|
|
||||||
this.offset = Converter.getLEint(data, fromOffset);
|
|
||||||
this.size = Converter.getLEint(data, fromOffset+4);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getOffset() {
|
|
||||||
return offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getSize() {
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -19,9 +19,7 @@
|
||||||
package libKonogonka.Tools.PFS0;
|
package libKonogonka.Tools.PFS0;
|
||||||
|
|
||||||
import libKonogonka.Tools.ISuperProvider;
|
import libKonogonka.Tools.ISuperProvider;
|
||||||
import libKonogonka.ctraes.InFileStreamProducer;
|
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
|
||||||
public interface IPFS0Provider extends ISuperProvider {
|
public interface IPFS0Provider extends ISuperProvider {
|
||||||
|
@ -34,10 +32,5 @@ public interface IPFS0Provider extends ISuperProvider {
|
||||||
PFS0subFile[] getPfs0subFiles();
|
PFS0subFile[] getPfs0subFiles();
|
||||||
|
|
||||||
void printDebug();
|
void printDebug();
|
||||||
|
|
||||||
InFileStreamProducer getStreamProducer(String subFileName) throws FileNotFoundException;
|
|
||||||
|
|
||||||
InFileStreamProducer getStreamProducer(int subFileNumber);
|
|
||||||
|
|
||||||
LinkedList<byte[]> getPfs0SHA256hashes();
|
LinkedList<byte[]> getPfs0SHA256hashes();
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,6 @@ import libKonogonka.RainbowDump;
|
||||||
import libKonogonka.Tools.NCA.NCASectionTableBlock.SuperBlockPFS0;
|
import libKonogonka.Tools.NCA.NCASectionTableBlock.SuperBlockPFS0;
|
||||||
import libKonogonka.ctraes.AesCtrBufferedInputStream;
|
import libKonogonka.ctraes.AesCtrBufferedInputStream;
|
||||||
import libKonogonka.ctraes.AesCtrDecryptSimple;
|
import libKonogonka.ctraes.AesCtrDecryptSimple;
|
||||||
import libKonogonka.ctraes.InFileStreamProducer;
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
@ -295,31 +294,6 @@ public class PFS0Provider implements IPFS0Provider{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public InFileStreamProducer getStreamProducer(String subFileName) throws FileNotFoundException {
|
|
||||||
for (int i = 0; i < pfs0subFiles.length; i++) {
|
|
||||||
if (pfs0subFiles[i].getName().equals(subFileName))
|
|
||||||
return getStreamProducer(i);
|
|
||||||
}
|
|
||||||
throw new FileNotFoundException("No file with such name exists: "+subFileName);
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public InFileStreamProducer getStreamProducer(int subFileNumber) {
|
|
||||||
PFS0subFile subFile = pfs0subFiles[subFileNumber];
|
|
||||||
long subFileOffset = subFile.getOffset() + mediaStartOffset * 0x200 + rawBlockDataStart;
|
|
||||||
|
|
||||||
if (encrypted) {
|
|
||||||
return new InFileStreamProducer(file,
|
|
||||||
pfs0subFiles[subFileNumber].getSize(),
|
|
||||||
ncaOffset,
|
|
||||||
subFileOffset,
|
|
||||||
decryptor,
|
|
||||||
mediaStartOffset,
|
|
||||||
mediaEndOffset);
|
|
||||||
}
|
|
||||||
return new InFileStreamProducer(file, offsetPositionInFile, subFileOffset);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @deprecated
|
* @deprecated
|
||||||
* */
|
* */
|
||||||
|
|
|
@ -1,119 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2018-2022 Dmitry Isaenko
|
|
||||||
|
|
||||||
This file is part of libKonogonka.
|
|
||||||
|
|
||||||
libKonogonka is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
libKonogonka is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with libKonogonka. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package libKonogonka.ctraes;
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
|
|
||||||
// TODO: rework, simplify
|
|
||||||
public class InFileStreamProducer {
|
|
||||||
private final boolean encrypted;
|
|
||||||
private final long size;
|
|
||||||
|
|
||||||
private final File file;
|
|
||||||
private final long initialOffset;
|
|
||||||
private long subOffset;
|
|
||||||
private AesCtrDecryptSimple decryptor;
|
|
||||||
private long mediaStartOffset;
|
|
||||||
private long mediaEndOffset;
|
|
||||||
|
|
||||||
public InFileStreamProducer(
|
|
||||||
File file,
|
|
||||||
long size,
|
|
||||||
long initialOffset,
|
|
||||||
long subOffset,
|
|
||||||
AesCtrDecryptSimple decryptor,
|
|
||||||
long mediaStartOffset,
|
|
||||||
long mediaEndOffset
|
|
||||||
){
|
|
||||||
this.encrypted = (decryptor != null);
|
|
||||||
this.file = file;
|
|
||||||
this.size = size;
|
|
||||||
this.initialOffset = initialOffset;
|
|
||||||
this.subOffset = subOffset;
|
|
||||||
this.decryptor = decryptor;
|
|
||||||
this.mediaStartOffset = mediaStartOffset;
|
|
||||||
this.mediaEndOffset = mediaEndOffset;
|
|
||||||
}
|
|
||||||
public InFileStreamProducer(File file, long size, long initialOffset, long subOffset){
|
|
||||||
this.encrypted = false;
|
|
||||||
this.file = file;
|
|
||||||
this.size = size;
|
|
||||||
this.initialOffset = 0;
|
|
||||||
this.subOffset = initialOffset+subOffset;
|
|
||||||
}
|
|
||||||
public InFileStreamProducer(File file, long size){
|
|
||||||
this.encrypted = false;
|
|
||||||
this.file = file;
|
|
||||||
this.size = size;
|
|
||||||
this.initialOffset = 0;
|
|
||||||
this.subOffset = 0;
|
|
||||||
}
|
|
||||||
public InFileStreamProducer(File file, long size, long subOffset){
|
|
||||||
this.encrypted = false;
|
|
||||||
this.file = file;
|
|
||||||
this.size = size;
|
|
||||||
this.initialOffset = 0;
|
|
||||||
this.subOffset = subOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BufferedInputStream produce() throws Exception{
|
|
||||||
if (encrypted) {
|
|
||||||
return produceAesCtr();
|
|
||||||
}
|
|
||||||
return produceNotEncrypted();
|
|
||||||
}
|
|
||||||
private AesCtrBufferedInputStream produceAesCtr() throws Exception{
|
|
||||||
decryptor.reset();
|
|
||||||
AesCtrBufferedInputStream stream = new AesCtrBufferedInputStream(
|
|
||||||
decryptor,
|
|
||||||
initialOffset,
|
|
||||||
mediaStartOffset,
|
|
||||||
mediaEndOffset,
|
|
||||||
Files.newInputStream(file.toPath()));
|
|
||||||
if (subOffset != stream.skip(subOffset))
|
|
||||||
throw new Exception("Unable to skip offset: " + subOffset);
|
|
||||||
return stream;
|
|
||||||
}
|
|
||||||
private BufferedInputStream produceNotEncrypted() throws Exception{
|
|
||||||
BufferedInputStream stream = new BufferedInputStream(Files.newInputStream(file.toPath()));
|
|
||||||
if (subOffset != stream.skip(subOffset))
|
|
||||||
throw new Exception("Unable to skip offset: " + subOffset);
|
|
||||||
return stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
public InFileStreamProducer getSuccessor(long subOffset, long size){
|
|
||||||
this.subOffset = subOffset;
|
|
||||||
return new InFileStreamProducer(file, size, initialOffset, subOffset, decryptor, mediaStartOffset, mediaEndOffset);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEncrypted() {
|
|
||||||
return encrypted;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getSize() {
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString(){
|
|
||||||
return file.getName();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2018-2022 Dmitry Isaenko
|
|
||||||
|
|
||||||
This file is part of libKonogonka.
|
|
||||||
|
|
||||||
libKonogonka is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
libKonogonka is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with libKonogonka. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package libKonogonka.RomFsDecrypted;
|
|
||||||
|
|
||||||
import libKonogonka.Tools.NSO.NSO0Provider;
|
|
||||||
import org.junit.jupiter.api.Disabled;
|
|
||||||
import org.junit.jupiter.api.DisplayName;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
|
|
||||||
public class NSODecompressTest {
|
|
||||||
private static final String ncaExtractedFileLocation = "./FilesForTests/NSO0/main";
|
|
||||||
private static final String ncaExtractedFileLocationDec = "./FilesForTests/NSO0/main_d";
|
|
||||||
|
|
||||||
@Disabled
|
|
||||||
@DisplayName("NSO0 Decompression test")
|
|
||||||
@Test
|
|
||||||
void nso0DecompressionTest() throws Exception {
|
|
||||||
NSO0Provider nso0Provider = new NSO0Provider(new File(ncaExtractedFileLocation));
|
|
||||||
//nso0Provider.exportAsDecompressedNSO0("./FilesForTests/NSO0");
|
|
||||||
nso0Provider.printDebug();
|
|
||||||
|
|
||||||
NSO0Provider nso0Provider1 = new NSO0Provider(new File(ncaExtractedFileLocationDec));
|
|
||||||
nso0Provider1.printDebug();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,129 +0,0 @@
|
||||||
/*
|
|
||||||
Copyright 2018-2022 Dmitry Isaenko
|
|
||||||
|
|
||||||
This file is part of libKonogonka.
|
|
||||||
|
|
||||||
libKonogonka is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
libKonogonka is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
along with libKonogonka. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package libKonogonka.RomFsDecrypted;
|
|
||||||
|
|
||||||
import libKonogonka.KeyChainHolder;
|
|
||||||
import libKonogonka.RainbowDump;
|
|
||||||
import libKonogonka.Tools.NCA.NCAProvider;
|
|
||||||
import libKonogonka.Tools.NSO.NSO0Provider;
|
|
||||||
import libKonogonka.Tools.PFS0.IPFS0Provider;
|
|
||||||
import libKonogonka.Tools.PFS0.PFS0subFile;
|
|
||||||
import libKonogonka.ctraes.AesCtrDecryptSimple;
|
|
||||||
import org.junit.jupiter.api.Disabled;
|
|
||||||
import org.junit.jupiter.api.DisplayName;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
|
|
||||||
public class NSOTest {
|
|
||||||
private static final String keysFileLocation = "./FilesForTests/prod.keys";
|
|
||||||
private static final String xci_header_keyFileLocation = "./FilesForTests/xci_header_key.txt";
|
|
||||||
private static final String ncaFileLocation = "./FilesForTests/nso_container.nca";
|
|
||||||
private static KeyChainHolder keyChainHolder;
|
|
||||||
private static NCAProvider ncaProvider;
|
|
||||||
|
|
||||||
|
|
||||||
@Disabled
|
|
||||||
@DisplayName("NSO0 test")
|
|
||||||
@Test
|
|
||||||
void nso0Test() throws Exception{
|
|
||||||
BufferedReader br = new BufferedReader(new FileReader(xci_header_keyFileLocation));
|
|
||||||
String keyValue = br.readLine();
|
|
||||||
br.close();
|
|
||||||
|
|
||||||
if (keyValue == null)
|
|
||||||
throw new Exception("Unable to retrieve xci_header_key");
|
|
||||||
|
|
||||||
keyValue = keyValue.trim();
|
|
||||||
keyChainHolder = new KeyChainHolder(keysFileLocation, keyValue);
|
|
||||||
|
|
||||||
ncaProvider = new NCAProvider(new File(ncaFileLocation), keyChainHolder.getRawKeySet());
|
|
||||||
|
|
||||||
pfs0Validation();
|
|
||||||
|
|
||||||
nso0Validation();
|
|
||||||
// AesCtrBufferedInputStreamTest();
|
|
||||||
}
|
|
||||||
|
|
||||||
void pfs0Validation() throws Exception{
|
|
||||||
for (byte i = 0; i < 4; i++){
|
|
||||||
System.out.println("..:: TEST SECTION #"+i+" ::..");
|
|
||||||
if (ncaProvider.getSectionBlock(i).getFsType() == 1 &&
|
|
||||||
ncaProvider.getSectionBlock(i).getHashType() == 2 &&
|
|
||||||
ncaProvider.getSectionBlock(i).getCryptoType() == 3){
|
|
||||||
ncaProvider.getNCAContentProvider(i).getPfs0().printDebug();
|
|
||||||
ncaProvider.getSectionBlock(i).printDebug();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
long ACBISoffsetPosition;
|
|
||||||
long ACBISmediaStartOffset;
|
|
||||||
long ACBISmediaEndOffset;
|
|
||||||
|
|
||||||
long offsetPosition;
|
|
||||||
|
|
||||||
|
|
||||||
void nso0Validation() throws Exception{
|
|
||||||
File nca = new File(ncaFileLocation);
|
|
||||||
PFS0subFile[] subfiles = ncaProvider.getNCAContentProvider(0).getPfs0().getPfs0subFiles();
|
|
||||||
|
|
||||||
offsetPosition = ncaProvider.getTableEntry0().getMediaStartOffset()*0x200 +
|
|
||||||
ncaProvider.getNCAContentProvider(0).getPfs0().getRawFileDataStart();
|
|
||||||
System.out.println("\t=============================================================");
|
|
||||||
System.out.println("\tNCA SIZE: "+ RainbowDump.formatDecHexString(nca.length()));
|
|
||||||
System.out.println("\tPFS0 Offset(get) "+RainbowDump.formatDecHexString(ncaProvider.getSectionBlock0().getSuperBlockPFS0().getPfs0offset()));
|
|
||||||
System.out.println("\tPFS0 MediaStart (* 0x200) "+RainbowDump.formatDecHexString(ncaProvider.getTableEntry0().getMediaStartOffset()*0x200));
|
|
||||||
System.out.println("\tPFS0 MediaEnd (* 0x200) "+RainbowDump.formatDecHexString(ncaProvider.getTableEntry0().getMediaEndOffset()*0x200));
|
|
||||||
System.out.println("\tPFS0 Offset+MediaBlockStart: "+RainbowDump.formatDecHexString(offsetPosition));
|
|
||||||
System.out.println("\tRAW Offset: "+RainbowDump.formatDecHexString(ncaProvider.getNCAContentProvider(0).getPfs0().getRawFileDataStart()));
|
|
||||||
System.out.println("\tHashTableSize: "+RainbowDump.formatDecHexString(ncaProvider.getSectionBlock0().getSuperBlockPFS0().getHashTableSize()));
|
|
||||||
for (PFS0subFile subFile : subfiles){
|
|
||||||
System.out.println("\n\tEntry Name: "+subFile.getName());
|
|
||||||
System.out.println("\tEntry Offset: "+RainbowDump.formatDecHexString(subFile.getOffset()));
|
|
||||||
System.out.println("\tEntry Size: "+RainbowDump.formatDecHexString(subFile.getSize()));
|
|
||||||
}
|
|
||||||
System.out.println("\t=============================================================");
|
|
||||||
|
|
||||||
ACBISoffsetPosition = 0;
|
|
||||||
ACBISmediaStartOffset = ncaProvider.getTableEntry0().getMediaStartOffset();
|
|
||||||
ACBISmediaEndOffset = ncaProvider.getTableEntry0().getMediaEndOffset();
|
|
||||||
|
|
||||||
AesCtrDecryptSimple decryptSimple = new AesCtrDecryptSimple(
|
|
||||||
ncaProvider.getDecryptedKey2(),
|
|
||||||
ncaProvider.getSectionBlock0().getSectionCTR(),
|
|
||||||
ncaProvider.getTableEntry0().getMediaStartOffset() * 0x200);
|
|
||||||
|
|
||||||
IPFS0Provider pfs0Provider = ncaProvider.getNCAContentProvider(0).getPfs0();
|
|
||||||
|
|
||||||
NSO0Provider nso0Provider = new NSO0Provider(pfs0Provider.getStreamProducer(0));
|
|
||||||
nso0Provider.printDebug();
|
|
||||||
|
|
||||||
// NPDMProvider npdmProvider = new NPDMProvider(pfs0Provider.getProviderSubFilePipedInpStream(1));
|
|
||||||
|
|
||||||
System.out.println("__--++ SDK VERSION ++--__\n"
|
|
||||||
+ncaProvider.getSdkVersion()[3]
|
|
||||||
+"."+ncaProvider.getSdkVersion()[2]
|
|
||||||
+"."+ncaProvider.getSdkVersion()[1]
|
|
||||||
+"."+ncaProvider.getSdkVersion()[0]);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue