Rework NSO0 related classes. Added option to pick one of the ELF's sections (.text, .data or .rodata) unpacked without pre-dumping it on disk.
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
Clean 'System.out.print*'
This commit is contained in:
parent
91ab6fd74b
commit
53af386738
15 changed files with 462 additions and 284 deletions
10
README.md
10
README.md
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
![License](https://img.shields.io/badge/License-GPLv3-blue.svg) [![Build Status](https://ci.redrise.ru/api/badges/desu/libKonogonka/status.svg)](https://ci.redrise.ru/desu/libKonogonka)
|
![License](https://img.shields.io/badge/License-GPLv3-blue.svg) [![Build Status](https://ci.redrise.ru/api/badges/desu/libKonogonka/status.svg)](https://ci.redrise.ru/desu/libKonogonka)
|
||||||
|
|
||||||
Library to work with NS-specific files / filesystem images. Ex-backend of [konogonka](https://github.com/developersu/konogonka) ([independent source location](https://git.redrise.ru/desu/konogonka))
|
Library made to work with NS-specific files / filesystem images. Separated backend of [konogonka](https://github.com/developersu/konogonka) ([independent source location](https://git.redrise.ru/desu/konogonka))
|
||||||
|
|
||||||
### Let's stay in touch
|
### Let's stay in touch
|
||||||
|
|
||||||
|
@ -20,8 +20,8 @@ You can get this application from independent source location: [https://git.redr
|
||||||
|
|
||||||
#### Thanks
|
#### Thanks
|
||||||
|
|
||||||
* Switch brew wiki
|
* [Switch brew](https://switchbrew.org) wiki
|
||||||
* Original ScriesM software
|
* Original [ScriesM software](https://github.com/SciresM/)
|
||||||
* 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!
|
||||||
|
|
||||||
### System requirements
|
### System requirements
|
||||||
|
@ -31,3 +31,7 @@ JRE/JDK 8u60 or higher.
|
||||||
### Build
|
### Build
|
||||||
|
|
||||||
See .drone.yml
|
See .drone.yml
|
||||||
|
|
||||||
|
### Install on local host (local maven repo)
|
||||||
|
|
||||||
|
`# mvn instal`
|
|
@ -25,13 +25,13 @@
|
||||||
inkscape:document-units="mm"
|
inkscape:document-units="mm"
|
||||||
showgrid="false"
|
showgrid="false"
|
||||||
inkscape:zoom="2.8284272"
|
inkscape:zoom="2.8284272"
|
||||||
inkscape:cx="378.30212"
|
inkscape:cx="378.65567"
|
||||||
inkscape:cy="70.5339"
|
inkscape:cy="71.06423"
|
||||||
inkscape:window-width="2266"
|
inkscape:window-width="3754"
|
||||||
inkscape:window-height="1414"
|
inkscape:window-height="2127"
|
||||||
inkscape:window-x="1305"
|
inkscape:window-x="1166"
|
||||||
inkscape:window-y="546"
|
inkscape:window-y="0"
|
||||||
inkscape:window-maximized="0"
|
inkscape:window-maximized="1"
|
||||||
inkscape:current-layer="layer1"
|
inkscape:current-layer="layer1"
|
||||||
showguides="false" />
|
showguides="false" />
|
||||||
<defs
|
<defs
|
||||||
|
@ -164,17 +164,59 @@
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke-width:0.495873"
|
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" />
|
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>
|
</g>
|
||||||
<text
|
<g
|
||||||
xml:space="preserve"
|
aria-label="libKonogonka"
|
||||||
style="font-size:10.5833px;line-height:1.25;font-family:Terminus;-inkscape-font-specification:'Terminus, Normal';stroke-width:0.264583;filter:url(#filter3747)"
|
id="text2788"
|
||||||
x="44.007172"
|
style="font-size:10.5833px;line-height:1.25;font-family:Terminus;-inkscape-font-specification:'Terminus, Normal';stroke-width:0.264583;filter:url(#filter3747)">
|
||||||
y="147.82162"
|
<path
|
||||||
id="text2788"><tspan
|
d="m 48.274372,147.82162 h -2.286 v -17.77999 h 2.286 z"
|
||||||
sodipodi:role="line"
|
style="font-size:25.4px;font-family:Play;-inkscape-font-specification:'Play, Normal';fill:#ffffff"
|
||||||
id="tspan2786"
|
id="path871" />
|
||||||
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"
|
<path
|
||||||
x="44.007172"
|
d="m 54.522779,133.47062 h -2.286 v -2.38759 h 2.286 z m 0,14.351 h -2.286 v -12.2936 h 2.286 z"
|
||||||
y="147.82162">libKonogonka</tspan></text>
|
style="font-size:25.4px;font-family:Play;-inkscape-font-specification:'Play, Normal';fill:#ffffff"
|
||||||
|
id="path873" />
|
||||||
|
<path
|
||||||
|
d="m 69.686586,138.75382 v 5.0546 q 0,1.1176 -0.3302,1.9304 -0.3302,0.8128 -0.8382,1.27 -0.4826,0.4318 -1.2954,0.6858 -0.8128,0.254 -1.524,0.3048 -0.7112,0.0762 -1.7272,0.0762 -1.3208,0 -2.159,-0.127 -0.8382,-0.127 -1.6764,-0.5334 -0.8382,-0.4064 -1.2446,-1.2954 -0.4064,-0.9144 -0.4064,-2.3114 v -13.76679 h 2.286 v 3.68299 q 0,0.4826 -0.0254,1.4732 0,0.9652 0,1.4478 0.1016,-0.5842 1.0414,-0.9652 0.9652,-0.4064 2.8194,-0.4064 5.08,0 5.08,3.4798 z m -2.3368,5.3848 v -4.826 q 0,-1.5748 -0.7366,-1.9812 -0.7366,-0.4318 -2.6162,-0.4318 -1.8034,0 -2.5146,0.4826 -0.7112,0.4826 -0.7112,1.9304 v 4.826 q 0,1.5494 0.6858,2.0066 0.7112,0.4318 2.5654,0.4318 1.8796,0 2.5908,-0.4318 0.7366,-0.4572 0.7366,-2.0066 z"
|
||||||
|
style="font-size:25.4px;font-family:Play;-inkscape-font-specification:'Play, Normal';fill:#ffffff"
|
||||||
|
id="path875" />
|
||||||
|
<path
|
||||||
|
d="m 86.933198,147.82162 h -3.2004 l -7.874,-8.763 7.366,-7.72159 h 2.9718 l -7.4168,7.61999 z m -11.1252,0 h -2.4638 v -16.48459 h 2.4638 z"
|
||||||
|
style="font-size:25.4px;font-family:Play;-inkscape-font-specification:'Play, Normal';fill:#ffffff"
|
||||||
|
id="path877" />
|
||||||
|
<path
|
||||||
|
d="m 99.53158,139.66822 v 4.2672 q 0,1.143 -0.2794,1.9304 -0.2794,0.7874 -0.7112,1.2192 -0.4318,0.4318 -1.2446,0.6604 -0.7874,0.2286 -1.524,0.2794 -0.7112,0.0508 -1.905,0.0508 -1.524,0 -2.3876,-0.1016 -0.8382,-0.1016 -1.7018,-0.4826 -0.8382,-0.4064 -1.2192,-1.27 -0.3556,-0.8636 -0.3556,-2.286 v -4.2672 q 0,-1.2192 0.2794,-2.0574 0.3048,-0.8382 0.7112,-1.2954 0.4318,-0.4572 1.2446,-0.6858 0.8128,-0.254 1.4986,-0.3048 0.7112,-0.0508 1.9304,-0.0508 1.2192,0 1.905,0.0508 0.7112,0.0508 1.524,0.3048 0.8128,0.2286 1.2192,0.6858 0.4318,0.4572 0.7112,1.2954 0.3048,0.8382 0.3048,2.0574 z m -2.3368,4.5974 v -4.9784 q 0,-1.7018 -0.6858,-2.1082 -0.6858,-0.4064 -2.6416,-0.4064 -1.9558,0 -2.6416,0.4064 -0.6858,0.4064 -0.6858,2.1082 v 4.9784 q 0,0.8128 0.1778,1.2954 0.1778,0.4826 0.6604,0.7112 0.4826,0.2032 0.9906,0.254 0.508,0.0508 1.4986,0.0508 0.9906,0 1.4986,-0.0508 0.508,-0.0508 0.9906,-0.254 0.4826,-0.2286 0.6604,-0.7112 0.1778,-0.4826 0.1778,-1.2954 z"
|
||||||
|
style="font-size:25.4px;font-family:Play;-inkscape-font-specification:'Play, Normal';fill:#ffffff"
|
||||||
|
id="path879" />
|
||||||
|
<path
|
||||||
|
d="m 113.6794,147.82162 h -2.286 v -8.89 q 0,-0.4572 -0.0762,-0.762 -0.0762,-0.3048 -0.3302,-0.6096 -0.2286,-0.3302 -0.8128,-0.4826 -0.5588,-0.1778 -1.4224,-0.1778 -2.0066,0 -2.6924,0.4318 -0.6858,0.4318 -0.6858,1.6002 v 8.89 h -2.286 v -12.2936 h 2.286 v 1.4986 q 0.3556,-1.7526 4.0386,-1.7526 2.4384,0 3.3528,0.889 0.9144,0.8636 0.9144,2.6416 z"
|
||||||
|
style="font-size:25.4px;font-family:Play;-inkscape-font-specification:'Play, Normal';fill:#ffffff"
|
||||||
|
id="path881" />
|
||||||
|
<path
|
||||||
|
d="m 128.513,139.66822 v 4.2672 q 0,1.143 -0.2794,1.9304 -0.2794,0.7874 -0.7112,1.2192 -0.4318,0.4318 -1.2446,0.6604 -0.7874,0.2286 -1.524,0.2794 -0.7112,0.0508 -1.905,0.0508 -1.524,0 -2.3876,-0.1016 -0.8382,-0.1016 -1.7018,-0.4826 -0.8382,-0.4064 -1.2192,-1.27 -0.3556,-0.8636 -0.3556,-2.286 v -4.2672 q 0,-1.2192 0.2794,-2.0574 0.3048,-0.8382 0.7112,-1.2954 0.4318,-0.4572 1.2446,-0.6858 0.8128,-0.254 1.4986,-0.3048 0.7112,-0.0508 1.9304,-0.0508 1.2192,0 1.905,0.0508 0.7112,0.0508 1.524,0.3048 0.8128,0.2286 1.2192,0.6858 0.4318,0.4572 0.7112,1.2954 0.3048,0.8382 0.3048,2.0574 z m -2.3368,4.5974 v -4.9784 q 0,-1.7018 -0.6858,-2.1082 -0.6858,-0.4064 -2.6416,-0.4064 -1.9558,0 -2.6416,0.4064 -0.6858,0.4064 -0.6858,2.1082 v 4.9784 q 0,0.8128 0.1778,1.2954 0.1778,0.4826 0.6604,0.7112 0.4826,0.2032 0.9906,0.254 0.508,0.0508 1.4986,0.0508 0.9906,0 1.4986,-0.0508 0.508,-0.0508 0.9906,-0.254 0.4826,-0.2286 0.6604,-0.7112 0.1778,-0.4826 0.1778,-1.2954 z"
|
||||||
|
style="font-size:25.4px;font-family:Play;-inkscape-font-specification:'Play, Normal';fill:#ffffff"
|
||||||
|
id="path883" />
|
||||||
|
<path
|
||||||
|
d="m 142.55921,135.52802 v 13.462 q 0,2.6162 -1.4732,3.5052 -1.4478,0.9144 -4.3688,0.9144 -2.6162,0 -4.2418,-0.2794 v -1.6256 q 2.921,0.3556 4.445,0.3556 1.7526,0 2.54,-0.5588 0.8128,-0.5588 0.8128,-1.8542 v -2.9464 q -0.0508,0.8636 -1.1938,1.2192 -1.1176,0.3556 -2.5654,0.3556 -2.8448,0 -3.8862,-0.8382 -1.016,-0.8382 -1.016,-2.8956 v -5.0038 q 0,-2.1844 1.1684,-3.1242 1.1938,-0.9398 3.8862,-0.9398 3.2004,0 3.7592,1.4478 v -1.1938 z m -2.286,8.9662 v -5.5626 q 0,-1.1938 -0.6604,-1.6256 -0.635,-0.4572 -2.667,-0.4572 -1.905,0 -2.4892,0.4318 -0.5842,0.4318 -0.5842,1.651 v 5.5626 q 0,1.27 0.6096,1.6764 0.635,0.381 2.5146,0.381 1.7272,0 2.4892,-0.4064 0.7874,-0.4318 0.7874,-1.651 z"
|
||||||
|
style="font-size:25.4px;font-family:Play;-inkscape-font-specification:'Play, Normal';fill:#ffffff"
|
||||||
|
id="path885" />
|
||||||
|
<path
|
||||||
|
d="m 157.39282,139.66822 v 4.2672 q 0,1.143 -0.2794,1.9304 -0.2794,0.7874 -0.7112,1.2192 -0.4318,0.4318 -1.2446,0.6604 -0.7874,0.2286 -1.524,0.2794 -0.7112,0.0508 -1.905,0.0508 -1.524,0 -2.3876,-0.1016 -0.8382,-0.1016 -1.7018,-0.4826 -0.8382,-0.4064 -1.2192,-1.27 -0.3556,-0.8636 -0.3556,-2.286 v -4.2672 q 0,-1.2192 0.2794,-2.0574 0.3048,-0.8382 0.7112,-1.2954 0.4318,-0.4572 1.2446,-0.6858 0.8128,-0.254 1.4986,-0.3048 0.7112,-0.0508 1.9304,-0.0508 1.2192,0 1.905,0.0508 0.7112,0.0508 1.524,0.3048 0.8128,0.2286 1.2192,0.6858 0.4318,0.4572 0.7112,1.2954 0.3048,0.8382 0.3048,2.0574 z m -2.3368,4.5974 v -4.9784 q 0,-1.7018 -0.6858,-2.1082 -0.6858,-0.4064 -2.6416,-0.4064 -1.9558,0 -2.6416,0.4064 -0.6858,0.4064 -0.6858,2.1082 v 4.9784 q 0,0.8128 0.1778,1.2954 0.1778,0.4826 0.6604,0.7112 0.4826,0.2032 0.9906,0.254 0.508,0.0508 1.4986,0.0508 0.9906,0 1.4986,-0.0508 0.508,-0.0508 0.9906,-0.254 0.4826,-0.2286 0.6604,-0.7112 0.1778,-0.4826 0.1778,-1.2954 z"
|
||||||
|
style="font-size:25.4px;font-family:Play;-inkscape-font-specification:'Play, Normal';fill:#ffffff"
|
||||||
|
id="path887" />
|
||||||
|
<path
|
||||||
|
d="m 171.54064,147.82162 h -2.286 v -8.89 q 0,-0.4572 -0.0762,-0.762 -0.0762,-0.3048 -0.3302,-0.6096 -0.2286,-0.3302 -0.8128,-0.4826 -0.5588,-0.1778 -1.4224,-0.1778 -2.0066,0 -2.6924,0.4318 -0.6858,0.4318 -0.6858,1.6002 v 8.89 h -2.286 v -12.2936 h 2.286 v 1.4986 q 0.3556,-1.7526 4.0386,-1.7526 2.4384,0 3.3528,0.889 0.9144,0.8636 0.9144,2.6416 z"
|
||||||
|
style="font-size:25.4px;font-family:Play;-inkscape-font-specification:'Play, Normal';fill:#ffffff"
|
||||||
|
id="path889" />
|
||||||
|
<path
|
||||||
|
d="m 186.52665,147.82162 h -2.8448 l -5.6134,-6.731 5.1816,-5.5626 h 2.794 l -5.334,5.5626 z m -8.7376,0 h -2.286 v -17.77999 h 2.286 z"
|
||||||
|
style="font-size:25.4px;font-family:Play;-inkscape-font-specification:'Play, Normal';fill:#ffffff"
|
||||||
|
id="path891" />
|
||||||
|
<path
|
||||||
|
d="m 198.26144,147.82162 h -2.2098 v -1.1684 q -0.2794,1.4224 -3.7084,1.4224 -2.54,0 -3.5306,-0.762 -0.9652,-0.7874 -0.9652,-3.1496 0,-2.0574 0.9144,-2.8194 0.9144,-0.7874 3.5306,-0.7874 h 3.683 v -1.9558 q 0,-1.016 -0.635,-1.3716 -0.6096,-0.3556 -2.3368,-0.3556 -1.8796,0 -4.2164,0.3048 v -1.6764 q 2.5146,-0.2286 4.5466,-0.2286 3.0988,0 4.0132,0.762 0.9144,0.7366 0.9144,3.0988 z m -2.286,-2.4892 v -3.3528 h -3.5306 q -1.5748,0 -1.9812,0.4064 -0.381,0.4064 -0.381,1.8542 0,1.2954 0.4064,1.8288 0.4318,0.508 1.778,0.508 h 0.4572 q 0.6604,0 1.0922,-0.0254 0.4572,-0.0254 1.016,-0.127 0.5588,-0.127 0.8382,-0.381 0.3048,-0.2794 0.3048,-0.7112 z"
|
||||||
|
style="font-size:25.4px;font-family:Play;-inkscape-font-specification:'Play, Normal';fill:#ffffff"
|
||||||
|
id="path893" />
|
||||||
|
</g>
|
||||||
<g
|
<g
|
||||||
id="g939">
|
id="g939">
|
||||||
<path
|
<path
|
||||||
|
|
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 15 KiB |
13
pom.xml
13
pom.xml
|
@ -70,6 +70,13 @@
|
||||||
<version>2.19.0</version>
|
<version>2.19.0</version>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.lz4</groupId>
|
||||||
|
<artifactId>lz4-pure-java</artifactId>
|
||||||
|
<version>1.8.0</version>
|
||||||
|
<scope>compile</scope>
|
||||||
|
</dependency>
|
||||||
<!-- testing -->
|
<!-- testing -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
@ -89,11 +96,7 @@
|
||||||
<version>5.9.0</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>
|
||||||
|
|
|
@ -22,7 +22,11 @@ import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
/* ANSI_BLACK = "\u001B[30m";
|
||||||
|
ANSI_YELLOW = "\u001B[33m";
|
||||||
|
ANSI_PURPLE = "\u001B[35m";
|
||||||
|
ANSI_CYAN = "\u001B[36m";
|
||||||
|
ANSI_WHITE = "\u001B[37m"; */
|
||||||
/**
|
/**
|
||||||
* Debug tool like hexdump <3
|
* Debug tool like hexdump <3
|
||||||
*/
|
*/
|
||||||
|
@ -30,14 +34,9 @@ public class RainbowDump {
|
||||||
private final static Logger log = LogManager.getLogger(Converter.class);
|
private final static Logger log = LogManager.getLogger(Converter.class);
|
||||||
|
|
||||||
private static final String ANSI_RESET = "\u001B[0m";
|
private static final String ANSI_RESET = "\u001B[0m";
|
||||||
private static final String ANSI_BLACK = "\u001B[30m";
|
|
||||||
private static final String ANSI_RED = "\u001B[31m";
|
private static final String ANSI_RED = "\u001B[31m";
|
||||||
private static final String ANSI_GREEN = "\u001B[32m";
|
private static final String ANSI_GREEN = "\u001B[32m";
|
||||||
private static final String ANSI_YELLOW = "\u001B[33m";
|
|
||||||
private static final String ANSI_BLUE = "\u001B[34m";
|
private static final String ANSI_BLUE = "\u001B[34m";
|
||||||
private static final String ANSI_PURPLE = "\u001B[35m";
|
|
||||||
private static final String ANSI_CYAN = "\u001B[36m";
|
|
||||||
private static final String ANSI_WHITE = "\u001B[37m";
|
|
||||||
|
|
||||||
private static StringBuilder stringBuilder;
|
private static StringBuilder stringBuilder;
|
||||||
public static void hexDumpUTF8(byte[] byteArray){
|
public static void hexDumpUTF8(byte[] byteArray){
|
||||||
|
@ -80,7 +79,13 @@ public class RainbowDump {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void printChars(byte[] byteArray, int pointer){
|
private static void printChars(byte[] byteArray, int pointer){
|
||||||
for (int j = pointer-16; j < pointer; j++){
|
int j;
|
||||||
|
if (pointer < 16)
|
||||||
|
j = 0;
|
||||||
|
else
|
||||||
|
j = pointer-16;
|
||||||
|
|
||||||
|
for (; j < pointer; j++){
|
||||||
if ((byteArray[j] > 21) && (byteArray[j] < 126)) // man ascii
|
if ((byteArray[j] > 21) && (byteArray[j] < 126)) // man ascii
|
||||||
stringBuilder.append((char) byteArray[j]);
|
stringBuilder.append((char) byteArray[j]);
|
||||||
else if (byteArray[j] == 0x0a)
|
else if (byteArray[j] == 0x0a)
|
||||||
|
@ -92,18 +97,22 @@ public class RainbowDump {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void hexDumpUTF8Legacy(byte[] byteArray){
|
public static void hexDumpUTF8Legacy(byte[] byteArray){
|
||||||
|
StringBuilder stringBuilderLegacy = new StringBuilder("HexDumpUTF8Legacy");
|
||||||
|
stringBuilderLegacy.append(ANSI_BLUE);
|
||||||
|
|
||||||
if (byteArray == null || byteArray.length == 0)
|
if (byteArray == null || byteArray.length == 0)
|
||||||
return;
|
return;
|
||||||
System.out.print(ANSI_BLUE);
|
|
||||||
for (int i=0; i < byteArray.length; i++)
|
for (int i=0; i < byteArray.length; i++)
|
||||||
System.out.printf("%02d-", i%100);
|
stringBuilderLegacy.append(String.format("%02d-", i%100));
|
||||||
System.out.println(">"+ANSI_RED+byteArray.length+ANSI_RESET);
|
stringBuilderLegacy.append(">" + ANSI_RED).append(byteArray.length).append(ANSI_RESET).append("\n");
|
||||||
for (byte b: byteArray)
|
for (byte b: byteArray)
|
||||||
System.out.printf("%02x ", b);
|
stringBuilderLegacy.append(String.format("%02x ", b));
|
||||||
System.out.println();
|
stringBuilderLegacy.append("\n")
|
||||||
System.out.print(new String(byteArray, StandardCharsets.UTF_8)+"\n");
|
.append(new String(byteArray, StandardCharsets.UTF_8))
|
||||||
|
.append("\n");
|
||||||
|
log.debug(stringBuilderLegacy.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void binDumpInt(int value){
|
public static void binDumpInt(int value){
|
||||||
|
@ -111,7 +120,7 @@ public class RainbowDump {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void binDumpLong(long value){
|
public static void binDumpLong(long value){
|
||||||
System.out.println(String.format("%64s", Long.toBinaryString( value )).replace(' ', '0')+" | "+value);
|
log.debug(String.format("%64s", Long.toBinaryString( value )).replace(' ', '0')+" | "+value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String formatDecHexString(long value){
|
public static String formatDecHexString(long value){
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
package libKonogonka.Tools.NPDM;
|
package libKonogonka.Tools.NPDM;
|
||||||
|
|
||||||
import libKonogonka.Converter;
|
import libKonogonka.Converter;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
@ -73,6 +75,7 @@ for (byte i = 0; i < 16; i++){
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class KernelAccessControlProvider {
|
public class KernelAccessControlProvider {
|
||||||
|
private final static Logger log = LogManager.getLogger(KernelAccessControlProvider.class);
|
||||||
|
|
||||||
private static final int KERNELFLAGS = 3,
|
private static final int KERNELFLAGS = 3,
|
||||||
SYSCALLMASK = 4,
|
SYSCALLMASK = 4,
|
||||||
|
@ -85,7 +88,7 @@ public class KernelAccessControlProvider {
|
||||||
DEBUGFLAGS = 16;
|
DEBUGFLAGS = 16;
|
||||||
|
|
||||||
// RAW data
|
// RAW data
|
||||||
private LinkedList<Integer> rawData;
|
private final LinkedList<Integer> rawData;
|
||||||
// Kernel flags
|
// Kernel flags
|
||||||
private boolean kernelFlagsAvailable;
|
private boolean kernelFlagsAvailable;
|
||||||
private int kernelFlagCpuIdHi,
|
private int kernelFlagCpuIdHi,
|
||||||
|
@ -93,13 +96,13 @@ public class KernelAccessControlProvider {
|
||||||
kernelFlagThreadPrioHi,
|
kernelFlagThreadPrioHi,
|
||||||
kernelFlagThreadPrioLo;
|
kernelFlagThreadPrioLo;
|
||||||
// Syscall Masks as index | mask - order AS IS. [0] = bit5; [1] = bit6
|
// Syscall Masks as index | mask - order AS IS. [0] = bit5; [1] = bit6
|
||||||
private LinkedHashMap<Byte, byte[]> syscallMasks; // Index, Mask
|
private final LinkedHashMap<Byte, byte[]> syscallMasks; // Index, Mask
|
||||||
// MapIoOrNormalRange
|
// MapIoOrNormalRange
|
||||||
private LinkedHashMap<byte[], Boolean> mapIoOrNormalRange; // alt page+num, RO flag
|
private final LinkedHashMap<byte[], Boolean> mapIoOrNormalRange; // alt page+num, RO flag
|
||||||
// MapNormalPage (RW)
|
// MapNormalPage (RW)
|
||||||
private byte[] mapNormalPage; // TODO: clarify is possible to have multiple
|
private byte[] mapNormalPage; // TODO: clarify is possible to have multiple
|
||||||
// InterruptPair
|
// InterruptPair
|
||||||
private LinkedHashMap<Integer, byte[][]> interruptPairs; // Number; irq0, irq2
|
private final LinkedHashMap<Integer, byte[][]> interruptPairs; // Number; irq0, irq2
|
||||||
// Application type
|
// Application type
|
||||||
private int applicationType;
|
private int applicationType;
|
||||||
// KernelReleaseVersion
|
// KernelReleaseVersion
|
||||||
|
@ -142,42 +145,42 @@ public class KernelAccessControlProvider {
|
||||||
kernelFlagCpuIdLo = block >> 16 & 0b11111111;
|
kernelFlagCpuIdLo = block >> 16 & 0b11111111;
|
||||||
kernelFlagThreadPrioHi = block >> 10 & 0b111111;
|
kernelFlagThreadPrioHi = block >> 10 & 0b111111;
|
||||||
kernelFlagThreadPrioLo = block >> 4 & 0b111111;
|
kernelFlagThreadPrioLo = block >> 4 & 0b111111;
|
||||||
//System.out.println("KERNELFLAGS "+kernelFlagCpuIdHi+" "+kernelFlagCpuIdLo+" "+kernelFlagThreadPrioHi+" "+kernelFlagThreadPrioLo);
|
//log.debug("KERNELFLAGS "+kernelFlagCpuIdHi+" "+kernelFlagCpuIdLo+" "+kernelFlagThreadPrioHi+" "+kernelFlagThreadPrioLo);
|
||||||
break;
|
break;
|
||||||
case SYSCALLMASK:
|
case SYSCALLMASK:
|
||||||
byte maskTableIndex = (byte) (block >> 29 & 0b111); // declared as byte; max value could be 7; min - 0;
|
byte maskTableIndex = (byte) (block >> 29 & 0b111); // declared as byte; max value could be 7; min - 0;
|
||||||
byte[] mask = new byte[24]; // Consider as bit.
|
byte[] mask = new byte[24]; // Consider as bit.
|
||||||
//System.out.println("SYSCALLMASK ind: "+maskTableIndex);
|
//log.debug("SYSCALLMASK ind: "+maskTableIndex);
|
||||||
|
|
||||||
for (int k = 28; k >= 5; k--) {
|
for (int k = 28; k >= 5; k--) {
|
||||||
mask[k-5] = (byte) (block >> k & 1); // Only 1 or 0 possible
|
mask[k-5] = (byte) (block >> k & 1); // Only 1 or 0 possible
|
||||||
//System.out.print(mask[k-5]);
|
//log.debug(" " + mask[k-5]);
|
||||||
}
|
}
|
||||||
//System.out.println();
|
//log.debug();
|
||||||
syscallMasks.put(maskTableIndex, mask);
|
syscallMasks.put(maskTableIndex, mask);
|
||||||
break;
|
break;
|
||||||
case MAPIOORNORMALRANGE:
|
case MAPIOORNORMALRANGE:
|
||||||
byte[] altStPgNPgNum = new byte[24];
|
byte[] altStPgNPgNum = new byte[24];
|
||||||
//System.out.println("MAPIOORNORMALRANGE Flag: "+((block >> 31 & 1) != 0));
|
//log.debug("MAPIOORNORMALRANGE Flag: "+((block >> 31 & 1) != 0));
|
||||||
|
|
||||||
for (int k = 30; k >= 7; k--){
|
for (int k = 30; k >= 7; k--){
|
||||||
altStPgNPgNum[k-7] = (byte) (block >> k & 1); // Only 1 or 0 possible
|
altStPgNPgNum[k-7] = (byte) (block >> k & 1); // Only 1 or 0 possible
|
||||||
//System.out.print(altStPgNPgNum[k-7]);
|
//log.debug(" " + altStPgNPgNum[k-7]);
|
||||||
}
|
}
|
||||||
mapIoOrNormalRange.put(altStPgNPgNum, (block >> 31 & 1) != 0);
|
mapIoOrNormalRange.put(altStPgNPgNum, (block >> 31 & 1) != 0);
|
||||||
//System.out.println();
|
//log.debug();
|
||||||
break;
|
break;
|
||||||
case MAPNORMALPAGE_RW:
|
case MAPNORMALPAGE_RW:
|
||||||
//System.out.println("MAPNORMALPAGE_RW\t");
|
//log.debug("MAPNORMALPAGE_RW\t");
|
||||||
mapNormalPage = new byte[24];
|
mapNormalPage = new byte[24];
|
||||||
for (int k = 31; k >= 8; k--){
|
for (int k = 31; k >= 8; k--){
|
||||||
mapNormalPage[k-8] = (byte) (block >> k & 1);
|
mapNormalPage[k-8] = (byte) (block >> k & 1);
|
||||||
//System.out.print(mapNormalPage[k-8]);
|
//log.debug(" " + mapNormalPage[k-8]);
|
||||||
}
|
}
|
||||||
//System.out.println();
|
//log.debug();
|
||||||
break;
|
break;
|
||||||
case INTERRUPTPAIR:
|
case INTERRUPTPAIR:
|
||||||
//System.out.println("INTERRUPTPAIR");
|
//log.debug("INTERRUPTPAIR");
|
||||||
//RainbowHexDump.octDumpInt(block);
|
//RainbowHexDump.octDumpInt(block);
|
||||||
byte[][] pair = new byte[2][];
|
byte[][] pair = new byte[2][];
|
||||||
byte[] irq0 = new byte[10];
|
byte[] irq0 = new byte[10];
|
||||||
|
@ -193,26 +196,26 @@ public class KernelAccessControlProvider {
|
||||||
break;
|
break;
|
||||||
case APPLICATIONTYPE:
|
case APPLICATIONTYPE:
|
||||||
applicationType = block >> 14 & 0b111;
|
applicationType = block >> 14 & 0b111;
|
||||||
//System.out.println("APPLICATIONTYPE "+applicationType);
|
//log.debug("APPLICATIONTYPE "+applicationType);
|
||||||
break;
|
break;
|
||||||
case KERNELRELEASEVERSION:
|
case KERNELRELEASEVERSION:
|
||||||
//System.out.println("KERNELRELEASEVERSION\t"+(block >> 19 & 0b111111111111)+"."+(block >> 15 & 0b1111)+".X");
|
//log.debug("KERNELRELEASEVERSION\t"+(block >> 19 & 0b111111111111)+"."+(block >> 15 & 0b1111)+".X");
|
||||||
isKernelRelVersionAvailable = true;
|
isKernelRelVersionAvailable = true;
|
||||||
kernelRelVersionMajor = (block >> 19 & 0b111111111111);
|
kernelRelVersionMajor = (block >> 19 & 0b111111111111);
|
||||||
kernelRelVersionMinor = (block >> 15 & 0b1111);
|
kernelRelVersionMinor = (block >> 15 & 0b1111);
|
||||||
break;
|
break;
|
||||||
case HANDLETABLESIZE:
|
case HANDLETABLESIZE:
|
||||||
handleTableSize = block >> 16 & 0b1111111111;
|
handleTableSize = block >> 16 & 0b1111111111;
|
||||||
//System.out.println("HANDLETABLESIZE "+handleTableSize);
|
//log.debug("HANDLETABLESIZE "+handleTableSize);
|
||||||
break;
|
break;
|
||||||
case DEBUGFLAGS:
|
case DEBUGFLAGS:
|
||||||
debugFlagsAvailable = true;
|
debugFlagsAvailable = true;
|
||||||
canBeDebugged = (block >> 17 & 1) != 0;
|
canBeDebugged = (block >> 17 & 1) != 0;
|
||||||
canDebugOthers = (block >> 18 & 1) != 0;
|
canDebugOthers = (block >> 18 & 1) != 0;
|
||||||
//System.out.println("DEBUGFLAGS "+canBeDebugged+" "+canDebugOthers);
|
//log.debug("DEBUGFLAGS "+canBeDebugged+" "+canDebugOthers);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
System.out.println("UNKNOWN\t\t"+block+" "+type);
|
log.error("UNKNOWN\t\t"+block+" "+type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
174
src/main/java/libKonogonka/Tools/NSO/NSO0Header.java
Normal file
174
src/main/java/libKonogonka/Tools/NSO/NSO0Header.java
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
/*
|
||||||
|
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 org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import static libKonogonka.Converter.getLEint;
|
||||||
|
|
||||||
|
public class NSO0Header {
|
||||||
|
private final static Logger log = LogManager.getLogger(NSO0Header.class);
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
public NSO0Header(byte[] headerBytes) throws Exception{
|
||||||
|
if (headerBytes.length < 0x100)
|
||||||
|
throw new Exception("Incorrect NSO0 header size");
|
||||||
|
parse(headerBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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" +
|
||||||
|
" ============================================================= "
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,45 +18,15 @@
|
||||||
*/
|
*/
|
||||||
package libKonogonka.Tools.NSO;
|
package libKonogonka.Tools.NSO;
|
||||||
|
|
||||||
import libKonogonka.Converter;
|
|
||||||
import libKonogonka.RainbowDump;
|
|
||||||
import libKonogonka.ctraes.InFileStreamProducer;
|
import libKonogonka.ctraes.InFileStreamProducer;
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import static libKonogonka.Converter.getLEint;
|
|
||||||
|
|
||||||
public class NSO0Provider {
|
public class NSO0Provider {
|
||||||
private final static Logger log = LogManager.getLogger(NSO0Provider.class);
|
|
||||||
|
|
||||||
private final InFileStreamProducer producer;
|
private final InFileStreamProducer producer;
|
||||||
|
|
||||||
private String magic;
|
private final NSO0Header header;
|
||||||
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 */
|
/* Encrypted */
|
||||||
public NSO0Provider(InFileStreamProducer producer) throws Exception {
|
public NSO0Provider(InFileStreamProducer producer) throws Exception {
|
||||||
|
@ -65,7 +35,7 @@ public class NSO0Provider {
|
||||||
byte[] knownStartingBytes = new byte[0x100];
|
byte[] knownStartingBytes = new byte[0x100];
|
||||||
if (0x100 != stream.read(knownStartingBytes))
|
if (0x100 != stream.read(knownStartingBytes))
|
||||||
throw new Exception("Reading stream suddenly ended while trying to read starting 0x100 bytes");
|
throw new Exception("Reading stream suddenly ended while trying to read starting 0x100 bytes");
|
||||||
parse(knownStartingBytes);
|
this.header = new NSO0Header(knownStartingBytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,123 +52,26 @@ public class NSO0Provider {
|
||||||
byte[] knownStartingBytes = new byte[0x100];
|
byte[] knownStartingBytes = new byte[0x100];
|
||||||
if (0x100 != stream.read(knownStartingBytes))
|
if (0x100 != stream.read(knownStartingBytes))
|
||||||
throw new Exception("Reading stream suddenly ended while trying to read starting 0x100 bytes");
|
throw new Exception("Reading stream suddenly ended while trying to read starting 0x100 bytes");
|
||||||
parse(knownStartingBytes);
|
this.header = new NSO0Header(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);
|
public NSO0Header getHeader() {
|
||||||
this.upperReserved = Arrays.copyOfRange(knownStartingBytes, 0x8, 0xC);
|
return header;
|
||||||
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{
|
public void exportAsDecompressedNSO0(String saveToLocation) throws Exception{
|
||||||
NSO0Unpacker.unpack(this, producer, saveToLocation);
|
NSO0Unpacker.unpack(header, producer, saveToLocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* API */
|
public NSO0Raw getAsDecompressedNSO0() throws Exception{
|
||||||
public String getMagic() { return magic; }
|
return NSO0Unpacker.getNSO0Raw(header, producer);
|
||||||
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; }
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints header.printDebug()
|
||||||
|
* */
|
||||||
public void printDebug(){
|
public void printDebug(){
|
||||||
log.debug(".:: NSO0 Provider ::.\n" +
|
header.printDebug();
|
||||||
" ============================================================= \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" +
|
|
||||||
" ============================================================= "
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
47
src/main/java/libKonogonka/Tools/NSO/NSO0Raw.java
Normal file
47
src/main/java/libKonogonka/Tools/NSO/NSO0Raw.java
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
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;
|
||||||
|
|
||||||
|
public class NSO0Raw {
|
||||||
|
private NSO0Header headerObject;
|
||||||
|
private final byte[] header;
|
||||||
|
private final byte[] _textDecompressedSection;
|
||||||
|
private final byte[] _rodataDecompressedSection;
|
||||||
|
private final byte[] _dataDecompressedSection;
|
||||||
|
|
||||||
|
NSO0Raw(byte[] header,
|
||||||
|
byte[] _textDecompressedSection,
|
||||||
|
byte[] _rodataDecompressedSection,
|
||||||
|
byte[] _dataDecompressedSection){
|
||||||
|
this.header = header;
|
||||||
|
this._textDecompressedSection = _textDecompressedSection;
|
||||||
|
this. _rodataDecompressedSection = _rodataDecompressedSection;
|
||||||
|
this._dataDecompressedSection = _dataDecompressedSection;
|
||||||
|
try {
|
||||||
|
this.headerObject = new NSO0Header(header);
|
||||||
|
}
|
||||||
|
catch (Exception e){ e.printStackTrace(); } //never happens
|
||||||
|
}
|
||||||
|
|
||||||
|
public NSO0Header getHeader() { return headerObject; }
|
||||||
|
public byte[] getHeaderRaw() {return header;}
|
||||||
|
public byte[] getTextRaw() {return _textDecompressedSection;}
|
||||||
|
public byte[] getRodataRaw() {return _rodataDecompressedSection;}
|
||||||
|
public byte[] getDataRaw() {return _dataDecompressedSection;}
|
||||||
|
}
|
|
@ -29,15 +29,14 @@ import java.nio.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
public class NSO0Unpacker {
|
class NSO0Unpacker {
|
||||||
private static final String DECOMPRESSED_FILE_NAME = "main_decompressed";
|
private static final String DECOMPRESSED_FILE_NAME = "main_decompressed";
|
||||||
private final MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
private final MessageDigest digest = MessageDigest.getInstance("SHA-256");
|
||||||
|
|
||||||
private NSO0Provider provider;
|
private final NSO0Header nso0Header;
|
||||||
private InFileStreamProducer producer;
|
private final InFileStreamProducer producer;
|
||||||
|
|
||||||
private byte[] _textDecompressedSection;
|
private byte[] _textDecompressedSection;
|
||||||
private byte[] _rodataDecompressedSection;
|
private byte[] _rodataDecompressedSection;
|
||||||
|
@ -47,19 +46,29 @@ public class NSO0Unpacker {
|
||||||
private int rodataFileOffsetNew;
|
private int rodataFileOffsetNew;
|
||||||
private int dataFileOffsetNew;
|
private int dataFileOffsetNew;
|
||||||
|
|
||||||
private NSO0Unpacker() throws NoSuchAlgorithmException { }
|
private NSO0Unpacker(NSO0Header nso0Header, InFileStreamProducer producer) throws Exception {
|
||||||
|
this.nso0Header = nso0Header;
|
||||||
|
this.producer = producer;
|
||||||
|
|
||||||
public static void unpack(NSO0Provider provider, InFileStreamProducer producer, String saveToLocation) throws Exception{
|
decompressSections();
|
||||||
if (! provider.isTextCompressFlag() && ! provider.isRoCompressFlag() && ! provider.isDataCompressFlag())
|
validateHashes();
|
||||||
|
makeHeader();
|
||||||
|
}
|
||||||
|
|
||||||
|
static NSO0Raw getNSO0Raw(NSO0Header nso0Header, InFileStreamProducer producer) throws Exception{
|
||||||
|
NSO0Unpacker instance = new NSO0Unpacker(nso0Header, producer);
|
||||||
|
|
||||||
|
return new NSO0Raw(instance.header,
|
||||||
|
instance._textDecompressedSection,
|
||||||
|
instance._rodataDecompressedSection,
|
||||||
|
instance._dataDecompressedSection);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void unpack(NSO0Header nso0Header, InFileStreamProducer producer, String saveToLocation) throws Exception{
|
||||||
|
if (! nso0Header.isTextCompressFlag() && ! nso0Header.isRoCompressFlag() && ! nso0Header.isDataCompressFlag())
|
||||||
throw new Exception("This file is not compressed");
|
throw new Exception("This file is not compressed");
|
||||||
|
NSO0Unpacker instance = new NSO0Unpacker(nso0Header, producer);
|
||||||
|
|
||||||
NSO0Unpacker instance = new NSO0Unpacker();
|
|
||||||
instance.provider = provider;
|
|
||||||
instance.producer = producer;
|
|
||||||
|
|
||||||
instance.decompressSections();
|
|
||||||
instance.validateHashes();
|
|
||||||
instance.makeHeader();
|
|
||||||
instance.writeFile(saveToLocation);
|
instance.writeFile(saveToLocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,22 +78,22 @@ public class NSO0Unpacker {
|
||||||
decompressDataSection();
|
decompressDataSection();
|
||||||
}
|
}
|
||||||
private void decompressTextSection() throws Exception{
|
private void decompressTextSection() throws Exception{
|
||||||
if (provider.isTextCompressFlag())
|
if (nso0Header.isTextCompressFlag())
|
||||||
_textDecompressedSection = decompressSection(provider.getTextSegmentHeader(), provider.getTextCompressedSize());
|
_textDecompressedSection = decompressSection(nso0Header.getTextSegmentHeader(), nso0Header.getTextCompressedSize());
|
||||||
else
|
else
|
||||||
_textDecompressedSection = duplicateSection(provider.getTextSegmentHeader());
|
_textDecompressedSection = duplicateSection(nso0Header.getTextSegmentHeader());
|
||||||
}
|
}
|
||||||
private void decompressRodataSection() throws Exception{
|
private void decompressRodataSection() throws Exception{
|
||||||
if (provider.isRoCompressFlag())
|
if (nso0Header.isRoCompressFlag())
|
||||||
_rodataDecompressedSection = decompressSection(provider.getRodataSegmentHeader(), provider.getRodataCompressedSize());
|
_rodataDecompressedSection = decompressSection(nso0Header.getRodataSegmentHeader(), nso0Header.getRodataCompressedSize());
|
||||||
else
|
else
|
||||||
_rodataDecompressedSection = duplicateSection(provider.getRodataSegmentHeader());
|
_rodataDecompressedSection = duplicateSection(nso0Header.getRodataSegmentHeader());
|
||||||
}
|
}
|
||||||
private void decompressDataSection() throws Exception{
|
private void decompressDataSection() throws Exception{
|
||||||
if (provider.isDataCompressFlag())
|
if (nso0Header.isDataCompressFlag())
|
||||||
_dataDecompressedSection = decompressSection(provider.getDataSegmentHeader(), provider.getDataCompressedSize());
|
_dataDecompressedSection = decompressSection(nso0Header.getDataSegmentHeader(), nso0Header.getDataCompressedSize());
|
||||||
else
|
else
|
||||||
_dataDecompressedSection = duplicateSection(provider.getDataSegmentHeader());
|
_dataDecompressedSection = duplicateSection(nso0Header.getDataSegmentHeader());
|
||||||
}
|
}
|
||||||
|
|
||||||
private byte[] decompressSection(SegmentHeader segmentHeader, int compressedSectionSize) throws Exception{
|
private byte[] decompressSection(SegmentHeader segmentHeader, int compressedSectionSize) throws Exception{
|
||||||
|
@ -126,13 +135,13 @@ public class NSO0Unpacker {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateHashes() throws Exception{
|
private void validateHashes() throws Exception{
|
||||||
if ( ! Arrays.equals(provider.getTextHash(), digest.digest(_textDecompressedSection)) ) {
|
if ( ! Arrays.equals(nso0Header.getTextHash(), digest.digest(_textDecompressedSection)) ) {
|
||||||
throw new Exception(".text hash mismatch for .text section");
|
throw new Exception(".text hash mismatch for .text section");
|
||||||
}
|
}
|
||||||
if ( ! Arrays.equals(provider.getRodataHash(), digest.digest(_rodataDecompressedSection)) ) {
|
if ( ! Arrays.equals(nso0Header.getRodataHash(), digest.digest(_rodataDecompressedSection)) ) {
|
||||||
throw new Exception(".rodata hash mismatch for .text section");
|
throw new Exception(".rodata hash mismatch for .text section");
|
||||||
}
|
}
|
||||||
if ( ! Arrays.equals(provider.getDataHash(), digest.digest(_dataDecompressedSection)) ) {
|
if ( ! Arrays.equals(nso0Header.getDataHash(), digest.digest(_dataDecompressedSection)) ) {
|
||||||
throw new Exception(".data hash mismatch for .text section");
|
throw new Exception(".data hash mismatch for .text section");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -163,41 +172,41 @@ public class NSO0Unpacker {
|
||||||
header = resultingHeader.array();
|
header = resultingHeader.array();
|
||||||
*/
|
*/
|
||||||
|
|
||||||
textFileOffsetNew = provider.getTextSegmentHeader().getMemoryOffset()+0x100;
|
textFileOffsetNew = nso0Header.getTextSegmentHeader().getMemoryOffset()+0x100;
|
||||||
rodataFileOffsetNew = provider.getRodataSegmentHeader().getMemoryOffset()+0x100;
|
rodataFileOffsetNew = nso0Header.getRodataSegmentHeader().getMemoryOffset()+0x100;
|
||||||
dataFileOffsetNew = provider.getDataSegmentHeader().getMemoryOffset()+0x100;
|
dataFileOffsetNew = nso0Header.getDataSegmentHeader().getMemoryOffset()+0x100;
|
||||||
|
|
||||||
ByteBuffer resultingHeader = ByteBuffer.allocate(0x100).order(ByteOrder.LITTLE_ENDIAN);
|
ByteBuffer resultingHeader = ByteBuffer.allocate(0x100).order(ByteOrder.LITTLE_ENDIAN);
|
||||||
resultingHeader.put("NSO0".getBytes(StandardCharsets.US_ASCII))
|
resultingHeader.put("NSO0".getBytes(StandardCharsets.US_ASCII))
|
||||||
.putInt(provider.getVersion())
|
.putInt(nso0Header.getVersion())
|
||||||
.put(provider.getUpperReserved())
|
.put(nso0Header.getUpperReserved())
|
||||||
.putInt(provider.getFlags() & 0b111000)
|
.putInt(nso0Header.getFlags() & 0b111000)
|
||||||
.putInt(textFileOffsetNew)
|
.putInt(textFileOffsetNew)
|
||||||
.putInt(provider.getTextSegmentHeader().getMemoryOffset())
|
.putInt(nso0Header.getTextSegmentHeader().getMemoryOffset())
|
||||||
.putInt(provider.getTextSegmentHeader().getSizeAsDecompressed())
|
.putInt(nso0Header.getTextSegmentHeader().getSizeAsDecompressed())
|
||||||
.putInt(0x100)
|
.putInt(0x100)
|
||||||
.putInt(rodataFileOffsetNew)
|
.putInt(rodataFileOffsetNew)
|
||||||
.putInt(provider.getRodataSegmentHeader().getMemoryOffset())
|
.putInt(nso0Header.getRodataSegmentHeader().getMemoryOffset())
|
||||||
.putInt(provider.getRodataSegmentHeader().getSizeAsDecompressed())
|
.putInt(nso0Header.getRodataSegmentHeader().getSizeAsDecompressed())
|
||||||
.putInt(0)
|
.putInt(0)
|
||||||
.putInt(dataFileOffsetNew)
|
.putInt(dataFileOffsetNew)
|
||||||
.putInt(provider.getDataSegmentHeader().getMemoryOffset())
|
.putInt(nso0Header.getDataSegmentHeader().getMemoryOffset())
|
||||||
.putInt(provider.getDataSegmentHeader().getSizeAsDecompressed())
|
.putInt(nso0Header.getDataSegmentHeader().getSizeAsDecompressed())
|
||||||
.putInt(provider.getBssSize())
|
.putInt(nso0Header.getBssSize())
|
||||||
.put(provider.getModuleId())
|
.put(nso0Header.getModuleId())
|
||||||
.putInt(provider.getTextSegmentHeader().getSizeAsDecompressed())
|
.putInt(nso0Header.getTextSegmentHeader().getSizeAsDecompressed())
|
||||||
.putInt(provider.getRodataSegmentHeader().getSizeAsDecompressed())
|
.putInt(nso0Header.getRodataSegmentHeader().getSizeAsDecompressed())
|
||||||
.putInt(provider.getDataSegmentHeader().getSizeAsDecompressed())
|
.putInt(nso0Header.getDataSegmentHeader().getSizeAsDecompressed())
|
||||||
.put(provider.getBottomReserved())
|
.put(nso0Header.getBottomReserved())
|
||||||
.putInt(provider.get_api_infoRelative().getOffset())
|
.putInt(nso0Header.get_api_infoRelative().getOffset())
|
||||||
.putInt(provider.get_api_infoRelative().getSize())
|
.putInt(nso0Header.get_api_infoRelative().getSize())
|
||||||
.putInt(provider.get_dynstrRelative().getOffset())
|
.putInt(nso0Header.get_dynstrRelative().getOffset())
|
||||||
.putInt(provider.get_dynstrRelative().getSize())
|
.putInt(nso0Header.get_dynstrRelative().getSize())
|
||||||
.putInt(provider.get_dynsymRelative().getOffset())
|
.putInt(nso0Header.get_dynsymRelative().getOffset())
|
||||||
.putInt(provider.get_dynsymRelative().getSize())
|
.putInt(nso0Header.get_dynsymRelative().getSize())
|
||||||
.put(provider.getTextHash())
|
.put(nso0Header.getTextHash())
|
||||||
.put(provider.getRodataHash())
|
.put(nso0Header.getRodataHash())
|
||||||
.put(provider.getDataHash());
|
.put(nso0Header.getDataHash());
|
||||||
|
|
||||||
header = resultingHeader.array();
|
header = resultingHeader.array();
|
||||||
}
|
}
|
||||||
|
|
|
@ -184,7 +184,7 @@ public class FileSystemEntry {
|
||||||
nextHashTableBucketFileOffset = Converter.getLEint(filesMetadataTable, i);
|
nextHashTableBucketFileOffset = Converter.getLEint(filesMetadataTable, i);
|
||||||
/*
|
/*
|
||||||
if (nextHashTableBucketFileOffset < 0) {
|
if (nextHashTableBucketFileOffset < 0) {
|
||||||
System.out.println("nextHashTableBucketFileOffset: "+ nextHashTableBucketFileOffset);
|
log.debug("nextHashTableBucketFileOffset: "+ nextHashTableBucketFileOffset);
|
||||||
}
|
}
|
||||||
//*/
|
//*/
|
||||||
i += 4;
|
i += 4;
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
package libKonogonka.Tools.XCI;
|
package libKonogonka.Tools.XCI;
|
||||||
|
|
||||||
import libKonogonka.Tools.ISuperProvider;
|
import libKonogonka.Tools.ISuperProvider;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
@ -31,6 +33,8 @@ import static libKonogonka.Converter.*;
|
||||||
* HFS0
|
* HFS0
|
||||||
* */
|
* */
|
||||||
public class HFS0Provider implements ISuperProvider {
|
public class HFS0Provider implements ISuperProvider {
|
||||||
|
private final static Logger log = LogManager.getLogger(HFS0Provider.class);
|
||||||
|
|
||||||
private final String magic;
|
private final String magic;
|
||||||
private final int filesCount;
|
private final int filesCount;
|
||||||
private final byte[] padding;
|
private final byte[] padding;
|
||||||
|
@ -136,7 +140,7 @@ public class HFS0Provider implements ISuperProvider {
|
||||||
PipedInputStream streamIn = new PipedInputStream(streamOut);
|
PipedInputStream streamIn = new PipedInputStream(streamOut);
|
||||||
|
|
||||||
workerThread = new Thread(() -> {
|
workerThread = new Thread(() -> {
|
||||||
System.out.println("HFS0Provider -> getHfs0FilePipedInpStream(): Executing thread");
|
log.trace("HFS0Provider -> getHfs0FilePipedInpStream(): Executing thread");
|
||||||
try{
|
try{
|
||||||
long subFileRealPosition = rawFileDataStart + hfs0Files[subFileNumber].getOffset();
|
long subFileRealPosition = rawFileDataStart + hfs0Files[subFileNumber].getOffset();
|
||||||
BufferedInputStream bis = new BufferedInputStream(Files.newInputStream(file.toPath()));
|
BufferedInputStream bis = new BufferedInputStream(Files.newInputStream(file.toPath()));
|
||||||
|
@ -164,10 +168,10 @@ public class HFS0Provider implements ISuperProvider {
|
||||||
streamOut.close();
|
streamOut.close();
|
||||||
}
|
}
|
||||||
catch (Exception ioe){
|
catch (Exception ioe){
|
||||||
System.out.println("HFS0Provider -> getHfs0FilePipedInpStream(): Unable to provide stream");
|
log.error("HFS0Provider -> getHfs0FilePipedInpStream(): Unable to provide stream");
|
||||||
ioe.printStackTrace();
|
ioe.printStackTrace();
|
||||||
}
|
}
|
||||||
System.out.println("HFS0Provider -> getHfs0FilePipedInpStream(): Thread died");
|
log.trace("HFS0Provider -> getHfs0FilePipedInpStream(): Thread died");
|
||||||
});
|
});
|
||||||
workerThread.start();
|
workerThread.start();
|
||||||
return streamIn;
|
return streamIn;
|
||||||
|
|
|
@ -18,20 +18,26 @@
|
||||||
*/
|
*/
|
||||||
package libKonogonka.Tools.XCI;
|
package libKonogonka.Tools.XCI;
|
||||||
|
|
||||||
|
import libKonogonka.Converter;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gamecard Cert
|
* Gamecard Cert
|
||||||
* */
|
* */
|
||||||
public class XCIGamecardCert {
|
public class XCIGamecardCert {
|
||||||
private byte[] rsa2048PKCS1sig;
|
private final static Logger log = LogManager.getLogger(XCIGamecardCert.class);
|
||||||
private byte[] magicCert;
|
|
||||||
private byte[] unknown1;
|
private final byte[] rsa2048PKCS1sig;
|
||||||
private byte kekIndex;
|
private final byte[] magicCert;
|
||||||
private byte[] unknown2;
|
private final byte[] unknown1;
|
||||||
private byte[] deviceID;
|
private final byte kekIndex;
|
||||||
private byte[] unknown3;
|
private final byte[] unknown2;
|
||||||
private byte[] encryptedData;
|
private final byte[] deviceID;
|
||||||
|
private final byte[] unknown3;
|
||||||
|
private final byte[] encryptedData;
|
||||||
|
|
||||||
XCIGamecardCert(byte[] certBytes) throws Exception{
|
XCIGamecardCert(byte[] certBytes) throws Exception{
|
||||||
if (certBytes.length != 512)
|
if (certBytes.length != 512)
|
||||||
|
@ -44,16 +50,6 @@ public class XCIGamecardCert {
|
||||||
deviceID = Arrays.copyOfRange(certBytes, 272, 288);
|
deviceID = Arrays.copyOfRange(certBytes, 272, 288);
|
||||||
unknown3 = Arrays.copyOfRange(certBytes, 288, 298);
|
unknown3 = Arrays.copyOfRange(certBytes, 288, 298);
|
||||||
encryptedData = Arrays.copyOfRange(certBytes, 298, 512);
|
encryptedData = Arrays.copyOfRange(certBytes, 298, 512);
|
||||||
/*
|
|
||||||
RainbowHexDump.hexDumpUTF8(rsa2048PKCS1sig);
|
|
||||||
RainbowHexDump.hexDumpUTF8(magicCert);
|
|
||||||
RainbowHexDump.hexDumpUTF8(unknown1);
|
|
||||||
System.out.println(kekIndex);
|
|
||||||
RainbowHexDump.hexDumpUTF8(unknown2);
|
|
||||||
RainbowHexDump.hexDumpUTF8(deviceID);
|
|
||||||
RainbowHexDump.hexDumpUTF8(unknown3);
|
|
||||||
RainbowHexDump.hexDumpUTF8(encryptedData);
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
public byte[] getRsa2048PKCS1sig() { return rsa2048PKCS1sig; }
|
public byte[] getRsa2048PKCS1sig() { return rsa2048PKCS1sig; }
|
||||||
public byte[] getMagicCert() { return magicCert; }
|
public byte[] getMagicCert() { return magicCert; }
|
||||||
|
@ -64,4 +60,17 @@ public class XCIGamecardCert {
|
||||||
public byte[] getDeviceID() { return deviceID; }
|
public byte[] getDeviceID() { return deviceID; }
|
||||||
public byte[] getUnknown3() { return unknown3; }
|
public byte[] getUnknown3() { return unknown3; }
|
||||||
public byte[] getEncryptedData() { return encryptedData; }
|
public byte[] getEncryptedData() { return encryptedData; }
|
||||||
|
|
||||||
|
public void printDebug(){
|
||||||
|
log.debug("== XCIGamecardCert ==\n" +
|
||||||
|
"rsa2048PKCS1sig " + Converter.byteArrToHexString(rsa2048PKCS1sig) + "\n" +
|
||||||
|
"magicCert " + Converter.byteArrToHexString(magicCert) + "\n" +
|
||||||
|
"unknown1 " + Converter.byteArrToHexString(unknown1) + "\n" +
|
||||||
|
"kekIndex " + kekIndex + "\n" +
|
||||||
|
"unknown2 " + Converter.byteArrToHexString(unknown2) + "\n" +
|
||||||
|
"deviceID " + Converter.byteArrToHexString(deviceID) + "\n" +
|
||||||
|
"unknown3 " + Converter.byteArrToHexString(unknown3) + "\n" +
|
||||||
|
"encryptedData " + Converter.byteArrToHexString(encryptedData) + "\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ import static libKonogonka.Converter.getLElong;
|
||||||
* Gamecard Info
|
* Gamecard Info
|
||||||
* */
|
* */
|
||||||
public class XCIGamecardInfo{
|
public class XCIGamecardInfo{
|
||||||
|
//private final static Logger log = LogManager.getLogger(XCIGamecardInfo.class);
|
||||||
|
|
||||||
private long fwVersion;
|
private long fwVersion;
|
||||||
private byte[] accessCtrlFlags; // 0x00A10011 for 25MHz access or 0x00A10010 for 50MHz access
|
private byte[] accessCtrlFlags; // 0x00A10011 for 25MHz access or 0x00A10010 for 50MHz access
|
||||||
|
@ -73,12 +74,12 @@ public class XCIGamecardInfo{
|
||||||
cupID = Arrays.copyOfRange(decrypted, 48, 56);
|
cupID = Arrays.copyOfRange(decrypted, 48, 56);
|
||||||
emptyPadding2 = Arrays.copyOfRange(decrypted, 56, 112);
|
emptyPadding2 = Arrays.copyOfRange(decrypted, 56, 112);
|
||||||
/*
|
/*
|
||||||
System.out.println(fwVersion);
|
log.debug(fwVersion);
|
||||||
RainbowHexDump.hexDumpUTF8(accessCtrlFlags);
|
RainbowHexDump.hexDumpUTF8(accessCtrlFlags);
|
||||||
System.out.println(readWaitTime1);
|
log.debug(readWaitTime1);
|
||||||
System.out.println(readWaitTime2);
|
log.debug(readWaitTime2);
|
||||||
System.out.println(writeWaitTime1);
|
log.debug(writeWaitTime1);
|
||||||
System.out.println(writeWaitTime2);
|
log.debug(writeWaitTime2);
|
||||||
RainbowHexDump.hexDumpUTF8(fwMode);
|
RainbowHexDump.hexDumpUTF8(fwMode);
|
||||||
RainbowHexDump.hexDumpUTF8(cupVersion);
|
RainbowHexDump.hexDumpUTF8(cupVersion);
|
||||||
RainbowHexDump.hexDumpUTF8(emptyPadding1);
|
RainbowHexDump.hexDumpUTF8(emptyPadding1);
|
||||||
|
|
|
@ -88,7 +88,7 @@ public class AesCtrBufferedInputStream extends BufferedInputStream {
|
||||||
//1
|
//1
|
||||||
System.arraycopy(decryptedBytes, pointerInsideDecryptedSection, b, 0, bytesFromFirstBlock);
|
System.arraycopy(decryptedBytes, pointerInsideDecryptedSection, b, 0, bytesFromFirstBlock);
|
||||||
//2
|
//2
|
||||||
System.out.println("\n"+bytesFromFirstBlock+"\n"+ middleBlocksCount+" = "+(middleBlocksCount*0x200)+" bytes\n"+ bytesFromEnd+"\n");
|
//log.debug("\n"+bytesFromFirstBlock+"\n"+ middleBlocksCount+" = "+(middleBlocksCount*0x200)+" bytes\n"+ bytesFromEnd+"\n");
|
||||||
for (int i = 0; i < middleBlocksCount; i++) {
|
for (int i = 0; i < middleBlocksCount; i++) {
|
||||||
fillDecryptedCache();
|
fillDecryptedCache();
|
||||||
System.arraycopy(decryptedBytes, 0, b, bytesFromFirstBlock+i*0x200, 0x200);
|
System.arraycopy(decryptedBytes, 0, b, bytesFromFirstBlock+i*0x200, 0x200);
|
||||||
|
|
|
@ -30,7 +30,6 @@ import org.junit.jupiter.api.DisplayName;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.file.Files;
|
|
||||||
|
|
||||||
public class NSOTest {
|
public class NSOTest {
|
||||||
private static final String keysFileLocation = "./FilesForTests/prod.keys";
|
private static final String keysFileLocation = "./FilesForTests/prod.keys";
|
||||||
|
@ -116,6 +115,7 @@ public class NSOTest {
|
||||||
|
|
||||||
NSO0Provider nso0Provider = new NSO0Provider(pfs0Provider.getStreamProducer(0));
|
NSO0Provider nso0Provider = new NSO0Provider(pfs0Provider.getStreamProducer(0));
|
||||||
nso0Provider.printDebug();
|
nso0Provider.printDebug();
|
||||||
|
nso0Provider.exportAsDecompressedNSO0("./tmp");
|
||||||
|
|
||||||
// NPDMProvider npdmProvider = new NPDMProvider(pfs0Provider.getProviderSubFilePipedInpStream(1));
|
// NPDMProvider npdmProvider = new NPDMProvider(pfs0Provider.getProviderSubFilePipedInpStream(1));
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue