Drafts for RomFs support

master
Dmitry Isaenko 2020-04-19 04:42:54 +03:00
parent 24d0925dda
commit 33ef6c5bf8
15 changed files with 445 additions and 38 deletions

View File

@ -28,6 +28,7 @@ import konogonka.Child.ChildWindow;
import konogonka.Controllers.NCA.NCAController;
import konogonka.Controllers.NPDM.NPDMController;
import konogonka.Controllers.NSP.NSPController;
import konogonka.Controllers.RFS.RomFsController;
import konogonka.Controllers.TIK.TIKController;
import konogonka.Controllers.XCI.XCIController;
import konogonka.Controllers.XML.XMLController;
@ -71,6 +72,8 @@ public class MainController implements Initializable {
private XMLController XMLTabController;
@FXML
private NPDMController NPDMTabController;
@FXML
private RomFsController RFSTabController;
private File selectedFile;
@ -104,10 +107,23 @@ public class MainController implements Initializable {
else
fileChooser.setInitialDirectory(new File(System.getProperty("user.home")));
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("NS files", "*.nsp", "*.nsz", "*.xci", "*.nca", "*.tik", "*.xml", "*.npdm"));
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("NS files",
"*.nsp", "*.nsz", "*.xci", "*.nca", "*.tik", "*.xml", "*.npdm", "*.romfs"));
this.selectedFile = fileChooser.showOpenDialog(analyzeBtn.getScene().getWindow());
// todo: fix
if (this.selectedFile != null && this.selectedFile.exists()) {
resetAllTabsContent();
filenameSelected.setText(this.selectedFile.getAbsolutePath());
previouslyOpenedPath = this.selectedFile.getParent();
analyzeBtn.setDisable(false);
String fileExtension = this.selectedFile.getName().toLowerCase().replaceAll("^.*\\.", "");
setFocusOnPane(fileExtension);
}
logArea.clear();
}
private void resetAllTabsContent(){
analyzeBtn.setDisable(true);
NSPTabController.resetTab();
XCITabController.resetTab();
@ -115,36 +131,32 @@ public class MainController implements Initializable {
TIKTabController.resetTab();
XMLTabController.resetTab();
NPDMTabController.resetTab();
if (this.selectedFile != null && this.selectedFile.exists()) {
filenameSelected.setText(this.selectedFile.getAbsolutePath());
previouslyOpenedPath = this.selectedFile.getParent();
analyzeBtn.setDisable(false);
String fileExtension = this.selectedFile.getName().toLowerCase().replaceAll("^.*\\.", "");
switch (fileExtension){
case "nsp":
case "nsz":
tabPane.getSelectionModel().select(0);
break;
case "xci":
tabPane.getSelectionModel().select(1);
break;
case "nca":
tabPane.getSelectionModel().select(2);
break;
case "tic":
tabPane.getSelectionModel().select(3);
break;
case "xml":
tabPane.getSelectionModel().select(4);
break;
case "npdm":
tabPane.getSelectionModel().select(5);
break;
}
RFSTabController.resetTab();
}
private void setFocusOnPane(String fileExtension){
switch (fileExtension){
case "nsp":
case "nsz":
tabPane.getSelectionModel().select(0);
break;
case "xci":
tabPane.getSelectionModel().select(1);
break;
case "nca":
tabPane.getSelectionModel().select(2);
break;
case "tic":
tabPane.getSelectionModel().select(3);
break;
case "xml":
tabPane.getSelectionModel().select(4);
break;
case "npdm":
tabPane.getSelectionModel().select(5);
break;
case "romfs":
tabPane.getSelectionModel().select(6);
}
logArea.clear();
}
/**
* Start analyze
@ -154,7 +166,7 @@ public class MainController implements Initializable {
switch (fileExtension){
case "nsp":
case "nsz":
NSPTabController.analyze(selectedFile); // TODO: NSP OR XCI
NSPTabController.analyze(selectedFile); // TODO: NSP OR NSZ ?
break;
case "xci":
XCITabController.analyze(selectedFile);
@ -171,6 +183,8 @@ public class MainController implements Initializable {
case "npdm":
NPDMTabController.analyze(selectedFile);
break;
case "romfs":
RFSTabController.analyze(selectedFile);
}
}
@FXML

View File

@ -0,0 +1,36 @@
/*
Copyright 2019-2020 Dmitry Isaenko
This file is part of Konogonka.
Konogonka 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.
Konogonka 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 Konogonka. If not, see <https://www.gnu.org/licenses/>.
*/
package konogonka.Controllers.RFS;
public class RFSFolderEntry {
private String name;
public RFSFolderEntry(String name){
this.name = name;
}
public String getName() {
return name;
}
@Override
public String toString(){
return name;
}
}

View File

@ -0,0 +1,81 @@
/*
Copyright 2019-2020 Dmitry Isaenko
This file is part of Konogonka.
Konogonka 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.
Konogonka 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 Konogonka. If not, see <https://www.gnu.org/licenses/>.
*/
package konogonka.Controllers.RFS;
import javafx.fxml.FXML;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import konogonka.Controllers.ITabController;
import konogonka.Tools.ISuperProvider;
import java.io.File;
import java.net.URL;
import java.util.ResourceBundle;
public class RomFsController implements ITabController {
@FXML
private TreeView<RFSFolderEntry> filesTreeView;
@FXML
private VBox folderContentVBox;
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
TreeItem<RFSFolderEntry> rootTest = getEmptyRoot();
TreeItem<RFSFolderEntry> test = new TreeItem<>(new RFSFolderEntry("WIP"), getFolderImage());
rootTest.getChildren().add(test);
filesTreeView.setRoot(rootTest);
}
@Override
public void analyze(File file) {
this.analyze(file, 0);
}
@Override
public void analyze(File file, long offset) {
TreeItem<RFSFolderEntry> rootItem = getEmptyRoot();
filesTreeView.setRoot(rootItem);
}
@Override
public void analyze(ISuperProvider parentProvider, int fileNo) throws Exception {
}
@Override
public void resetTab() {
filesTreeView.setRoot(null);
}
private Region getFolderImage(){
final Region folderImage = new Region();
folderImage.getStyleClass().add("regionFolder");
return folderImage;
}
private TreeItem<RFSFolderEntry> getEmptyRoot(){
return new TreeItem<>(new RFSFolderEntry("/"));
}
}

View File

@ -23,7 +23,7 @@ import java.nio.charset.StandardCharsets;
/**
* Debug tool like hexdump <3
*/
public class RainbowHexDump {
public class RainbowDump {
private static final String ANSI_RESET = "\u001B[0m";
private static final String ANSI_BLACK = "\u001B[30m";
private static final String ANSI_RED = "\u001B[31m";
@ -55,4 +55,8 @@ public class RainbowHexDump {
public static void octDumpLong(long value){
System.out.println(String.format("%64s", Long.toBinaryString( value )).replace(' ', '0')+" | "+value);
}
public static String formatDecHexString(long value){
return String.format("%-20d 0x%x", value, value);
}
}

View File

@ -313,7 +313,6 @@ public class NCAContent {
}
}
private class CryptoSection03RomFS{
CryptoSection03RomFS(File file,
long offsetPosition,
byte[] decryptedKey,

View File

@ -19,12 +19,9 @@
package konogonka.Tools.NPDM;
import konogonka.LoperConverter;
import konogonka.RainbowHexDump;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map;
/*
NOTE: This implementation is extremely bad for using application as library. Use raw for own purposes.

View File

@ -0,0 +1,29 @@
/*
Copyright 2019-2020 Dmitry Isaenko
This file is part of Konogonka.
Konogonka 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.
Konogonka 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 Konogonka. If not, see <https://www.gnu.org/licenses/>.
*/
package konogonka.Tools.RomFs;
public class FileMeta {
private int containingDirectoryOffset;
private int nextSiblingFileOffset;
private long fileDataOffset;
private long fileDataLength;
private int nextFileOffset;
private int fileNameLength;
private String fileName;
}

View File

@ -0,0 +1,29 @@
/*
Copyright 2019-2020 Dmitry Isaenko
This file is part of Konogonka.
Konogonka 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.
Konogonka 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 Konogonka. If not, see <https://www.gnu.org/licenses/>.
*/
package konogonka.Tools.RomFs;
public class FolderMeta {
private int parentDirectoryOffset;
private int nextSiblingDirectoryOffset;
private int firstSubdirectoryOffset;
private int firstFileOffset;
private int nextDirectoryOffset;
private int dirNameLength;
private String dirName;
}

View File

@ -0,0 +1,84 @@
/*
* Copyright 2019-2020 Dmitry Isaenko
*
* This file is part of Konogonka.
*
* Konogonka 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.
*
* Konogonka 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 Konogonka. If not, see <https://www.gnu.org/licenses/>.
*/
package konogonka.Tools.RomFs;
import konogonka.LoperConverter;
import konogonka.RainbowDump;
public class Level6Header {
private long headerHeaderLength;
private long headerDirectoryHashTableOffset;
private long headerDirectoryHashTableLength;
private long headerDirectoryMetadataTableOffset;
private long headerDirectoryMetadataTableLength;
private long headerFileHashTableOffset;
private long headerFileHashTableLength;
private long headerFileMetadataTableOffset;
private long headerFileMetadataTableLength;
private long headerFileDataOffset;
Level6Header(byte[] headerBytes){
int i = 0;
headerHeaderLength = LoperConverter.getLEint(headerBytes, i);
i += 0x8;
headerDirectoryHashTableOffset = LoperConverter.getLEint(headerBytes, i);
i += 0x8;
headerDirectoryHashTableLength = LoperConverter.getLEint(headerBytes, i);
i += 0x8;
headerDirectoryMetadataTableOffset = LoperConverter.getLEint(headerBytes, i);
i += 0x8;
headerDirectoryMetadataTableLength = LoperConverter.getLEint(headerBytes, i);
i += 0x8;
headerFileHashTableOffset = LoperConverter.getLEint(headerBytes, i);
i += 0x8;
headerFileHashTableLength = LoperConverter.getLEint(headerBytes, i);
i += 0x8;
headerFileMetadataTableOffset = LoperConverter.getLEint(headerBytes, i);
i += 0x8;
headerFileMetadataTableLength = LoperConverter.getLEint(headerBytes, i);
i += 0x8;
headerFileDataOffset = LoperConverter.getLEint(headerBytes, i);
System.out.println("== Level 6 Header ==\n" +
"Header Length (always 0x50 ?) "+ RainbowDump.formatDecHexString(headerHeaderLength)+" (size of this structure within first 0x200 block of LEVEL 6 part)\n" +
"Directory Hash Table Offset "+ RainbowDump.formatDecHexString(headerDirectoryHashTableOffset)+" (against THIS block where HEADER contains)\n" +
"Directory Hash Table Length "+ RainbowDump.formatDecHexString(headerDirectoryHashTableLength) + "\n" +
"Directory Metadata Table Offset "+ RainbowDump.formatDecHexString(headerDirectoryMetadataTableOffset) + "\n" +
"Directory Metadata Table Length "+ RainbowDump.formatDecHexString(headerDirectoryMetadataTableLength) + "\n" +
"File Hash Table Offset "+ RainbowDump.formatDecHexString(headerFileHashTableOffset) + "\n" +
"File Hash Table Length "+ RainbowDump.formatDecHexString(headerFileHashTableLength) + "\n" +
"File Metadata Table Offset "+ RainbowDump.formatDecHexString(headerFileMetadataTableOffset) + "\n" +
"File Metadata Table Length "+ RainbowDump.formatDecHexString(headerFileMetadataTableLength) + "\n" +
"File Data Offset "+ RainbowDump.formatDecHexString(headerFileDataOffset) + "\n" +
"-------------------------------------------------------------"
);
}
public long getHeaderHeaderLength() { return headerHeaderLength; }
public long getHeaderDirectoryHashTableOffset() { return headerDirectoryHashTableOffset; }
public long getHeaderDirectoryHashTableLength() { return headerDirectoryHashTableLength; }
public long getHeaderDirectoryMetadataTableOffset() { return headerDirectoryMetadataTableOffset; }
public long getHeaderDirectoryMetadataTableLength() { return headerDirectoryMetadataTableLength; }
public long getHeaderFileHashTableOffset() { return headerFileHashTableOffset; }
public long getHeaderFileHashTableLength() { return headerFileHashTableLength; }
public long getHeaderFileMetadataTableOffset() { return headerFileMetadataTableOffset; }
public long getHeaderFileMetadataTableLength() { return headerFileMetadataTableLength; }
public long getHeaderFileDataOffset() { return headerFileDataOffset; }
}

View File

@ -0,0 +1,87 @@
/*
* Copyright 2019-2020 Dmitry Isaenko
*
* This file is part of Konogonka.
*
* Konogonka 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.
*
* Konogonka 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 Konogonka. If not, see <https://www.gnu.org/licenses/>.
*/
package konogonka.Tools.RomFs;
import konogonka.Tools.ISuperProvider;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.PipedInputStream;
public class RomFsDecryptedProvider implements ISuperProvider {
private static final long LEVEL_6_DEFAULT_OFFSET = 0x14000;
private File decryptedFSImage;
private Level6Header header;
public RomFsDecryptedProvider(File decryptedFSImage) throws Exception{ // TODO: add default setup AND using meta-data headers from NCA RomFs section (?)
this.decryptedFSImage = decryptedFSImage;
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(decryptedFSImage));
skipTo(bis, LEVEL_6_DEFAULT_OFFSET);
byte[] rawDataChunk = new byte[0x50];
if (bis.read(rawDataChunk) != 0x50)
throw new Exception("Failed to read header (0x50)");
this.header = new Level6Header(rawDataChunk);
bis.close();
}
private void skipTo(BufferedInputStream bis, long size) throws Exception{
long mustSkip = size;
long skipped = 0;
while (mustSkip > 0){
skipped += bis.skip(mustSkip);
mustSkip = size - skipped;
}
}
private int getRealNameSize(int value){
if (value % 4 == 0)
return value;
return value + 4 - value % 4;
}
public Level6Header getHeader() { return header; }
@Override
public PipedInputStream getProviderSubFilePipedInpStream(String subFileName) throws Exception {
return null;
}
@Override
public PipedInputStream getProviderSubFilePipedInpStream(int subFileNumber) throws Exception {
return null;
}
@Override
public File getFile() {
return decryptedFSImage;
}
@Override
public long getRawFileDataStart() {
return 0;
}
}

View File

@ -18,7 +18,6 @@
*/
package konogonka.Tools.XCI;
import konogonka.RainbowHexDump;
import konogonka.Tools.ISuperProvider;
import java.io.*;

View File

@ -4,7 +4,10 @@
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Separator?>
<?import javafx.scene.control.SplitPane?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.TitledPane?>
<?import javafx.scene.control.TreeView?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
@ -61,5 +64,20 @@
<fx:include fx:id="SectionPFS0" source="../NSP/NSPTab.fxml" />
</VBox>
</content></TitledPane>
<TitledPane prefHeight="200.0" prefWidth="200.0" text="'RomFS'">
<content>
<VBox spacing="5.0">
<children>
<Label text="WIP" />
<SplitPane dividerPositions="0.5" prefHeight="160.0" prefWidth="200.0">
<items>
<TreeView prefHeight="200.0" prefWidth="200.0" />
<TableView prefHeight="200.0" prefWidth="200.0" />
</items>
</SplitPane>
</children>
</VBox>
</content>
</TitledPane>
</children>
</VBox>

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.SplitPane?>
<?import javafx.scene.control.TreeView?>
<?import javafx.scene.layout.VBox?>
<SplitPane dividerPositions="0.2" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1" fx:controller="konogonka.Controllers.RFS.RomFsController">
<items>
<TreeView fx:id="filesTreeView" />
<VBox fx:id="folderContentVBox">
</VBox>
</items>
</SplitPane>

View File

@ -95,6 +95,14 @@
<Label text="NPDM" />
</graphic>
</Tab>
<Tab styleClass="tab-sub">
<content>
<fx:include fx:id="RFSTab" source="RomFS/RFSTab.fxml" />
</content>
<graphic>
<Label text="RomFS" />
</graphic>
</Tab>
</tabs>
</TabPane>
<AnchorPane fx:id="logPane" prefHeight="200.0" prefWidth="200.0">

View File

@ -426,4 +426,11 @@
.titled-pane:focused > .title > .arrow-button .arrow
{
-fx-background-color: white, white;
}
.regionFolder {
-fx-shape: "M10,4H4C2.89,4 2,4.89 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V8C22,6.89 21.1,6 20,6H12L10,4Z";
-fx-background-color: #ffbf00;
-fx-min-height: 17.0;
-fx-min-width: 17.5;
}