NCA table/contoller/provider updates

master
Dmitry Isaenko 2019-08-19 17:22:42 +03:00
parent 643f09fd9e
commit 4e1bacd4fc
20 changed files with 450 additions and 302 deletions

View File

@ -14,8 +14,9 @@ Deep WIP multi-tool to work with XCI/NSP/NCA/NRO(?) files
### Information taken from
* Switch brew wiki
* roothorick, shchmue, He and others advices and notes. Thanks!
* Original ScriesM software
* Thanks to roothorick, shchmue, He and others for their advices, notes and examples
### System requirements
JRE/JDK 8u60 or higher.
JRE/JDK 8u60 or higher.

View File

@ -0,0 +1,70 @@
package konogonka.Child;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.stage.Stage;
import konogonka.Controllers.IRowModel;
import konogonka.Controllers.NCA.NCAController;
import konogonka.Tools.ISuperProvider;
import java.io.IOException;
import java.util.Locale;
import java.util.ResourceBundle;
public class ChildWindow {
public ChildWindow(ISuperProvider provider, IRowModel model) throws IOException{
Stage stageSettings = new Stage();
stageSettings.setMinWidth(570);
stageSettings.setMinHeight(500);
FXMLLoader loaderSettings;
if (model.getFileName().endsWith(".nca")){
loaderSettings = new FXMLLoader(getClass().getResource("/FXML/NCA/NCATab.fxml"));
}
else if(model.getFileName().endsWith(".cert")){
// TODO: IMPLEMENT
return;
}
else if(model.getFileName().endsWith(".tik")){
// TODO: IMPLEMENT
return;
}
else if(model.getFileName().endsWith(".xml")){
// TODO: IMPLEMENT
return;
}
else
return;
Locale userLocale = new Locale(Locale.getDefault().getISO3Language());
ResourceBundle resourceBundle = ResourceBundle.getBundle("locale", userLocale);
loaderSettings.setResources(resourceBundle);
Parent parentAbout = loaderSettings.load();
// TODO: REFACTOR
if (model.getFileName().endsWith(".nca")){
NCAController ncaController = loaderSettings.<NCAController>getController();
ncaController.analyze(provider.getFile(), provider.getRawFileDataStart()+model.getFileOffset());
}
stageSettings.setTitle(model.getFileName());
stageSettings.getIcons().addAll(
new Image(getClass().getResourceAsStream("/res/app_icon32x32.png")),
new Image(getClass().getResourceAsStream("/res/app_icon48x48.png")),
new Image(getClass().getResourceAsStream("/res/app_icon64x64.png")),
new Image(getClass().getResourceAsStream("/res/app_icon128x128.png"))
);
Scene settingsScene = new Scene(parentAbout, 800, 800);
settingsScene.getStylesheets().add("/res/app_light.css");
stageSettings.setScene(settingsScene);
stageSettings.setMinWidth(550.0);
stageSettings.setMinHeight(550.0);
stageSettings.show();
}
}

View File

@ -0,0 +1,12 @@
package konogonka.Controllers;
import javafx.fxml.Initializable;
import konogonka.Tools.ISuperProvider;
import java.io.File;
public interface ITabController extends Initializable {
void analyze(File file);
void analyze(ISuperProvider provider, int subFileNumber);
void resetTab();
}

View File

@ -6,11 +6,13 @@ import javafx.scene.control.*;
import javafx.scene.layout.AnchorPane;
import javafx.stage.FileChooser;
import konogonka.AppPreferences;
import konogonka.Child.ChildWindow;
import konogonka.Controllers.NCA.NCAController;
import konogonka.Controllers.NSP.NSPController;
import konogonka.Controllers.XCI.XCIController;
import konogonka.MediatorControl;
import konogonka.Settings.SettingsWindow;
import konogonka.Tools.ISuperProvider;
import java.io.*;
import java.net.URL;
@ -115,6 +117,14 @@ public class MainController implements Initializable {
else
splitPane.getItems().add(logPane);
}
public void showContentWindow(ISuperProvider provider, IRowModel model){
try{
new ChildWindow(provider, model);
}
catch (IOException e){
logArea.appendText("\nUnable to create windows for "+model.getFileName()+"\n"+e.getMessage());
}
};
public void exit(){ AppPreferences.getInstance().setRecentPath(previouslyOpenedPath); }
}

View File

@ -4,7 +4,8 @@ import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import konogonka.AppPreferences;
import konogonka.Controllers.TabController;
import konogonka.Controllers.ITabController;
import konogonka.Tools.ISuperProvider;
import konogonka.Tools.NCA.NCAContentPFS0;
import konogonka.Tools.NCA.NCAProvider;
import konogonka.Workers.AnalyzerNCA;
@ -16,7 +17,7 @@ import java.util.ResourceBundle;
import static konogonka.LoperConverter.byteArrToHexString;
public class NCAController implements TabController {
public class NCAController implements ITabController {
private File selectedFile;
@FXML
@ -46,6 +47,7 @@ public class NCAController implements TabController {
keyIndexLbl,
ncaSizeLbl,
titleIdLbl,
contentIndexLbl,
sdkVersionLbl,
cryptoType2Lbl,
ticketLbl;
@ -70,9 +72,12 @@ public class NCAController implements TabController {
public void initialize(URL url, ResourceBundle resourceBundle) {
}
@Override
public void analyze(File file) {
public void analyze(ISuperProvider provider, int subFileNumber){
// TODO
}
public void analyze(File file, long offset) {
this.selectedFile = file;
HashMap<String, String> keysMap = new HashMap<>();
keysMap.put("header_key", AppPreferences.getInstance().getHeaderKey());
@ -90,8 +95,8 @@ public class NCAController implements TabController {
if ( ! pair[0].equals("0") && ! pair[1].equals("0"))
keysMap.put(pair[0], pair[1]);
}
AnalyzerNCA analyzerNCA = new AnalyzerNCA(file, keysMap);
AnalyzerNCA analyzerNCA = new AnalyzerNCA(file, keysMap, offset);
analyzerNCA.setOnSucceeded(e->{
populateFields(analyzerNCA.getValue());
});
@ -100,6 +105,11 @@ public class NCAController implements TabController {
workThread.start();
}
@Override
public void analyze(File file) {
analyze(file, 0);
}
@Override
public void resetTab() {
// Header
@ -115,6 +125,7 @@ public class NCAController implements TabController {
sdkVersionLbl.setText("-");
cryptoType2Lbl.setText("-");
ticketLbl.setText("-");
contentIndexLbl.setText("-");
sha256section1TF.setText("-");
sha256section2TF.setText("-");
sha256section3TF.setText("-");
@ -156,6 +167,7 @@ public class NCAController implements TabController {
keyIndexLbl.setText(Byte.toString(ncaProvider.getKeyIndex()));
ncaSizeLbl.setText(Long.toString(ncaProvider.getNcaSize()));
titleIdLbl.setText(byteArrToHexString(ncaProvider.getTitleId()));
contentIndexLbl.setText(byteArrToHexString(ncaProvider.getContentIndx())); //
sdkVersionLbl.setText(ncaProvider.getSdkVersion()[3]
+"."+ncaProvider.getSdkVersion()[2]
+"."+ncaProvider.getSdkVersion()[1]

View File

@ -8,7 +8,6 @@ import javafx.scene.layout.VBox;
import konogonka.Controllers.NSP.NSPController;
import konogonka.LoperConverter;
import konogonka.Tools.PFS0.IPFS0Provider;
import konogonka.Tools.PFS0.PFS0Provider;
import java.io.File;
import java.util.LinkedList;

View File

@ -4,7 +4,7 @@ import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import konogonka.Controllers.IRowModel;
import konogonka.Controllers.TabController;
import konogonka.Controllers.ITabController;
import konogonka.MediatorControl;
import konogonka.Tools.ISuperProvider;
import konogonka.Tools.PFS0.IPFS0Provider;
@ -19,7 +19,7 @@ import java.util.ResourceBundle;
import static konogonka.LoperConverter.byteArrToHexString;
public class NSPController implements TabController {
public class NSPController implements ITabController {
@FXML
private Button extractBtn;
@ -92,6 +92,11 @@ public class NSPController implements TabController {
* Start analyze NSP
* */
@Override
public void analyze(ISuperProvider provider, int subFileNumber){
// TODO: IMPLEMENT
return;
}
@Override
public void analyze(File selectedFile){
this.selectedFile = selectedFile;
AnalyzerNSP analyzerNSP = new AnalyzerNSP(selectedFile);

View File

@ -1,10 +1,12 @@
package konogonka.Controllers.NSP;
import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
@ -17,6 +19,7 @@ import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.util.Callback;
import konogonka.Controllers.IRowModel;
import konogonka.MediatorControl;
import konogonka.Tools.ISuperProvider;
import konogonka.Tools.PFS0.IPFS0Provider;
@ -117,15 +120,30 @@ public class Pfs0TableViewController implements Initializable {
uploadColumn.setCellFactory(new Callback<TableColumn<Pfs0RowModel, Boolean>, TableCell<Pfs0RowModel, Boolean>>() {
@Override
public TableCell<Pfs0RowModel, Boolean> call(TableColumn<Pfs0RowModel, Boolean> paramFeatures) {
CheckBoxTableCell<Pfs0RowModel, Boolean> cell = new CheckBoxTableCell<>();
return cell;
return new CheckBoxTableCell<>();
}
});
table.setRowFactory( // this shit is made to implement context menu. It's such a pain..
new Callback<TableView<Pfs0RowModel>, TableRow<Pfs0RowModel>>() {
@Override
public TableRow<Pfs0RowModel> call(TableView<Pfs0RowModel> nslRowModelTableView) {
public TableRow<Pfs0RowModel> call(TableView<Pfs0RowModel> Pfs0RowModelTableView) {
final TableRow<Pfs0RowModel> row = new TableRow<>();
ContextMenu contextMenu = new ContextMenu();
MenuItem openMenuItem = new MenuItem("Open");
openMenuItem.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent actionEvent) {
MediatorControl.getInstance().getContoller().showContentWindow(provider, row.getItem()); // TODO: change to something better
}
});
contextMenu.getItems().addAll(openMenuItem);
row.setContextMenu(contextMenu);
row.contextMenuProperty().bind(
Bindings.when(Bindings.isNotNull(row.itemProperty())).then(contextMenu).otherwise((ContextMenu)null)
);
row.setOnMouseClicked(new EventHandler<MouseEvent>() { // Just.. don't ask..
@Override
public void handle(MouseEvent mouseEvent) {

View File

@ -1,10 +0,0 @@
package konogonka.Controllers;
import javafx.fxml.Initializable;
import java.io.File;
public interface TabController extends Initializable {
void analyze(File file);
void resetTab();
}

View File

@ -1,11 +1,11 @@
package konogonka.Controllers.XCI;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import konogonka.AppPreferences;
import konogonka.Controllers.TabController;
import konogonka.Controllers.ITabController;
import konogonka.Tools.ISuperProvider;
import konogonka.Tools.XCI.XCIProvider;
import konogonka.Workers.AnalyzerXCI;
@ -15,7 +15,7 @@ import java.util.ResourceBundle;
import static konogonka.LoperConverter.byteArrToHexString;
public class XCIController implements TabController {
public class XCIController implements ITabController {
/* Header */
@FXML
@ -90,6 +90,11 @@ public class XCIController implements TabController {
* Start analyze XCI
* */
@Override
public void analyze(ISuperProvider provider, int subFileNumber){
// TODO: IMPLEMENT
return;
}
@Override
public void analyze(File selectedFile){
HFSBlockController.setSelectedFile(selectedFile);

View File

@ -1,8 +1,12 @@
package konogonka.Tools;
import java.io.File;
import java.io.PipedInputStream;
public interface ISuperProvider {
PipedInputStream getProviderSubFilePipedInpStream(String subFileName);
PipedInputStream getProviderSubFilePipedInpStream(int subFileNumber);
PipedInputStream getProviderSubFilePipedInpStream(String subFileName) throws Exception;
PipedInputStream getProviderSubFilePipedInpStream(int subFileNumber) throws Exception;
File getFile();
long getRawFileDataStart();
}

View File

@ -71,12 +71,12 @@ public class NCAContentPFS0 {
private class CryptoSection03{
CryptoSection03(File file, long offsetPosition, byte[] decryptedKey, NCASectionBlock ncaSectionBlock, long mediaStartOffset, long mediaEndOffset) throws Exception{
CryptoSection03(File file, long offsetPosition, byte[] decryptedKey, NCASectionBlock ncaSectionBlock, long mediaStartBlocksOffset, long mediaEndBlocksOffset) throws Exception{
/*//--------------------------------------------------------------------------------------------------
System.out.println("Media start location: " + mediaStartOffset);
System.out.println("Media end location: " + mediaEndOffset);
System.out.println("Media size : " + (mediaEndOffset-mediaStartOffset));
System.out.println("Media act. location: " + (offsetPosition + (mediaStartOffset * 0x200)));
System.out.println("Media start location: " + mediaStartBlocksOffset);
System.out.println("Media end location: " + mediaEndBlocksOffset);
System.out.println("Media size : " + (mediaEndBlocksOffset-mediaStartBlocksOffset));
System.out.println("Media act. location: " + (offsetPosition + (mediaStartBlocksOffset * 0x200)));
System.out.println("SHA256 hash tbl size: " + ncaSectionBlock.getSuperBlockPFS0().getHashTableSize());
System.out.println("SHA256 hash tbl offs: " + ncaSectionBlock.getSuperBlockPFS0().getHashTableOffset());
System.out.println("PFS0 Offs: " + ncaSectionBlock.getSuperBlockPFS0().getPfs0offset());
@ -89,14 +89,14 @@ public class NCAContentPFS0 {
throw new Exception("CryptoSection03: unable to proceed. No decrypted key provided.");
RandomAccessFile raf = new RandomAccessFile(file, "r");
long abosluteOffsetPosition = offsetPosition + (mediaStartOffset * 0x200);
long abosluteOffsetPosition = offsetPosition + (mediaStartBlocksOffset * 0x200);
raf.seek(abosluteOffsetPosition);
AesCtrDecryptSimple decryptor = new AesCtrDecryptSimple(decryptedKey, ncaSectionBlock.getSectionCTR(), mediaStartOffset * 0x200);
AesCtrDecryptSimple decryptor = new AesCtrDecryptSimple(decryptedKey, ncaSectionBlock.getSectionCTR(), mediaStartBlocksOffset * 0x200);
byte[] encryptedBlock;
byte[] dectyptedBlock;
long mediaBlockSize = mediaEndOffset - mediaStartOffset;
long mediaBlocksSize = mediaEndBlocksOffset - mediaStartBlocksOffset;
// Prepare thread to parse encrypted data
PipedOutputStream streamOut = new PipedOutputStream();
PipedInputStream streamInp = new PipedInputStream(streamOut);
@ -110,12 +110,12 @@ public class NCAContentPFS0 {
file,
decryptedKey,
ncaSectionBlock.getSectionCTR(),
mediaStartOffset,
mediaEndOffset
mediaStartBlocksOffset,
mediaEndBlocksOffset
));
pThread.start();
// Decrypt data
for (int i = 0; i < mediaBlockSize; i++){
for (int i = 0; i < mediaBlocksSize; i++){
encryptedBlock = new byte[0x200];
if (raf.read(encryptedBlock) != -1){
//dectyptedBlock = aesCtr.decrypt(encryptedBlock);
@ -139,9 +139,9 @@ public class NCAContentPFS0 {
raf = new RandomAccessFile(file, "r");
raf.seek(abosluteOffsetPosition);
decryptor = new AesCtrDecryptSimple(decryptedKey, ncaSectionBlock.getSectionCTR(), mediaStartOffset * 0x200);
decryptor = new AesCtrDecryptSimple(decryptedKey, ncaSectionBlock.getSectionCTR(), mediaStartBlocksOffset * 0x200);
for (int i = 0; i < mediaBlockSize; i++){
for (int i = 0; i < mediaBlocksSize; i++){
encryptedBlock = new byte[0x200];
if (raf.read(encryptedBlock) != -1){
//dectyptedBlock = aesCtr.decrypt(encryptedBlock);

View File

@ -1,6 +1,5 @@
package konogonka.Tools.NCA;
import konogonka.LoperConverter;
import konogonka.Tools.NCA.NCASectionTableBlock.NCASectionBlock;
import konogonka.xtsaes.XTSAESCipher;
import org.bouncycastle.crypto.params.KeyParameter;
@ -30,6 +29,7 @@ public class NCAProvider {
private byte keyIndex; // application/ocean/system (kaek index?)
private long ncaSize; // Size of this NCA (bytes)
private byte[] titleId;
private byte[] contentIndx;
private byte[] sdkVersion; // version ver_revision.ver_micro.vev_minor.ver_major
private byte cryptoType2; // keyblob index. Considering as number within application/ocean/system
private byte[] rightsId;
@ -139,7 +139,8 @@ public class NCAProvider {
cryptoType1 = decryptedData[0x206];
keyIndex = decryptedData[0x207];
ncaSize = getLElong(decryptedData, 0x208);
titleId = Arrays.copyOfRange(decryptedData, 0x210, 0x21C); // 0x218 ?
titleId = Arrays.copyOfRange(decryptedData, 0x210, 0x218);
contentIndx = Arrays.copyOfRange(decryptedData, 0x218, 0x21C);
sdkVersion = Arrays.copyOfRange(decryptedData, 0x21c, 0x220);
cryptoType2 = decryptedData[0x220];
rightsId = Arrays.copyOfRange(decryptedData, 0x230, 0x240);
@ -214,6 +215,7 @@ public class NCAProvider {
public byte getKeyIndex() { return keyIndex; }
public long getNcaSize() { return ncaSize; }
public byte[] getTitleId() { return titleId; }
public byte[] getContentIndx() { return contentIndx; }
public byte[] getSdkVersion() { return sdkVersion; }
public byte getCryptoType2() { return cryptoType2; }
public byte[] getRightsId() { return rightsId; }
@ -273,7 +275,7 @@ public class NCAProvider {
}
switch (sectionNumber) {
case 0:
return new NCAContentPFS0(file, offset, sectionBlock0, tableEntry0, key); // TODO: remove decryptedKey2
return new NCAContentPFS0(file, offset, sectionBlock0, tableEntry0, key); // TODO: remove decryptedKey2 ?
case 1:
return new NCAContentPFS0(file, offset, sectionBlock1, tableEntry1, key);
case 2:

View File

@ -9,6 +9,5 @@ public interface IPFS0Provider extends ISuperProvider {
int getStringTableSize();
byte[] getPadding();
long getRawFileDataStart();
PFS0subFile[] getPfs0subFiles();
}

View File

@ -25,8 +25,8 @@ public class PFS0EncryptedProvider implements IPFS0Provider{
private File file;
private byte[] key;
private byte[] sectionCTR;
private long mediaStartOffset;
private long mediaEndOffset;
private long mediaStartOffset; // In 512-blocks
private long mediaEndOffset; // In 512-blocks
public PFS0EncryptedProvider(PipedInputStream pipedInputStream, long pfs0offsetPosition,
long offsetPositionInFile,
@ -44,7 +44,7 @@ public class PFS0EncryptedProvider implements IPFS0Provider{
this.mediaStartOffset = mediaStartOffset;
this.mediaEndOffset = mediaEndOffset;
// pfs0offsetPosition is a position relative to Media block. Lets add pfs0 'header's' bytes count and get raw data start position in media block
rawFileDataStart = -1; // Set -1 for PFS0EncryptedProvider
rawFileDataStart = -1; // Set -1 for PFS0EncryptedProvider
// Detect raw data start position using next var
rawBlockDataStart = pfs0offsetPosition;
@ -145,145 +145,137 @@ public class PFS0EncryptedProvider implements IPFS0Provider{
public long getRawFileDataStart() { return rawFileDataStart; }
@Override
public PFS0subFile[] getPfs0subFiles() { return pfs0subFiles; }
@Override
public PipedInputStream getProviderSubFilePipedInpStream(int subFileNumber) {
public File getFile(){ return file; }
@Override
public PipedInputStream getProviderSubFilePipedInpStream(int subFileNumber) throws Exception {
if (subFileNumber >= pfs0subFiles.length) {
System.out.println("PFS0Provider -> getPfs0subFilePipedInpStream(): Requested sub file doesn't exists");
return null;
throw new Exception("PFS0Provider -> getPfs0subFilePipedInpStream(): Requested sub file doesn't exists");
}
/*/------------------------------------------------------------------
System.out.println("Raw Block Data Start (PFS0 Start): " + rawBlockDataStart);
System.out.println("Skipped blocks: " + rawBlockDataStart/0x200); // aesCtrDecryptSimple.skip(THIS)
System.out.println("Skip bytes: " + (rawBlockDataStart-(rawBlockDataStart/0x200)*0x200)); // write to stream after skiping THIS
*///-----------------------------------------------------------------
Thread workerThread;
PipedOutputStream streamOut = new PipedOutputStream();
try{
PipedInputStream streamIn = new PipedInputStream(streamOut);
workerThread = new Thread(() -> {
System.out.println("PFS0EncryptedProvider -> getPfs0subFilePipedInpStream(): Executing thread");
try {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
// Let's store what we're about to skip
int skipBytes = (int) (offsetPositionInFile + (mediaStartOffset * 0x200));
// Check if skip was successful
PipedInputStream streamIn = new PipedInputStream(streamOut);
workerThread = new Thread(() -> {
System.out.println("PFS0EncryptedProvider -> getPfs0subFilePipedInpStream(): Executing thread");
try {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
// Let's store what we're about to skip
int skipBytes = (int) (offsetPositionInFile + (mediaStartOffset * 0x200));
// Check if skip was successful
if (bis.skip(skipBytes) != skipBytes) {
System.out.println("PFS0EncryptedProvider -> getPfs0subFilePipedInpStream(): Failed to skip range "+skipBytes);
return;
}
AesCtrDecryptSimple aesCtrDecryptSimple = new AesCtrDecryptSimple(key, sectionCTR, mediaStartOffset * 0x200);
byte[] encryptedBlock;
byte[] dectyptedBlock;
//----------------------------- Pre-set: skip non-necessary data --------------------------------
long startBlock = (rawBlockDataStart + pfs0subFiles[subFileNumber].getOffset()) / 0x200; // <- pointing to place where actual data starts
if (startBlock > 0) {
aesCtrDecryptSimple.skipNext(startBlock);
skipBytes = (int)(startBlock * 0x200);
if (bis.skip(skipBytes) != skipBytes) {
System.out.println("PFS0EncryptedProvider -> getPfs0subFilePipedInpStream(): Failed to skip range "+skipBytes);
return;
}
AesCtrDecryptSimple aesCtrDecryptSimple = new AesCtrDecryptSimple(key, sectionCTR, mediaStartOffset * 0x200);
byte[] encryptedBlock;
byte[] dectyptedBlock;
//----------------------------- Pre-set: skip non-necessary data --------------------------------
long startBlock = (rawBlockDataStart + pfs0subFiles[subFileNumber].getOffset()) / 0x200; // <- pointing to place where actual data starts
if (startBlock > 0) {
aesCtrDecryptSimple.skipNext(startBlock);
skipBytes = (int)(startBlock * 0x200);
if (bis.skip(skipBytes) != skipBytes) {
System.out.println("PFS0EncryptedProvider -> getPfs0subFilePipedInpStream(): Failed to skip range "+skipBytes);
return;
}
}
//----------------------------- Step 1: get starting bytes from the end of the junk block --------------------------------
// Since our data could be located in position with some offset from the decrypted block, let's skip bytes left. Considering the case when data is not aligned to block
skipBytes = (int) ( (rawBlockDataStart + pfs0subFiles[subFileNumber].getOffset()) - startBlock * 0x200); // <- How much bytes shall we skip to reach requested data start of sub-file
if (skipBytes > 0) {
encryptedBlock = new byte[0x200];
if (bis.read(encryptedBlock) == 0x200) {
dectyptedBlock = aesCtrDecryptSimple.dectyptNext(encryptedBlock);
// If we have extra-small file that is less then a block and even more
if ((0x200 - skipBytes) > pfs0subFiles[subFileNumber].getSize()){
streamOut.write(dectyptedBlock, skipBytes, (int) pfs0subFiles[subFileNumber].getSize()); // safe cast
return;
}
else
streamOut.write(dectyptedBlock, skipBytes, 0x200 - skipBytes);
}
else {
System.out.println("PFS0EncryptedProvider -> getProviderSubFilePipedInpStream(): Unable to get 512 bytes from 1st bock");
return;
}
startBlock++;
}
long endBlock = pfs0subFiles[subFileNumber].getSize() / 0x200 + startBlock; // <- pointing to place where any data related to this media-block ends
//----------------------------- Step 2: Detect if we have junk data on the end of the final block --------------------------------
int extraData = (int)(rawBlockDataStart+pfs0subFiles[subFileNumber].getOffset()+pfs0subFiles[subFileNumber].getSize() - (endBlock*0x200)); // safe cast
if (extraData < 0){
endBlock--;
}
//----------------------------- Step 3: Read main part of data --------------------------------
// Here we're reading main amount of bytes. We can read only less bytes.
while ( startBlock < endBlock) {
encryptedBlock = new byte[0x200];
if (bis.read(encryptedBlock) == 0x200) {
//dectyptedBlock = aesCtr.decrypt(encryptedBlock);
dectyptedBlock = aesCtrDecryptSimple.dectyptNext(encryptedBlock);
// Writing decrypted data to pipe
streamOut.write(dectyptedBlock);
}
else {
System.out.println("PFS0EncryptedProvider -> getProviderSubFilePipedInpStream(): Unable to get 512 bytes from bock");
return;
}
startBlock++;
}
//----------------------------- Step 4: Read what's left --------------------------------
// Now we have to find out if data overlaps to one more extra block
if (extraData > 0){ // In case we didn't get what we want
encryptedBlock = new byte[0x200];
if (bis.read(encryptedBlock) == 0x200) {
dectyptedBlock = aesCtrDecryptSimple.dectyptNext(encryptedBlock);
streamOut.write(dectyptedBlock, 0, extraData);
}
else {
System.out.println("PFS0EncryptedProvider -> getProviderSubFilePipedInpStream(): Unable to get 512 bytes from 1st bock");
return;
}
}
else if (extraData < 0){ // In case we can get more than we need
encryptedBlock = new byte[0x200];
if (bis.read(encryptedBlock) == 0x200) {
dectyptedBlock = aesCtrDecryptSimple.dectyptNext(encryptedBlock);
streamOut.write(dectyptedBlock, 0, 0x200 + extraData);
}
else {
System.out.println("PFS0EncryptedProvider -> getProviderSubFilePipedInpStream(): Unable to get 512 bytes from 1st bock");
return;
}
}
bis.close();
streamOut.close();
}
catch (Exception e){
System.out.println("PFS0EncryptedProvider -> getProviderSubFilePipedInpStream(): "+e.getMessage());
e.printStackTrace();
//----------------------------- Step 1: get starting bytes from the end of the junk block --------------------------------
// Since our data could be located in position with some offset from the decrypted block, let's skip bytes left. Considering the case when data is not aligned to block
skipBytes = (int) ( (rawBlockDataStart + pfs0subFiles[subFileNumber].getOffset()) - startBlock * 0x200); // <- How much bytes shall we skip to reach requested data start of sub-file
if (skipBytes > 0) {
encryptedBlock = new byte[0x200];
if (bis.read(encryptedBlock) == 0x200) {
dectyptedBlock = aesCtrDecryptSimple.dectyptNext(encryptedBlock);
// If we have extra-small file that is less then a block and even more
if ((0x200 - skipBytes) > pfs0subFiles[subFileNumber].getSize()){
streamOut.write(dectyptedBlock, skipBytes, (int) pfs0subFiles[subFileNumber].getSize()); // safe cast
return;
}
else
streamOut.write(dectyptedBlock, skipBytes, 0x200 - skipBytes);
}
else {
System.out.println("PFS0EncryptedProvider -> getProviderSubFilePipedInpStream(): Unable to get 512 bytes from 1st bock");
return;
}
startBlock++;
}
System.out.println("PFS0EncryptedProvider -> getPfs0subFilePipedInpStream(): Thread died");
long endBlock = pfs0subFiles[subFileNumber].getSize() / 0x200 + startBlock; // <- pointing to place where any data related to this media-block ends
//----------------------------- Step 2: Detect if we have junk data on the end of the final block --------------------------------
int extraData = (int)(rawBlockDataStart+pfs0subFiles[subFileNumber].getOffset()+pfs0subFiles[subFileNumber].getSize() - (endBlock*0x200)); // safe cast
if (extraData < 0){
endBlock--;
}
//----------------------------- Step 3: Read main part of data --------------------------------
// Here we're reading main amount of bytes. We can read only less bytes.
while ( startBlock < endBlock) {
encryptedBlock = new byte[0x200];
if (bis.read(encryptedBlock) == 0x200) {
//dectyptedBlock = aesCtr.decrypt(encryptedBlock);
dectyptedBlock = aesCtrDecryptSimple.dectyptNext(encryptedBlock);
// Writing decrypted data to pipe
streamOut.write(dectyptedBlock);
}
else {
System.out.println("PFS0EncryptedProvider -> getProviderSubFilePipedInpStream(): Unable to get 512 bytes from bock");
return;
}
startBlock++;
}
//----------------------------- Step 4: Read what's left --------------------------------
// Now we have to find out if data overlaps to one more extra block
if (extraData > 0){ // In case we didn't get what we want
encryptedBlock = new byte[0x200];
if (bis.read(encryptedBlock) == 0x200) {
dectyptedBlock = aesCtrDecryptSimple.dectyptNext(encryptedBlock);
streamOut.write(dectyptedBlock, 0, extraData);
}
else {
System.out.println("PFS0EncryptedProvider -> getProviderSubFilePipedInpStream(): Unable to get 512 bytes from 1st bock");
return;
}
}
else if (extraData < 0){ // In case we can get more than we need
encryptedBlock = new byte[0x200];
if (bis.read(encryptedBlock) == 0x200) {
dectyptedBlock = aesCtrDecryptSimple.dectyptNext(encryptedBlock);
streamOut.write(dectyptedBlock, 0, 0x200 + extraData);
}
else {
System.out.println("PFS0EncryptedProvider -> getProviderSubFilePipedInpStream(): Unable to get 512 bytes from 1st bock");
return;
}
}
bis.close();
streamOut.close();
}
catch (Exception e){
System.out.println("PFS0EncryptedProvider -> getProviderSubFilePipedInpStream(): "+e.getMessage());
e.printStackTrace();
}
System.out.println("PFS0EncryptedProvider -> getPfs0subFilePipedInpStream(): Thread died");
});
workerThread.start();
return streamIn;
}
catch (IOException ioe){
System.out.println("PFS0Provider -> getPfs0subFilePipedInpStream(): Unable to provide stream");
return null;
}
});
workerThread.start();
return streamIn;
}
@Override
public PipedInputStream getProviderSubFilePipedInpStream(String subFileName) {
public PipedInputStream getProviderSubFilePipedInpStream(String subFileName) throws Exception{
for (int i = 0; i < pfs0subFiles.length; i++){
if (pfs0subFiles[i].getName().equals(subFileName))
return getProviderSubFilePipedInpStream(i);

View File

@ -110,68 +110,61 @@ public class PFS0Provider implements IPFS0Provider{
@Override
public PFS0subFile[] getPfs0subFiles() { return pfs0subFiles; }
@Override
public PipedInputStream getProviderSubFilePipedInpStream(int subFileNumber){ // TODO: Throw exceptions?
public File getFile(){ return file; }
@Override
public PipedInputStream getProviderSubFilePipedInpStream(int subFileNumber) throws Exception{ // TODO: Throw exceptions?
if (subFileNumber >= pfs0subFiles.length) {
System.out.println("PFS0Provider -> getPfs0subFilePipedInpStream(): Requested sub file doesn't exists");
return null;
throw new Exception("PFS0Provider -> getPfs0subFilePipedInpStream(): Requested sub file doesn't exists");
}
PipedOutputStream streamOut = new PipedOutputStream();
Thread workerThread;
try{
PipedInputStream streamIn = new PipedInputStream(streamOut);
workerThread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("PFS0Provider -> getPfs0subFilePipedInpStream(): Executing thread");
try {
long subFileRealPosition = rawFileDataStart + pfs0subFiles[subFileNumber].getOffset();
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
if (bis.skip(subFileRealPosition) != subFileRealPosition) {
System.out.println("PFS0Provider -> getPfs0subFilePipedInpStream(): Unable to skip requested offset");
return;
}
PipedInputStream streamIn = new PipedInputStream(streamOut);
int readPice = 8388608; // 8mb NOTE: consider switching to 1mb 1048576
long readFrom = 0;
long realFileSize = pfs0subFiles[subFileNumber].getSize();
byte[] readBuf;
while (readFrom < realFileSize) {
if (realFileSize - readFrom < readPice)
readPice = Math.toIntExact(realFileSize - readFrom); // it's safe, I guarantee
readBuf = new byte[readPice];
if (bis.read(readBuf) != readPice) {
System.out.println("PFS0Provider -> getPfs0subFilePipedInpStream(): Unable to read requested size from file.");
return;
}
streamOut.write(readBuf);
readFrom += readPice;
}
bis.close();
streamOut.close();
} catch (IOException ioe) {
System.out.println("PFS0Provider -> getPfs0subFilePipedInpStream(): Unable to provide stream");
ioe.printStackTrace();
}
System.out.println("PFS0Provider -> getPfs0subFilePipedInpStream(): Thread died");
workerThread = new Thread(() -> {
System.out.println("PFS0Provider -> getPfs0subFilePipedInpStream(): Executing thread");
try {
long subFileRealPosition = rawFileDataStart + pfs0subFiles[subFileNumber].getOffset();
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
if (bis.skip(subFileRealPosition) != subFileRealPosition) {
System.out.println("PFS0Provider -> getPfs0subFilePipedInpStream(): Unable to skip requested offset");
return;
}
});
workerThread.start();
return streamIn;
}
catch (IOException ioe){
System.out.println("PFS0Provider -> getPfs0subFilePipedInpStream(): Unable to provide stream");
return null;
}
int readPice = 8388608; // 8mb NOTE: consider switching to 1mb 1048576
long readFrom = 0;
long realFileSize = pfs0subFiles[subFileNumber].getSize();
byte[] readBuf;
while (readFrom < realFileSize) {
if (realFileSize - readFrom < readPice)
readPice = Math.toIntExact(realFileSize - readFrom); // it's safe, I guarantee
readBuf = new byte[readPice];
if (bis.read(readBuf) != readPice) {
System.out.println("PFS0Provider -> getPfs0subFilePipedInpStream(): Unable to read requested size from file.");
return;
}
streamOut.write(readBuf);
readFrom += readPice;
}
bis.close();
streamOut.close();
} catch (IOException ioe) {
System.out.println("PFS0Provider -> getPfs0subFilePipedInpStream(): Unable to provide stream");
ioe.printStackTrace();
}
System.out.println("PFS0Provider -> getPfs0subFilePipedInpStream(): Thread died");
});
workerThread.start();
return streamIn;
}
/**
* Some sugar
* */
@Override
public PipedInputStream getProviderSubFilePipedInpStream(String subFileName){
public PipedInputStream getProviderSubFilePipedInpStream(String subFileName) throws Exception {
for (int i = 0; i < pfs0subFiles.length; i++){
if (pfs0subFiles[i].getName().equals(subFileName))
return getProviderSubFilePipedInpStream(i);

View File

@ -105,71 +105,65 @@ public class HFS0Provider implements ISuperProvider {
public int getFilesCnt() { return filesCnt; }
public boolean isPaddingHfs0() { return paddingHfs0; }
public int getStringTableSize() { return stringTableSize; }
@Override
public long getRawFileDataStart() { return rawFileDataStart; }
public HFS0File[] getHfs0Files() { return hfs0Files; }
@Override
public PipedInputStream getProviderSubFilePipedInpStream(int subFileNumber){
public File getFile(){ return file; }
@Override
public PipedInputStream getProviderSubFilePipedInpStream(int subFileNumber) throws Exception{
PipedOutputStream streamOut = new PipedOutputStream();
Thread workerThread;
if (subFileNumber >= hfs0Files.length) {
System.out.println("HFS0Provider -> getHfs0FilePipedInpStream(): Requested sub file doesn't exists");
return null;
throw new Exception("HFS0Provider -> getHfs0FilePipedInpStream(): Requested sub file doesn't exists");
}
try{
PipedInputStream streamIn = new PipedInputStream(streamOut);
PipedInputStream streamIn = new PipedInputStream(streamOut);
workerThread = new Thread(() -> {
System.out.println("HFS0Provider -> getHfs0FilePipedInpStream(): Executing thread");
try{
long subFileRealPosition = rawFileDataStart + hfs0Files[subFileNumber].getOffset();
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
if (bis.skip(subFileRealPosition) != subFileRealPosition) {
System.out.println("HFS0Provider -> getHfs0FilePipedInpStream(): Unable to skip requested offset");
workerThread = new Thread(() -> {
System.out.println("HFS0Provider -> getHfs0FilePipedInpStream(): Executing thread");
try{
long subFileRealPosition = rawFileDataStart + hfs0Files[subFileNumber].getOffset();
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
if (bis.skip(subFileRealPosition) != subFileRealPosition) {
System.out.println("HFS0Provider -> getHfs0FilePipedInpStream(): Unable to skip requested offset");
return;
}
int readPice = 8388608; // 8mb NOTE: consider switching to 1mb 1048576
long readFrom = 0;
long realFileSize = hfs0Files[subFileNumber].getSize();
byte[] readBuf;
while (readFrom < realFileSize){
if (realFileSize - readFrom < readPice)
readPice = Math.toIntExact(realFileSize - readFrom); // it's safe, I guarantee
readBuf = new byte[readPice];
if (bis.read(readBuf) != readPice) {
System.out.println("HFS0Provider -> getHfs0FilePipedInpStream(): Unable to read requested size from file.");
return;
}
int readPice = 8388608; // 8mb NOTE: consider switching to 1mb 1048576
long readFrom = 0;
long realFileSize = hfs0Files[subFileNumber].getSize();
byte[] readBuf;
while (readFrom < realFileSize){
if (realFileSize - readFrom < readPice)
readPice = Math.toIntExact(realFileSize - readFrom); // it's safe, I guarantee
readBuf = new byte[readPice];
if (bis.read(readBuf) != readPice) {
System.out.println("HFS0Provider -> getHfs0FilePipedInpStream(): Unable to read requested size from file.");
return;
}
streamOut.write(readBuf, 0, readPice);
readFrom += readPice;
}
bis.close();
streamOut.close();
streamOut.write(readBuf, 0, readPice);
readFrom += readPice;
}
catch (IOException ioe){
System.out.println("HFS0Provider -> getHfs0FilePipedInpStream(): Unable to provide stream");
ioe.printStackTrace();
}
System.out.println("HFS0Provider -> getHfs0FilePipedInpStream(): Thread died");
});
workerThread.start();
return streamIn;
}
catch (IOException ioe){
System.out.println("HFS0Provider -> getHfs0FilePipedInpStream(): Unable to provide stream");
return null;
}
bis.close();
streamOut.close();
}
catch (IOException ioe){
System.out.println("HFS0Provider -> getHfs0FilePipedInpStream(): Unable to provide stream");
ioe.printStackTrace();
}
System.out.println("HFS0Provider -> getHfs0FilePipedInpStream(): Thread died");
});
workerThread.start();
return streamIn;
}
/**
* Sugar
* */
@Override
public PipedInputStream getProviderSubFilePipedInpStream(String subFileName){
public PipedInputStream getProviderSubFilePipedInpStream(String subFileName) throws Exception {
for (int i = 0; i < hfs0Files.length; i++){
if (hfs0Files[i].getName().equals(subFileName))
return getProviderSubFilePipedInpStream(i);

View File

@ -3,6 +3,7 @@ package konogonka.Workers;
import javafx.concurrent.Task;
import konogonka.ModelControllers.EMsgType;
import konogonka.ModelControllers.LogPrinter;
import konogonka.Tools.ISuperProvider;
import konogonka.Tools.NCA.NCAProvider;
import java.io.File;
@ -11,11 +12,18 @@ import java.util.HashMap;
public class AnalyzerNCA extends Task<NCAProvider> {
private File file;
private long offset;
private LogPrinter logPrinter;
private HashMap<String, String> keysMap;
public AnalyzerNCA(File file, HashMap<String, String> keysMap){
this(file, keysMap, 0);
}
public AnalyzerNCA(File file, HashMap<String, String> keysMap, long offset){
this.file = file;
this.offset = offset;
this.logPrinter = new LogPrinter();
this.keysMap = keysMap;
}
@ -27,7 +35,7 @@ public class AnalyzerNCA extends Task<NCAProvider> {
NCAProvider ncaProvider;
try {
ncaProvider = new NCAProvider(file, keysMap);
ncaProvider = new NCAProvider(file, keysMap, offset);
}catch (Exception e){
logPrinter.print(e.getMessage(), EMsgType.FAIL);
ncaProvider = null;

View File

@ -22,9 +22,6 @@ public class NspXciExtractor extends Task<Void> {
this.models = models;
this.filesDestPath = filesDestPath;
this.logPrinter = new LogPrinter();
for (IRowModel model : models) {
System.out.println(model.getFileName());
}
}
@Override
@ -60,8 +57,8 @@ public class NspXciExtractor extends Task<Void> {
getException().printStackTrace(); // TODO: Do something with this
}
extractedFileBOS.close();
} catch (IOException ioe) {
logPrinter.print("\tRead/Write error\n\t" + ioe.getMessage(), EMsgType.INFO);
} catch (Exception ioe) {
logPrinter.print("\tExtracting issue\n\t" + ioe.getMessage(), EMsgType.INFO);
return null;
} finally {
logPrinter.print("\tEnd extracting", EMsgType.INFO);

View File

@ -37,6 +37,7 @@
<RowConstraints maxHeight="30.0" minHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="30.0" minHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="30.0" minHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="30.0" minHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="30.0" minHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="30.0" minHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="30.0" minHeight="30.0" vgrow="SOMETIMES" />
@ -72,7 +73,7 @@
</Label>
</children>
</AnchorPane>
<AnchorPane styleClass="customGrid" GridPane.rowIndex="13">
<AnchorPane styleClass="customGrid" GridPane.rowIndex="14">
<children>
<Label text="0x240" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<padding>
@ -108,7 +109,7 @@
</Label>
</children>
</AnchorPane>
<AnchorPane styleClass="customGrid" GridPane.rowIndex="11">
<AnchorPane styleClass="customGrid" GridPane.rowIndex="12">
<children>
<Label text="0x220" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<padding>
@ -135,7 +136,7 @@
</Label>
</children>
</AnchorPane>
<AnchorPane styleClass="customGrid" GridPane.rowIndex="10">
<AnchorPane styleClass="customGrid" GridPane.rowIndex="11">
<children>
<Label text="0x21C" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<padding>
@ -144,7 +145,7 @@
</Label>
</children>
</AnchorPane>
<AnchorPane styleClass="customGrid" GridPane.rowIndex="12">
<AnchorPane styleClass="customGrid" GridPane.rowIndex="13">
<children>
<Label text="0x230" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<padding>
@ -162,7 +163,7 @@
</Label>
</children>
</AnchorPane>
<AnchorPane styleClass="customGrid" GridPane.rowIndex="14">
<AnchorPane styleClass="customGrid" GridPane.rowIndex="15">
<children>
<Label text="0x280" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<padding>
@ -171,7 +172,7 @@
</Label>
</children>
</AnchorPane>
<AnchorPane styleClass="customGrid" GridPane.rowIndex="15">
<AnchorPane styleClass="customGrid" GridPane.rowIndex="16">
<children>
<Label text="0x300" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<padding>
@ -243,7 +244,7 @@
</Label>
</children>
</AnchorPane>
<AnchorPane styleClass="customGrid" GridPane.columnIndex="1" GridPane.rowIndex="15">
<AnchorPane styleClass="customGrid" GridPane.columnIndex="1" GridPane.rowIndex="16">
<children>
<Label text="0x10*0x4(0x40)" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<padding>
@ -252,7 +253,7 @@
</Label>
</children>
</AnchorPane>
<AnchorPane styleClass="customGrid" GridPane.columnIndex="1" GridPane.rowIndex="14">
<AnchorPane styleClass="customGrid" GridPane.columnIndex="1" GridPane.rowIndex="15">
<children>
<Label text="0x20*0x4(0x80)" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<padding>
@ -261,7 +262,7 @@
</Label>
</children>
</AnchorPane>
<AnchorPane styleClass="customGrid" GridPane.columnIndex="1" GridPane.rowIndex="13">
<AnchorPane styleClass="customGrid" GridPane.columnIndex="1" GridPane.rowIndex="14">
<children>
<Label text="0x10*0x4(0x40)" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<padding>
@ -324,7 +325,7 @@
</Label>
</children>
</AnchorPane>
<AnchorPane styleClass="customGrid" GridPane.columnIndex="1" GridPane.rowIndex="10">
<AnchorPane styleClass="customGrid" GridPane.columnIndex="1" GridPane.rowIndex="11">
<children>
<Label text="0x4" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<padding>
@ -333,7 +334,7 @@
</Label>
</children>
</AnchorPane>
<AnchorPane styleClass="customGrid" GridPane.columnIndex="1" GridPane.rowIndex="11">
<AnchorPane styleClass="customGrid" GridPane.columnIndex="1" GridPane.rowIndex="12">
<children>
<Label text="0x1" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<padding>
@ -342,7 +343,7 @@
</Label>
</children>
</AnchorPane>
<AnchorPane styleClass="customGrid" GridPane.columnIndex="1" GridPane.rowIndex="12">
<AnchorPane styleClass="customGrid" GridPane.columnIndex="1" GridPane.rowIndex="13">
<children>
<Label text="0x10" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<padding>
@ -362,7 +363,7 @@
</AnchorPane>
<AnchorPane styleClass="customGrid" GridPane.columnIndex="2" GridPane.rowIndex="6">
<children>
<Label text="Crypto Type. Only used stating with 3.0.0. Normally 0. 2 = Crypto supported starting with 3.0.0." AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<Label text="Old Key Generation. Only used stating with 3.0.0. Normally 0. 2 = Crypto supported starting with 3.0.0." AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
@ -387,7 +388,7 @@
</Label>
</children>
</AnchorPane>
<AnchorPane styleClass="customGrid" GridPane.columnIndex="2" GridPane.rowIndex="10">
<AnchorPane styleClass="customGrid" GridPane.columnIndex="2" GridPane.rowIndex="11">
<children>
<Label text="sdk_version. byte0 usually 0? Known minimum-value(0x000B0000). Calc: byte3.byte2.byte1.byte0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<padding>
@ -396,16 +397,16 @@
</Label>
</children>
</AnchorPane>
<AnchorPane styleClass="customGrid" GridPane.columnIndex="2" GridPane.rowIndex="11">
<AnchorPane styleClass="customGrid" GridPane.columnIndex="2" GridPane.rowIndex="12">
<children>
<Label text="Crypto-Type2. Selects which crypto-sysver to use. 0x3 = 3.0.1, 0x4 = 4.0.0, 0x5 = 5.0.0." AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<Label text="Key Generation. Selects which crypto-sysver to use. 0x3 = 3.0.1, 0x4 = 4.0.0, 0x5 = 5.0.0." AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
</children>
</AnchorPane>
<AnchorPane styleClass="customGrid" GridPane.columnIndex="2" GridPane.rowIndex="12">
<AnchorPane styleClass="customGrid" GridPane.columnIndex="2" GridPane.rowIndex="13">
<children>
<Label text="Rights ID (title.keys key-name to decrypt)" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<padding>
@ -414,7 +415,7 @@
</Label>
</children>
</AnchorPane>
<AnchorPane styleClass="customGrid" GridPane.columnIndex="2" GridPane.columnSpan="2" GridPane.rowIndex="13">
<AnchorPane styleClass="customGrid" GridPane.columnIndex="2" GridPane.columnSpan="2" GridPane.rowIndex="14">
<children>
<VBox spacing="5.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
@ -437,7 +438,7 @@
</VBox>
</children>
</AnchorPane>
<AnchorPane styleClass="customGrid" GridPane.columnIndex="2" GridPane.rowIndex="14">
<AnchorPane styleClass="customGrid" GridPane.columnIndex="2" GridPane.rowIndex="15">
<children>
<Label text="Table of SHA256 hashes, over each 0x200-byte Section Header Block." AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<padding>
@ -446,7 +447,7 @@
</Label>
</children>
</AnchorPane>
<AnchorPane styleClass="customGrid" GridPane.columnIndex="2" GridPane.rowIndex="15">
<AnchorPane styleClass="customGrid" GridPane.columnIndex="2" GridPane.rowIndex="16">
<children>
<Label text="Key area" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<padding>
@ -518,7 +519,7 @@
</Label>
</children>
</AnchorPane>
<AnchorPane styleClass="customGrid" GridPane.columnIndex="3" GridPane.rowIndex="10">
<AnchorPane styleClass="customGrid" GridPane.columnIndex="3" GridPane.rowIndex="11">
<children>
<Label fx:id="sdkVersionLbl" text="-" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<padding>
@ -527,7 +528,7 @@
</Label>
</children>
</AnchorPane>
<AnchorPane styleClass="customGrid" GridPane.columnIndex="3" GridPane.rowIndex="11">
<AnchorPane styleClass="customGrid" GridPane.columnIndex="3" GridPane.rowIndex="12">
<children>
<Label fx:id="cryptoType2Lbl" text="-" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<padding>
@ -536,7 +537,7 @@
</Label>
</children>
</AnchorPane>
<AnchorPane styleClass="customGrid" GridPane.columnIndex="3" GridPane.rowIndex="12">
<AnchorPane styleClass="customGrid" GridPane.columnIndex="3" GridPane.rowIndex="13">
<children>
<Label fx:id="ticketLbl" text="-" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<padding>
@ -564,7 +565,7 @@
<TextField fx:id="rsa2048twoTF" editable="false" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
</children>
</AnchorPane>
<AnchorPane styleClass="customGrid" GridPane.columnIndex="3" GridPane.rowIndex="14">
<AnchorPane styleClass="customGrid" GridPane.columnIndex="3" GridPane.rowIndex="15">
<children>
<VBox spacing="3.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
@ -579,7 +580,7 @@
</VBox>
</children>
</AnchorPane>
<AnchorPane styleClass="customGrid" GridPane.columnIndex="3" GridPane.rowIndex="15">
<AnchorPane styleClass="customGrid" GridPane.columnIndex="3" GridPane.rowIndex="16">
<children>
<VBox spacing="3.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<padding>
@ -648,6 +649,42 @@
</Label>
</children>
</AnchorPane>
<AnchorPane styleClass="customGrid" GridPane.rowIndex="10">
<children>
<Label text="0x218" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
</children>
</AnchorPane>
<AnchorPane styleClass="customGrid" GridPane.columnIndex="1" GridPane.rowIndex="10">
<children>
<Label text="0x4" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
</children>
</AnchorPane>
<AnchorPane styleClass="customGrid" GridPane.columnIndex="2" GridPane.rowIndex="10">
<children>
<Label text="Content Index" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
</children>
</AnchorPane>
<AnchorPane styleClass="customGrid" GridPane.columnIndex="3" GridPane.rowIndex="10">
<children>
<Label fx:id="contentIndexLbl" text="-" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
</children>
</AnchorPane>
</children>
</GridPane>
</children>