Compare commits
	
		
			2 commits
		
	
	
		
			51398a6ea9
			...
			91ab6fd74b
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
							 | 
						91ab6fd74b | ||
| 
							 | 
						610aa5dd96 | 
					 16 changed files with 1091 additions and 41 deletions
				
			
		| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
# libKonogonka
 | 
					<img src="misc/logo.svg" title="" alt="libKonogonka" data-align="center">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 [](https://ci.redrise.ru/desu/libKonogonka)
 | 
					 [](https://ci.redrise.ru/desu/libKonogonka)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,10 +13,13 @@ 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
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										197
									
								
								misc/logo.svg
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,197 @@
 | 
				
			||||||
 | 
					<?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>
 | 
				
			||||||
| 
		 After 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://developersu.blogspot.com/</url>
 | 
					            <url>https://redrise.ru/</url>
 | 
				
			||||||
        </organization>
 | 
					        </organization>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <licenses>
 | 
					        <licenses>
 | 
				
			||||||
| 
						 | 
					@ -67,28 +67,33 @@
 | 
				
			||||||
            <dependency>
 | 
					            <dependency>
 | 
				
			||||||
                <groupId>org.apache.logging.log4j</groupId>
 | 
					                <groupId>org.apache.logging.log4j</groupId>
 | 
				
			||||||
                <artifactId>log4j-core</artifactId>
 | 
					                <artifactId>log4j-core</artifactId>
 | 
				
			||||||
                <version>2.18.0</version>
 | 
					                <version>2.19.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.5.2</version>
 | 
					                <version>5.9.0</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.5.2</version>
 | 
					                <version>5.9.0</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.5.2</version>
 | 
					                <version>5.9.0</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>
 | 
				
			||||||
| 
						 | 
					@ -111,7 +116,7 @@
 | 
				
			||||||
                        </execution>
 | 
					                        </execution>
 | 
				
			||||||
                    </executions>
 | 
					                    </executions>
 | 
				
			||||||
                </plugin>
 | 
					                </plugin>
 | 
				
			||||||
<!-- UNCOMMENT WHEN LEARN HOW TO MAKE NORMAL DOCS
 | 
					<!-- UNCOMMENT ONCE 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,6 +56,10 @@ 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();
 | 
					        stringBuilder = new StringBuilder(" -- RainbowDump --\n");
 | 
				
			||||||
        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 octDumpInt(int value){
 | 
					    public static void binDumpInt(int value){
 | 
				
			||||||
        System.out.println(String.format("%32s", Integer.toBinaryString( value )).replace(' ', '0')+" | "+value);
 | 
					        log.debug(Converter.intToBinaryString(value));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public static void octDumpLong(long value){
 | 
					    public static void binDumpLong(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 byte version;
 | 
					    private final byte version;
 | 
				
			||||||
    private byte[] padding;
 | 
					    private final byte[] padding;
 | 
				
			||||||
    private long permissionsBitmask;
 | 
					    private final long permissionsBitmask;
 | 
				
			||||||
    private int dataSize;
 | 
					    private final int dataSize;
 | 
				
			||||||
    private int contentOwnIdSectionSize;
 | 
					    private final int contentOwnIdSectionSize;
 | 
				
			||||||
    private int dataNownerSizes;
 | 
					    private final int dataOwnerSizes;
 | 
				
			||||||
    private int saveDataOwnSectionSize;
 | 
					    private final int saveDataOwnSectionSize;
 | 
				
			||||||
    private byte[] unknownData;
 | 
					    private final 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);
 | 
				
			||||||
        dataNownerSizes = Converter.getLEint(bytes, 0x14);
 | 
					        dataOwnerSizes = 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 dataNownerSizes; }
 | 
					    public int getDataNownerSizes() { return dataOwnerSizes; }
 | 
				
			||||||
    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 String magicNum;
 | 
					    private final String magicNum;
 | 
				
			||||||
    private byte[] reserved1;
 | 
					    private final byte[] reserved1;
 | 
				
			||||||
    private byte MMUFlags;
 | 
					    private final byte MMUFlags;
 | 
				
			||||||
    private byte reserved2;
 | 
					    private final byte reserved2;
 | 
				
			||||||
    private byte mainThreadPrio;
 | 
					    private final byte mainThreadPrio;
 | 
				
			||||||
    private byte mainThreadCoreNum;
 | 
					    private final byte mainThreadCoreNum;
 | 
				
			||||||
    private byte[] reserved3;
 | 
					    private final byte[] reserved3;
 | 
				
			||||||
    private int personalMmHeapSize;     // safe-to-store
 | 
					    private final int personalMmHeapSize;     // safe-to-store
 | 
				
			||||||
    private int version;                // safe?
 | 
					    private final int version;                // safe?
 | 
				
			||||||
    private long mainThreadStackSize;    // TODO: check if safe
 | 
					    private final long mainThreadStackSize;    // TODO: check if safe
 | 
				
			||||||
    private String titleName;
 | 
					    private final String titleName;
 | 
				
			||||||
    private byte[] productCode;
 | 
					    private final byte[] productCode;
 | 
				
			||||||
    private byte[] reserved4;
 | 
					    private final byte[] reserved4;
 | 
				
			||||||
    private int aci0offset;            // originally 4-bytes (u-int)
 | 
					    private final int aci0offset;            // originally 4-bytes (u-int)
 | 
				
			||||||
    private int aci0size;              // originally 4-bytes (u-int)
 | 
					    private final int aci0size;              // originally 4-bytes (u-int)
 | 
				
			||||||
    private int acidOffset;            // originally 4-bytes (u-int)
 | 
					    private final int acidOffset;            // originally 4-bytes (u-int)
 | 
				
			||||||
    private int acidSize;              // originally 4-bytes (u-int)
 | 
					    private final int acidSize;              // originally 4-bytes (u-int)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private ACI0Provider aci0;
 | 
					    private final ACI0Provider aci0;
 | 
				
			||||||
    private ACIDProvider acid;
 | 
					    private final ACIDProvider acid;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public NPDMProvider(PipedInputStream pis) throws Exception{
 | 
					    public NPDMProvider(PipedInputStream pis) throws Exception{
 | 
				
			||||||
        byte[] mainBuf = new byte[0x80];
 | 
					        byte[] mainBuf = new byte[0x80];
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										204
									
								
								src/main/java/libKonogonka/Tools/NSO/NSO0Provider.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										204
									
								
								src/main/java/libKonogonka/Tools/NSO/NSO0Provider.java
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,204 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					    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" +
 | 
				
			||||||
 | 
					                " ============================================================= "
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										221
									
								
								src/main/java/libKonogonka/Tools/NSO/NSO0Unpacker.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										221
									
								
								src/main/java/libKonogonka/Tools/NSO/NSO0Unpacker.java
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,221 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					    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);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										49
									
								
								src/main/java/libKonogonka/Tools/NSO/SegmentHeader.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/main/java/libKonogonka/Tools/NSO/SegmentHeader.java
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,49 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					    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;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,43 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					    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,7 +19,9 @@
 | 
				
			||||||
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 {
 | 
				
			||||||
| 
						 | 
					@ -32,5 +34,10 @@ 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,6 +23,7 @@ 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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -294,6 +295,31 @@ 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
 | 
				
			||||||
     * */
 | 
					     * */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										119
									
								
								src/main/java/libKonogonka/ctraes/InFileStreamProducer.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								src/main/java/libKonogonka/ctraes/InFileStreamProducer.java
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,119 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					    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();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,43 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					    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();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										129
									
								
								src/test/java/libKonogonka/RomFsDecrypted/NSOTest.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										129
									
								
								src/test/java/libKonogonka/RomFsDecrypted/NSOTest.java
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,129 @@
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					    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