RomFs support: Frontend implementation for decrypted blobs (no export functionality); backend structures corrected, updated and ready for usage.

Bug fix for NSP table implementation: checkbox is visible now.
Fix compilation time warnings regarding JavaFX unsafe stuff.
This commit is contained in:
Dmitry Isaenko 2020-04-26 05:06:17 +03:00
parent 5c0b037fa8
commit bad2924c02
22 changed files with 1300 additions and 195 deletions

View file

@ -8,7 +8,7 @@
<name>konogonka</name>
<artifactId>konogonka</artifactId>
<version>0.0.2-SNAPSHOT</version>
<version>0.0.3-SNAPSHOT</version>
<url>https://github.com/developersu/${project.name}}/</url>
<description>

View file

@ -21,6 +21,8 @@ package konogonka.Controllers;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
import javafx.scene.input.DragEvent;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.AnchorPane;
import javafx.stage.FileChooser;
import konogonka.AppPreferences;
@ -35,10 +37,10 @@ import konogonka.Controllers.XML.XMLController;
import konogonka.MediatorControl;
import konogonka.Settings.SettingsWindow;
import konogonka.Tools.ISuperProvider;
import konogonka.Tools.TIK.TIKProvider;
import java.io.*;
import java.net.URL;
import java.util.List;
import java.util.ResourceBundle;
public class MainController implements Initializable {
@ -187,6 +189,21 @@ public class MainController implements Initializable {
RFSTabController.analyze(selectedFile);
}
}
private boolean isNotSupportedFileFormat(String fileExtension){
switch (fileExtension){
case "nsp":
case "nsz":
case "xci":
case "nca":
case "tic":
case "xml":
case "npdm":
case "romfs":
return false;
default:
return true;
}
}
@FXML
private void showHideLogs(){
if (splitPane.getItems().size() == 2)
@ -194,6 +211,50 @@ public class MainController implements Initializable {
else
splitPane.getItems().add(logPane);
}
/**
* Drag-n-drop support (dragOver consumer)
* */
@FXML
private void handleDragOver(DragEvent event){
event.acceptTransferModes(TransferMode.ANY);
event.consume();
}
/**
* Drag-n-drop support (drop consumer)
* */
@FXML
private void handleDrop(DragEvent event){
List<File> filesDropped = event.getDragboard().getFiles();
if ( filesDropped.isEmpty() ) {
event.setDropCompleted(true);
event.consume();
return;
}
File droppedFile = filesDropped.get(0);
String fileExtension = droppedFile.getName().toLowerCase().replaceAll("^.*\\.", "");
if (isNotSupportedFileFormat(fileExtension)) {
event.setDropCompleted(true);
event.consume();
return;
}
selectedFile = droppedFile;
resetAllTabsContent();
filenameSelected.setText(selectedFile.getAbsolutePath());
previouslyOpenedPath = selectedFile.getParent();
analyzeBtn.setDisable(false);
setFocusOnPane(fileExtension);
event.setDropCompleted(true);
event.consume();
}
public void showContentWindow(ISuperProvider provider, IRowModel model){
try{
new ChildWindow(provider, model);

View file

@ -18,11 +18,8 @@
*/
package konogonka.Controllers.NSP;
import javafx.beans.Observable;
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;
@ -35,8 +32,6 @@ import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
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;
@ -44,7 +39,6 @@ import konogonka.Tools.PFS0.IPFS0Provider;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.ResourceBundle;
@ -82,13 +76,13 @@ public class Pfs0TableViewController implements Initializable {
TableColumn<Pfs0RowModel, String> fileNameColumn = new TableColumn<>(resourceBundle.getString("tableFileNameLbl"));
TableColumn<Pfs0RowModel, Long> fileOffsetColumn = new TableColumn<>(resourceBundle.getString("tableOffsetLbl"));
TableColumn<Pfs0RowModel, Long> fileSizeColumn = new TableColumn<>(resourceBundle.getString("tableSizeLbl"));
TableColumn<Pfs0RowModel, Boolean> uploadColumn = new TableColumn<>(resourceBundle.getString("tableUploadLbl"));
TableColumn<Pfs0RowModel, Boolean> checkBoxColumn = new TableColumn<>(resourceBundle.getString("tableUploadLbl"));
numberColumn.setEditable(false);
fileNameColumn.setEditable(false);
fileOffsetColumn.setEditable(false);
fileSizeColumn.setEditable(false);
uploadColumn.setEditable(true);
checkBoxColumn.setEditable(true);
// See https://bugs.openjdk.java.net/browse/JDK-8157687
numberColumn.setMinWidth(30.0);
@ -108,17 +102,17 @@ public class Pfs0TableViewController implements Initializable {
fileSizeColumn.setMaxWidth(120.0);
fileSizeColumn.setResizable(false);
uploadColumn.setMinWidth(120.0);
uploadColumn.setPrefWidth(120.0);
uploadColumn.setMaxWidth(120.0);
uploadColumn.setResizable(false);
checkBoxColumn.setMinWidth(120.0);
checkBoxColumn.setPrefWidth(120.0);
checkBoxColumn.setMaxWidth(120.0);
checkBoxColumn.setResizable(false);
numberColumn.setCellValueFactory(new PropertyValueFactory<>("number"));
fileNameColumn.setCellValueFactory(new PropertyValueFactory<>("fileName"));
fileSizeColumn.setCellValueFactory(new PropertyValueFactory<>("fileSize"));
fileOffsetColumn.setCellValueFactory(new PropertyValueFactory<>("fileOffset"));
// ><
uploadColumn.setCellValueFactory(paramFeatures -> {
checkBoxColumn.setCellValueFactory(paramFeatures -> {
Pfs0RowModel model = paramFeatures.getValue();
SimpleBooleanProperty booleanProperty = new SimpleBooleanProperty(model.isMarkSelected());
@ -130,7 +124,7 @@ public class Pfs0TableViewController implements Initializable {
return booleanProperty;
});
uploadColumn.setCellFactory(paramFeatures -> new CheckBoxTableCell<>());
checkBoxColumn.setCellFactory(paramFeatures -> new CheckBoxTableCell<>());
table.setRowFactory( // this shit is made to implement context menu. It's such a pain..
Pfs0RowModelTableView -> {
final TableRow<Pfs0RowModel> row = new TableRow<>();
@ -167,6 +161,7 @@ public class Pfs0TableViewController implements Initializable {
table.getColumns().add(fileNameColumn);
table.getColumns().add(fileOffsetColumn);
table.getColumns().add(fileSizeColumn);
table.getColumns().add(checkBoxColumn);
}
/**
* Add files when user selected them

View file

@ -0,0 +1,59 @@
/*
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 konogonka.Controllers.IRowModel;
import konogonka.Tools.RomFs.FileSystemEntry;
public class RFSEntry implements IRowModel {
private FileSystemEntry fileSystemEntry;
private boolean check;
public RFSEntry(FileSystemEntry fileSystemEntry){
this.fileSystemEntry = fileSystemEntry;
}
public boolean isDirectory(){
return fileSystemEntry.isDirectory();
}
@Override
public String toString(){
return fileSystemEntry.getName();
}
@Override
public int getNumber() { return 0; }
@Override
public String getFileName() { return fileSystemEntry.getName(); }
@Override
public long getFileSize() { return fileSystemEntry.getFileSize(); }
@Override
public long getFileOffset() { return fileSystemEntry.getFileOffset(); }
@Override
public boolean isMarkSelected() { return check; }
@Override
public void setMarkSelected(boolean value) { check = value; }
}

View file

@ -1,37 +0,0 @@
/*
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,172 @@
/*
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.beans.binding.Bindings;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
import javafx.scene.control.cell.CheckBoxTableCell;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.input.KeyCode;
import javafx.scene.input.MouseButton;
import konogonka.Controllers.IRowModel;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;
public class RFSFolderTableViewController implements Initializable {
@FXML
private TableView<RFSEntry> table;
private ObservableList<RFSEntry> rowsObsLst;
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
rowsObsLst = FXCollections.observableArrayList();
table.setPlaceholder(new Label());
table.setEditable(false); // At least with hacks it works as expected. Otherwise - null pointer exception
table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
table.setOnKeyPressed(keyEvent -> {
if (!rowsObsLst.isEmpty()) {
if (keyEvent.getCode() == KeyCode.SPACE) {
for (RFSEntry item : table.getSelectionModel().getSelectedItems()) {
item.setMarkSelected( ! item.isMarkSelected());
}
table.refresh();
}
}
keyEvent.consume();
});
TableColumn<RFSEntry, Integer> numberColumn = new TableColumn<>(resourceBundle.getString("tableNumberLbl"));
TableColumn<RFSEntry, String> fileNameColumn = new TableColumn<>(resourceBundle.getString("tableFileNameLbl"));
TableColumn<RFSEntry, Long> fileOffsetColumn = new TableColumn<>(resourceBundle.getString("tableOffsetLbl"));
TableColumn<RFSEntry, Long> fileSizeColumn = new TableColumn<>(resourceBundle.getString("tableSizeLbl"));
TableColumn<RFSEntry, Boolean> checkBoxColumn = new TableColumn<>(resourceBundle.getString("tableUploadLbl"));
numberColumn.setEditable(false);
fileNameColumn.setEditable(false);
fileOffsetColumn.setEditable(false);
fileSizeColumn.setEditable(false);
checkBoxColumn.setEditable(true);
// See https://bugs.openjdk.java.net/browse/JDK-8157687
numberColumn.setMinWidth(30.0);
numberColumn.setPrefWidth(30.0);
numberColumn.setMaxWidth(30.0);
numberColumn.setResizable(false);
fileNameColumn.setMinWidth(25.0);
fileOffsetColumn.setMinWidth(130.0);
fileOffsetColumn.setPrefWidth(130.0);
fileOffsetColumn.setMaxWidth(130.0);
fileOffsetColumn.setResizable(false);
fileSizeColumn.setMinWidth(120.0);
fileSizeColumn.setPrefWidth(120.0);
fileSizeColumn.setMaxWidth(120.0);
fileSizeColumn.setResizable(false);
checkBoxColumn.setMinWidth(120.0);
checkBoxColumn.setPrefWidth(120.0);
checkBoxColumn.setMaxWidth(120.0);
checkBoxColumn.setResizable(false);
numberColumn.setCellValueFactory(new PropertyValueFactory<>("number"));
fileNameColumn.setCellValueFactory(new PropertyValueFactory<>("fileName"));
fileSizeColumn.setCellValueFactory(new PropertyValueFactory<>("fileSize"));
fileOffsetColumn.setCellValueFactory(new PropertyValueFactory<>("fileOffset"));
// ><
checkBoxColumn.setCellValueFactory(paramFeatures -> {
RFSEntry model = paramFeatures.getValue();
SimpleBooleanProperty booleanProperty = new SimpleBooleanProperty(model.isMarkSelected());
booleanProperty.addListener((observableValue, oldValue, newValue) -> {
model.setMarkSelected(newValue);
table.refresh();
});
return booleanProperty;
});
checkBoxColumn.setCellFactory(paramFeatures -> new CheckBoxTableCell<>());
table.setRowFactory( // this shit is made to implement context menu. It's such a pain..
RFSEntryTableView -> {
final TableRow<RFSEntry> row = new TableRow<>();
ContextMenu contextMenu = new ContextMenu();
/* // TODO: CHANGE TO 'Export' or something
MenuItem openMenuItem = new MenuItem("Open");
openMenuItem.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent actionEvent) {
MediatorControl.getInstance().getContoller().showContentWindow(provider, row.getItem());
}
});
contextMenu.getItems().addAll(openMenuItem);
*/
row.setContextMenu(contextMenu);
row.contextMenuProperty().bind(
Bindings.when(Bindings.isNotNull(row.itemProperty())).then(contextMenu).otherwise((ContextMenu)null)
);
// Just.. don't ask..
row.setOnMouseClicked(mouseEvent -> {
if (!row.isEmpty() && mouseEvent.getButton() == MouseButton.PRIMARY){
RFSEntry thisItem = row.getItem();
thisItem.setMarkSelected(!thisItem.isMarkSelected());
table.refresh();
}
mouseEvent.consume();
});
return row;
}
);
table.setItems(rowsObsLst);
table.getColumns().add(numberColumn);
table.getColumns().add(fileNameColumn);
table.getColumns().add(fileOffsetColumn);
table.getColumns().add(fileSizeColumn);
table.getColumns().add(checkBoxColumn);
}
/**
* Add files when user selected them on left-hand tree
* */
public void setContent(TreeItem<RFSEntry> containerTreeItem){
rowsObsLst.clear();
if (containerTreeItem == null) {
table.refresh();
return;
}
for (TreeItem<RFSEntry> childTreeItem : containerTreeItem.getChildren())
rowsObsLst.add(childTreeItem.getValue());
table.refresh();
}
}

View file

@ -19,12 +19,14 @@
package konogonka.Controllers.RFS;
import javafx.fxml.FXML;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.control.*;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.util.Callback;
import konogonka.Controllers.ITabController;
import konogonka.Tools.ISuperProvider;
import konogonka.Tools.RomFs.FileSystemEntry;
import konogonka.Tools.RomFs.Level6Header;
import konogonka.Tools.RomFs.RomFsDecryptedProvider;
import java.io.File;
@ -34,20 +36,48 @@ import java.util.ResourceBundle;
public class RomFsController implements ITabController {
@FXML
private TreeView<RFSFolderEntry> filesTreeView;
private Label headerHeaderLengthLbl,
headerDirectoryHashTableOffsetLbl,
headerDirectoryHashTableLengthLbl,
headerDirectoryMetadataTableOffsetLbl,
headerDirectoryMetadataTableLengthLbl,
headerFileHashTableOffsetLbl,
headerFileHashTableLengthLbl,
headerFileMetadataTableOffsetLbl,
headerFileMetadataTableLengthLbl,
headerFileDataOffsetLbl;
@FXML
private VBox folderContentVBox;
private Label headerHeaderLengthHexLbl,
headerDirectoryHashTableOffsetHexLbl,
headerDirectoryHashTableLengthHexLbl,
headerDirectoryMetadataTableOffsetHexLbl,
headerDirectoryMetadataTableLengthHexLbl,
headerFileHashTableOffsetHexLbl,
headerFileHashTableLengthHexLbl,
headerFileMetadataTableOffsetHexLbl,
headerFileMetadataTableLengthHexLbl,
headerFileDataOffsetHexLbl;
@FXML
private TreeView<RFSEntry> filesTreeView;
private RomFsDecryptedProvider RomFsProvider;
@FXML
private RFSFolderTableViewController RFSTableViewController;
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
TreeItem<RFSFolderEntry> rootTest = getEmptyRoot();
TreeItem<RFSFolderEntry> test = new TreeItem<>(new RFSFolderEntry("WIP"), getFolderImage());
filesTreeView.setOnMouseClicked(mouseEvent -> {
TreeItem<RFSEntry> item = filesTreeView.getSelectionModel().getSelectedItem();
if (item != null && item.getValue().isDirectory())
RFSTableViewController.setContent(item);
mouseEvent.consume();
});
}
rootTest.getChildren().add(test);
private final class RFSTreeCell extends TreeCell<RFSEntry>{
filesTreeView.setRoot(rootTest);
}
@Override
@ -59,22 +89,96 @@ public class RomFsController implements ITabController {
public void analyze(File file, long offset) {
try {
this.RomFsProvider = new RomFsDecryptedProvider(file);
Level6Header header = RomFsProvider.getHeader();
long tempValue;
tempValue = header.getHeaderLength();
headerHeaderLengthLbl.setText(Long.toString(tempValue));
headerHeaderLengthHexLbl.setText(getHexString(tempValue));
tempValue = header.getDirectoryHashTableOffset();
headerDirectoryHashTableOffsetLbl.setText(Long.toString(tempValue));
headerDirectoryHashTableOffsetHexLbl.setText(getHexString(tempValue));
tempValue = header.getDirectoryHashTableLength();
headerDirectoryHashTableLengthLbl.setText(Long.toString(tempValue));
headerDirectoryHashTableLengthHexLbl.setText(getHexString(tempValue));
tempValue = header.getDirectoryMetadataTableOffset();
headerDirectoryMetadataTableOffsetLbl.setText(Long.toString(tempValue));
headerDirectoryMetadataTableOffsetHexLbl.setText(getHexString(tempValue));
tempValue = header.getDirectoryMetadataTableLength();
headerDirectoryMetadataTableLengthLbl.setText(Long.toString(tempValue));
headerDirectoryMetadataTableLengthHexLbl.setText(getHexString(tempValue));
tempValue = header.getFileHashTableOffset();
headerFileHashTableOffsetLbl.setText(Long.toString(tempValue));
headerFileHashTableOffsetHexLbl.setText(getHexString(tempValue));
tempValue = header.getFileHashTableLength();
headerFileHashTableLengthLbl.setText(Long.toString(tempValue));
headerFileHashTableLengthHexLbl.setText(getHexString(tempValue));
tempValue = header.getFileMetadataTableOffset();
headerFileMetadataTableOffsetLbl.setText(Long.toString(tempValue));
headerFileMetadataTableOffsetHexLbl.setText(getHexString(tempValue));
tempValue = header.getFileMetadataTableLength();
headerFileMetadataTableLengthLbl.setText(Long.toString(tempValue));
headerFileMetadataTableLengthHexLbl.setText(getHexString(tempValue));
tempValue = header.getFileDataOffset();
headerFileDataOffsetLbl.setText(Long.toString(tempValue));
headerFileDataOffsetHexLbl.setText(getHexString(tempValue));
TreeItem<RFSEntry> rootItem = getTreeFolderItem(RomFsProvider.getRootEntry());
filesTreeView.setRoot(rootItem);
RFSTableViewController.setContent(rootItem);
}
catch (Exception e){ // TODO: FIX
e.printStackTrace();
}
TreeItem<RFSFolderEntry> rootItem = getEmptyRoot();
}
filesTreeView.setRoot(rootItem);
private TreeItem<RFSEntry> getTreeFolderItem(FileSystemEntry childEntry){
TreeItem<RFSEntry> entryTreeItem = new TreeItem<>(new RFSEntry(childEntry), getFolderImage());
for (FileSystemEntry entry : childEntry.getContent()){
if (entry.isDirectory()) {
entryTreeItem.getChildren().add(getTreeFolderItem(entry));
}
else
entryTreeItem.getChildren().add( getTreeFileItem(entry) );;
}
entryTreeItem.setExpanded(true);
return entryTreeItem;
}
private TreeItem<RFSEntry> getTreeFileItem(FileSystemEntry childEntry) {
return new TreeItem<>(new RFSEntry(childEntry), getFileImage());
}
@Override
public void analyze(ISuperProvider parentProvider, int fileNo) throws Exception {
throw new Exception("NOT IMPLEMENTED: analyze(ISuperProvider parentProvider, int fileNo)");
}
@Override
public void resetTab() {
headerHeaderLengthLbl.setText("");
headerDirectoryHashTableOffsetLbl.setText("");
headerDirectoryHashTableLengthLbl.setText("");
headerDirectoryMetadataTableOffsetLbl.setText("");
headerDirectoryMetadataTableLengthLbl.setText("");
headerFileHashTableOffsetLbl.setText("");
headerFileHashTableLengthLbl.setText("");
headerFileMetadataTableOffsetLbl.setText("");
headerFileMetadataTableLengthLbl.setText("");
headerFileDataOffsetLbl.setText("");
headerHeaderLengthHexLbl.setText("");
headerDirectoryHashTableOffsetHexLbl.setText("");
headerDirectoryHashTableLengthHexLbl.setText("");
headerDirectoryMetadataTableOffsetHexLbl.setText("");
headerDirectoryMetadataTableLengthHexLbl.setText("");
headerFileHashTableOffsetHexLbl.setText("");
headerFileHashTableLengthHexLbl.setText("");
headerFileMetadataTableOffsetHexLbl.setText("");
headerFileMetadataTableLengthHexLbl.setText("");
headerFileDataOffsetHexLbl.setText("");
filesTreeView.setRoot(null);
}
@ -83,8 +187,13 @@ public class RomFsController implements ITabController {
folderImage.getStyleClass().add("regionFolder");
return folderImage;
}
private Region getFileImage(){
final Region folderImage = new Region();
folderImage.getStyleClass().add("regionFile");
return folderImage;
}
private TreeItem<RFSFolderEntry> getEmptyRoot(){
return new TreeItem<>(new RFSFolderEntry("/"));
private String getHexString(long value){
return String.format("0x%x", value);
}
}

View file

@ -87,9 +87,9 @@ public class TIKController implements ITabController {
}
@Override
public void analyze(File file, long offset) {
Task analyzer = Analyzer.analyzeTIK(file, offset);
Task<TIKProvider> analyzer = Analyzer.analyzeTIK(file, offset);
analyzer.setOnSucceeded(e->{
TIKProvider tik = (TIKProvider) analyzer.getValue();
TIKProvider tik = analyzer.getValue();
if (offset == 0)
setData(tik, file);
else

View file

@ -42,12 +42,8 @@ import konogonka.Tools.ISuperProvider;
import konogonka.Tools.XCI.HFS0Provider;
import java.lang.reflect.Array;
import java.net.URL;
import java.nio.IntBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.ResourceBundle;

View file

@ -30,7 +30,7 @@ import java.util.Locale;
import java.util.ResourceBundle;
public class MainFx extends Application {
public static final String appVersion = "v0.0.2";
public static final String appVersion = "v0.0.3";
@Override
public void start(Stage primaryStage) throws Exception{

View file

@ -1,29 +0,0 @@
/*
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,88 @@
/*
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.ModelControllers.LogPrinter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static konogonka.RainbowDump.formatDecHexString;
public class FileMeta4Debug {
List<FileMeta> allFiles;
FileMeta4Debug(long fileMetadataTableLength, byte[] fileMetadataTable) {
allFiles = new ArrayList<>();
int i = 0;
while (i < fileMetadataTableLength) {
FileMeta fileMeta = new FileMeta();
fileMeta.containingDirectoryOffset = LoperConverter.getLEint(fileMetadataTable, i);
i += 4;
fileMeta.nextSiblingFileOffset = LoperConverter.getLEint(fileMetadataTable, i);
i += 4;
fileMeta.fileDataOffset = LoperConverter.getLElong(fileMetadataTable, i);
i += 8;
fileMeta.fileDataLength = LoperConverter.getLElong(fileMetadataTable, i);
i += 8;
fileMeta.nextFileOffset = LoperConverter.getLEint(fileMetadataTable, i);
i += 4;
fileMeta.fileNameLength = LoperConverter.getLEint(fileMetadataTable, i);
i += 4;
fileMeta.fileName = new String(Arrays.copyOfRange(fileMetadataTable, i, i + fileMeta.fileNameLength), StandardCharsets.UTF_8);
;
i += getRealNameSize(fileMeta.fileNameLength);
allFiles.add(fileMeta);
}
for (FileMeta fileMeta : allFiles){
System.out.println(
"-------------------------FILE--------------------------------\n" +
"Offset of Containing Directory " + formatDecHexString(fileMeta.containingDirectoryOffset) + "\n" +
"Offset of next Sibling File " + formatDecHexString(fileMeta.nextSiblingFileOffset) + "\n" +
"Offset of File's Data " + formatDecHexString(fileMeta.fileDataOffset) + "\n" +
"Length of File's Data " + formatDecHexString(fileMeta.fileDataLength) + "\n" +
"Offset of next File in the same Hash Table bucket " + formatDecHexString(fileMeta.nextFileOffset) + "\n" +
"Name Length " + formatDecHexString(fileMeta.fileNameLength) + "\n" +
"Name Length (rounded up to multiple of 4) " + fileMeta.fileName + "\n"
);
}
}
private int getRealNameSize(int value){
if (value % 4 == 0)
return value;
return value + 4 - value % 4;
}
private static class FileMeta{
int containingDirectoryOffset;
int nextSiblingFileOffset;
long fileDataOffset;
long fileDataLength;
int nextFileOffset;
int fileNameLength;
String fileName;
}
}

View file

@ -0,0 +1,192 @@
/*
* 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 java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
public class FileSystemEntry {
private boolean directoryFlag;
private String name;
private List<FileSystemEntry> content;
private static byte[] dirsMetadataTable;
private static byte[] filesMetadataTable;
private long fileOffset;
private long fileSize;
public FileSystemEntry(byte[] dirsMetadataTable, byte[] filesMetadataTable) throws Exception{
FileSystemEntry.dirsMetadataTable = dirsMetadataTable;
FileSystemEntry.filesMetadataTable = filesMetadataTable;
this.content = new ArrayList<>();
this.directoryFlag = true;
DirectoryMetaData rootDirectoryMetaData = new DirectoryMetaData();
if (rootDirectoryMetaData.dirName.isEmpty())
this.name = "/";
else
this.name = rootDirectoryMetaData.dirName;
if (rootDirectoryMetaData.parentDirectoryOffset != 0)
throw new Exception("Offset of Parent Directory is incorrect. Expected 0 for root, received value is "+ rootDirectoryMetaData.parentDirectoryOffset);
if (rootDirectoryMetaData.nextSiblingDirectoryOffset != -1)
throw new Exception("Offset of next Sibling Directory is incorrect. Expected -1 for root, received value is "+ rootDirectoryMetaData.nextSiblingDirectoryOffset);
if (rootDirectoryMetaData.firstSubdirectoryOffset != -1)
content.add(getDirectory(rootDirectoryMetaData.firstSubdirectoryOffset));
if (rootDirectoryMetaData.firstFileOffset != -1)
content.add(getFile(this, rootDirectoryMetaData.firstFileOffset));
content.sort(Comparator.comparingLong(FileSystemEntry::getFileOffset));
}
private FileSystemEntry(){
this.content = new ArrayList<>();
}
private FileSystemEntry getDirectory(int childDirMetaPosition){
FileSystemEntry fileSystemEntry = new FileSystemEntry();
fileSystemEntry.directoryFlag = true;
DirectoryMetaData directoryMetaData = new DirectoryMetaData(childDirMetaPosition);
fileSystemEntry.name = directoryMetaData.dirName;
if (directoryMetaData.nextSiblingDirectoryOffset != -1)
this.content.add(getDirectory(directoryMetaData.nextSiblingDirectoryOffset));
if (directoryMetaData.firstSubdirectoryOffset != -1)
fileSystemEntry.content.add(getDirectory(directoryMetaData.firstSubdirectoryOffset));
if (directoryMetaData.firstFileOffset != -1)
fileSystemEntry.content.add(getFile(fileSystemEntry, directoryMetaData.firstFileOffset));
fileSystemEntry.content.sort(Comparator.comparingLong(FileSystemEntry::getFileOffset));
return fileSystemEntry;
}
private FileSystemEntry getFile(FileSystemEntry directoryContainer, int childFileMetaPosition){
FileSystemEntry fileSystemEntry = new FileSystemEntry();
fileSystemEntry.directoryFlag = false;
FileMetaData fileMetaData = new FileMetaData(childFileMetaPosition);
fileSystemEntry.name = fileMetaData.fileName;
fileSystemEntry.fileOffset = fileMetaData.fileDataRealOffset;
fileSystemEntry.fileSize = fileMetaData.fileDataRealLength;
if (fileMetaData.nextSiblingFileOffset != -1)
directoryContainer.content.add( getFile(directoryContainer, fileMetaData.nextSiblingFileOffset) );
return fileSystemEntry;
}
public boolean isDirectory() { return directoryFlag; }
public boolean isFile() { return ! directoryFlag; }
public long getFileOffset() { return fileOffset; }
public long getFileSize() { return fileSize; }
public List<FileSystemEntry> getContent() { return content; }
public String getName(){ return name; }
private static class DirectoryMetaData {
private int parentDirectoryOffset;
private int nextSiblingDirectoryOffset;
private int firstSubdirectoryOffset;
private int firstFileOffset;
private String dirName;
private DirectoryMetaData(){
this(0);
}
private DirectoryMetaData(int childDirMetaPosition){
int i = childDirMetaPosition;
parentDirectoryOffset = LoperConverter.getLEint(dirsMetadataTable, i);
i += 4;
nextSiblingDirectoryOffset = LoperConverter.getLEint(dirsMetadataTable, i);
i += 4;
firstSubdirectoryOffset = LoperConverter.getLEint(dirsMetadataTable, i);
i += 4;
firstFileOffset = LoperConverter.getLEint(dirsMetadataTable, i);
i += 4;
// int nextHashTableBucketDirectoryOffset = LoperConverter.getLEint(dirsMetadataTable, i);
i += 4;
int dirNameLength = LoperConverter.getLEint(dirsMetadataTable, i);
i += 4;
dirName = new String(Arrays.copyOfRange(dirsMetadataTable, i, i + dirNameLength), StandardCharsets.UTF_8);
//i += getRealNameSize(dirNameLength);
}
private int getRealNameSize(int value){
if (value % 4 == 0)
return value;
return value + 4 - value % 4;
}
}
private static class FileMetaData {
private int nextSiblingFileOffset;
private long fileDataRealOffset;
private long fileDataRealLength;
private String fileName;
private FileMetaData(){
this(0);
}
private FileMetaData(int childFileMetaPosition){
int i = childFileMetaPosition;
// int containingDirectoryOffset = LoperConverter.getLEint(filesMetadataTable, i); // never used
i += 4;
nextSiblingFileOffset = LoperConverter.getLEint(filesMetadataTable, i);
i += 4;
fileDataRealOffset = LoperConverter.getLElong(filesMetadataTable, i);
i += 8;
fileDataRealLength = LoperConverter.getLElong(filesMetadataTable, i);
i += 8;
//int nextHashTableBucketFileOffset = LoperConverter.getLEint(filesMetadataTable, i);
i += 4;
int fileNameLength = LoperConverter.getLEint(filesMetadataTable, i);
i += 4;
fileName = new String(Arrays.copyOfRange(filesMetadataTable, i, i + fileNameLength), StandardCharsets.UTF_8);;
//i += getRealNameSize(fileNameLength);
}
}
public void printTreeForDebug(){
System.out.println("/");
for (FileSystemEntry entry: content)
printEntry(2, entry);
}
private void printEntry(int cnt, FileSystemEntry entry) {
for (int i = 0; i < cnt; i++)
System.out.print(" ");
if (entry.isDirectory()){
System.out.println("|-" + entry.getName());
for (FileSystemEntry e : entry.content)
printEntry(cnt+2, e);
}
else
System.out.println("|-" + entry.getName() + String.format(" 0x%-10x 0x%-10x", entry.fileOffset, entry.fileSize));
}
}

View file

@ -1,29 +0,0 @@
/*
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,85 @@
/*
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 java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static konogonka.RainbowDump.formatDecHexString;
public class FolderMeta4Debug {
List<FolderMeta> allFolders;
FolderMeta4Debug(long directoryMetadataTableLength, byte[] directoryMetadataTable){
allFolders = new ArrayList<>();
int i = 0;
while (i < directoryMetadataTableLength){
FolderMeta folderMeta = new FolderMeta();
folderMeta.parentDirectoryOffset = LoperConverter.getLEint(directoryMetadataTable, i);
i += 4;
folderMeta.nextSiblingDirectoryOffset = LoperConverter.getLEint(directoryMetadataTable, i);
i += 4;
folderMeta.firstSubdirectoryOffset = LoperConverter.getLEint(directoryMetadataTable, i);
i += 4;
folderMeta.firstFileOffset = LoperConverter.getLEint(directoryMetadataTable, i);
i += 4;
folderMeta.nextDirectoryOffset = LoperConverter.getLEint(directoryMetadataTable, i);
i += 4;
folderMeta.dirNameLength = LoperConverter.getLEint(directoryMetadataTable, i);
i += 4;
folderMeta.dirName = new String(Arrays.copyOfRange(directoryMetadataTable, i, i + folderMeta.dirNameLength), StandardCharsets.UTF_8);
i += getRealNameSize(folderMeta.dirNameLength);
allFolders.add(folderMeta);
}
for (FolderMeta headersDirectory : allFolders)
System.out.println(
"---------------------------DIRECTORY------------------------\n" +
"Offset of Parent Directory (self if Root) " + formatDecHexString(headersDirectory.parentDirectoryOffset ) +"\n" +
"Offset of next Sibling Directory " + formatDecHexString(headersDirectory.nextSiblingDirectoryOffset) +"\n" +
"Offset of first Child Directory (Subdirectory) " + formatDecHexString(headersDirectory.firstSubdirectoryOffset ) +"\n" +
"Offset of first File (in File Metadata Table) " + formatDecHexString(headersDirectory.firstFileOffset ) +"\n" +
"Offset of next Directory in the same Hash Table bucket " + formatDecHexString(headersDirectory.nextDirectoryOffset ) +"\n" +
"Name Length " + formatDecHexString(headersDirectory.dirNameLength ) +"\n" +
"Name Length (rounded up to multiple of 4) " + headersDirectory.dirName + "\n"
);
}
private int getRealNameSize(int value){
if (value % 4 == 0)
return value;
return value + 4 - value % 4;
}
private static class FolderMeta {
int parentDirectoryOffset;
int nextSiblingDirectoryOffset;
int firstSubdirectoryOffset;
int firstFileOffset;
int nextDirectoryOffset;
int dirNameLength;
String dirName;
}
}

View file

@ -20,49 +20,34 @@
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;
private long headerLength;
private long directoryHashTableOffset;
private long directoryHashTableLength;
private long directoryMetadataTableOffset;
private long directoryMetadataTableLength;
private long fileHashTableOffset;
private long fileHashTableLength;
private long fileMetadataTableOffset;
private long fileMetadataTableLength;
private long fileDataOffset;
private byte[] headerBytes;
private int i;
Level6Header(byte[] headerBytes){
this.headerBytes = headerBytes;
headerHeaderLength = getNext();
headerDirectoryHashTableOffset = getNext();
headerDirectoryHashTableLength = getNext();
headerDirectoryMetadataTableOffset = getNext();
headerDirectoryMetadataTableLength = getNext();
headerFileHashTableOffset = getNext();
headerFileHashTableLength = getNext();
headerFileMetadataTableOffset = getNext();
headerFileMetadataTableLength = getNext();
headerFileDataOffset = getNext();
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" +
"-------------------------------------------------------------"
);
headerLength = getNext();
directoryHashTableOffset = getNext();
directoryHashTableLength = getNext();
directoryMetadataTableOffset = getNext();
directoryMetadataTableLength = getNext();
fileHashTableOffset = getNext();
fileHashTableLength = getNext();
fileMetadataTableOffset = getNext();
fileMetadataTableLength = getNext();
fileDataOffset = getNext();
}
private long getNext(){
@ -71,14 +56,14 @@ public class Level6Header {
return result;
}
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; }
public long getHeaderLength() { return headerLength; }
public long getDirectoryHashTableOffset() { return directoryHashTableOffset; }
public long getDirectoryHashTableLength() { return directoryHashTableLength; }
public long getDirectoryMetadataTableOffset() { return directoryMetadataTableOffset; }
public long getDirectoryMetadataTableLength() { return directoryMetadataTableLength; }
public long getFileHashTableOffset() { return fileHashTableOffset; }
public long getFileHashTableLength() { return fileHashTableLength; }
public long getFileMetadataTableOffset() { return fileMetadataTableOffset; }
public long getFileMetadataTableLength() { return fileMetadataTableLength; }
public long getFileDataOffset() { return fileDataOffset; }
}

View file

@ -19,12 +19,19 @@
package konogonka.Tools.RomFs;
import konogonka.LoperConverter;
import konogonka.Tools.ISuperProvider;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.PipedInputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import static konogonka.RainbowDump.formatDecHexString;
public class RomFsDecryptedProvider implements ISuperProvider {
@ -33,12 +40,14 @@ public class RomFsDecryptedProvider implements ISuperProvider {
private File decryptedFSImage;
private Level6Header header;
private FileSystemEntry rootEntry;
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);
skipBytes(bis, LEVEL_6_DEFAULT_OFFSET);
byte[] rawDataChunk = new byte[0x50];
@ -46,10 +55,52 @@ public class RomFsDecryptedProvider implements ISuperProvider {
throw new Exception("Failed to read header (0x50)");
this.header = new Level6Header(rawDataChunk);
/*
// Print Dir Hash table as is:
long seekTo = header.getDirectoryHashTableOffset() - 0x50;
rawDataChunk = new byte[(int) header.getDirectoryHashTableLength()];
skipTo(bis, seekTo);
if (bis.read(rawDataChunk) != (int) header.getDirectoryHashTableLength())
throw new Exception("Failed to read Dir hash table");
RainbowDump.hexDumpUTF8(rawDataChunk);
// Print Files Hash table as is:
seekTo = header.getFileHashTableOffset() - header.getDirectoryMetadataTableOffset();
rawDataChunk = new byte[(int) header.getFileHashTableLength()];
skipTo(bis, seekTo);
if (bis.read(rawDataChunk) != (int) header.getFileHashTableLength())
throw new Exception("Failed to read Files hash table");
RainbowDump.hexDumpUTF8(rawDataChunk);
*/
// Read directories metadata
long locationInFile = header.getDirectoryMetadataTableOffset() - 0x50;
skipBytes(bis, locationInFile);
if (header.getDirectoryMetadataTableLength() < 0)
throw new Exception("Not supported operation.");
byte[] directoryMetadataTable = new byte[(int) header.getDirectoryMetadataTableLength()];
if (bis.read(directoryMetadataTable) != (int) header.getDirectoryMetadataTableLength())
throw new Exception("Failed to read "+header.getDirectoryMetadataTableLength());
// Read files metadata
locationInFile = header.getFileMetadataTableOffset() - header.getFileHashTableOffset(); // TODO: replace to 'CurrentPosition'?
skipBytes(bis, locationInFile);
if (header.getFileMetadataTableLength() < 0)
throw new Exception("Not supported operation.");
byte[] fileMetadataTable = new byte[(int) header.getFileMetadataTableLength()];
if (bis.read(fileMetadataTable) != (int) header.getFileMetadataTableLength())
throw new Exception("Failed to read "+header.getFileMetadataTableLength());
rootEntry = new FileSystemEntry(directoryMetadataTable, fileMetadataTable);
//printDebug(directoryMetadataTable, fileMetadataTable);
bis.close();
}
private void skipTo(BufferedInputStream bis, long size) throws Exception{
private void skipBytes(BufferedInputStream bis, long size) throws Exception{
long mustSkip = size;
long skipped = 0;
while (mustSkip > 0){
@ -57,13 +108,9 @@ public class RomFsDecryptedProvider implements ISuperProvider {
mustSkip = size - skipped;
}
}
private int getRealNameSize(int value){
if (value % 4 == 0)
return value;
return value + 4 - value % 4;
}
public Level6Header getHeader() { return header; }
public FileSystemEntry getRootEntry() { return rootEntry; }
@Override
public PipedInputStream getProviderSubFilePipedInpStream(String subFileName) throws Exception {
@ -72,7 +119,7 @@ public class RomFsDecryptedProvider implements ISuperProvider {
@Override
public PipedInputStream getProviderSubFilePipedInpStream(int subFileNumber) throws Exception {
return null;
throw new Exception("RomFsDecryptedProvider -> getProviderSubFilePipedInpStream(): Get files by number is not supported.");
}
@Override
@ -84,4 +131,10 @@ public class RomFsDecryptedProvider implements ISuperProvider {
public long getRawFileDataStart() {
return 0;
}
private void printDebug(byte[] directoryMetadataTable, byte[] fileMetadataTable){
new FolderMeta4Debug(header.getDirectoryMetadataTableLength(), directoryMetadataTable);
new FileMeta4Debug(header.getFileMetadataTableLength(), fileMetadataTable);
rootEntry.printTreeForDebug();
}
}

View file

@ -3,6 +3,25 @@
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.AnchorPane?>
<!--
~ 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/>.
-->
<AnchorPane xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1" fx:controller="konogonka.Controllers.NSP.Pfs0TableViewController">
<children>
<TableView fx:id="table" editable="true" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />

View file

@ -1,15 +1,366 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.SplitPane?>
<?import javafx.scene.control.TitledPane?>
<?import javafx.scene.control.TreeView?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<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">
<VBox spacing="5.0" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1" fx:controller="konogonka.Controllers.RFS.RomFsController">
<children>
<TitledPane animated="false" expanded="false" text="Header (Level 6)">
<content>
<GridPane gridLinesVisible="true">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" maxWidth="60.0" minWidth="55.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="60.0" minWidth="55.0" />
<ColumnConstraints hgrow="SOMETIMES" maxWidth="180.0" minWidth="250.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="100.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="100.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<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" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="30.0" minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="30.0" minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="30.0" minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="30.0" minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="30.0" minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints maxHeight="30.0" minHeight="30.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<AnchorPane styleClass="customTitleGrid" GridPane.columnIndex="1">
<children>
<Label layoutX="38.0" layoutY="6.0" text="Size" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<font>
<Font name="System Bold" size="13.0" />
</font>
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
</children>
</AnchorPane>
<AnchorPane styleClass="customTitleGrid" GridPane.columnIndex="2">
<children>
<Label layoutX="14.0" layoutY="7.0" text="Description" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<font>
<Font name="System Bold" size="13.0" />
</font>
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
</children>
</AnchorPane>
<AnchorPane styleClass="customTitleGrid" GridPane.columnIndex="3">
<children>
<Label layoutY="6.0" text="Value Dec" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<font>
<Font name="System Bold" size="13.0" />
</font>
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
</children>
</AnchorPane>
<AnchorPane styleClass="customTitleGrid">
<children>
<Label layoutX="2.0" layoutY="7.0" text="Offset" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<font>
<Font name="System Bold" size="13.0" />
</font>
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
</children>
</AnchorPane>
<Label text="Directory Metadata Table Length" GridPane.columnIndex="2" GridPane.rowIndex="5">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label text="Directory Metadata Table Offset" GridPane.columnIndex="2" GridPane.rowIndex="4">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label text="File Hash Table Offset" GridPane.columnIndex="2" GridPane.rowIndex="6">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label text="File Hash Table Length" GridPane.columnIndex="2" GridPane.rowIndex="7">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label text="File Metadata Table Offset" GridPane.columnIndex="2" GridPane.rowIndex="8">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label text="File Metadata Table Length" GridPane.columnIndex="2" GridPane.rowIndex="9">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label text="File Data Offset" GridPane.columnIndex="2" GridPane.rowIndex="10">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label text="0x8" GridPane.rowIndex="2">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label text="0x8" GridPane.columnIndex="1" GridPane.rowIndex="1">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label text="0x0" GridPane.rowIndex="1">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label fx:id="headerHeaderLengthLbl" text="-" GridPane.columnIndex="3" GridPane.rowIndex="1">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label text="Header Length (always 0x50 ?)" GridPane.columnIndex="2" GridPane.rowIndex="1">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label text="Directory Hash Table Offset" GridPane.columnIndex="2" GridPane.rowIndex="2">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label text="Directory Hash Table Length" GridPane.columnIndex="2" GridPane.rowIndex="3">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label text="0x8" GridPane.columnIndex="1" GridPane.rowIndex="2">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label text="0x10" GridPane.rowIndex="3">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label text="0x8" GridPane.columnIndex="1" GridPane.rowIndex="3">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label text="0x18" GridPane.rowIndex="4">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label text="0x8" GridPane.columnIndex="1" GridPane.rowIndex="4">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label text="0x20" GridPane.rowIndex="5">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label text="0x8" GridPane.columnIndex="1" GridPane.rowIndex="5">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label text="0x28" GridPane.rowIndex="6">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label text="0x8" GridPane.columnIndex="1" GridPane.rowIndex="6">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label text="0x30" GridPane.rowIndex="7">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label text="0x8" GridPane.columnIndex="1" GridPane.rowIndex="7">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label text="0x38" GridPane.rowIndex="8">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label text="0x8" GridPane.columnIndex="1" GridPane.rowIndex="8">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label text="0x40" GridPane.rowIndex="9">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label text="0x8" GridPane.columnIndex="1" GridPane.rowIndex="9">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label text="0x48" GridPane.rowIndex="10">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label text="0x8" GridPane.columnIndex="1" GridPane.rowIndex="10">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label fx:id="headerDirectoryHashTableOffsetLbl" text="-" GridPane.columnIndex="3" GridPane.rowIndex="2">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label fx:id="headerDirectoryHashTableLengthLbl" text="-" GridPane.columnIndex="3" GridPane.rowIndex="3">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label fx:id="headerDirectoryMetadataTableOffsetLbl" text="-" GridPane.columnIndex="3" GridPane.rowIndex="4">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label fx:id="headerFileHashTableOffsetLbl" text="-" GridPane.columnIndex="3" GridPane.rowIndex="6">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label fx:id="headerDirectoryMetadataTableLengthLbl" text="-" GridPane.columnIndex="3" GridPane.rowIndex="5">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label fx:id="headerFileHashTableLengthLbl" text="-" GridPane.columnIndex="3" GridPane.rowIndex="7">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label fx:id="headerFileMetadataTableOffsetLbl" text="-" GridPane.columnIndex="3" GridPane.rowIndex="8">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label fx:id="headerFileMetadataTableLengthLbl" text="-" GridPane.columnIndex="3" GridPane.rowIndex="9">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label fx:id="headerFileDataOffsetLbl" text="-" GridPane.columnIndex="3" GridPane.rowIndex="10">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<AnchorPane styleClass="customTitleGrid" GridPane.columnIndex="4">
<children>
<Label layoutY="6.0" text="Value Hex" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<font>
<Font name="System Bold" size="13.0" />
</font>
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
</children>
</AnchorPane>
<Label fx:id="headerHeaderLengthHexLbl" text="-" GridPane.columnIndex="4" GridPane.rowIndex="1">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label fx:id="headerDirectoryHashTableOffsetHexLbl" text="-" GridPane.columnIndex="4" GridPane.rowIndex="2">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label fx:id="headerDirectoryHashTableLengthHexLbl" text="-" GridPane.columnIndex="4" GridPane.rowIndex="3">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label fx:id="headerDirectoryMetadataTableOffsetHexLbl" text="-" GridPane.columnIndex="4" GridPane.rowIndex="4">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label fx:id="headerDirectoryMetadataTableLengthHexLbl" text="-" GridPane.columnIndex="4" GridPane.rowIndex="5">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label fx:id="headerFileHashTableOffsetHexLbl" text="-" GridPane.columnIndex="4" GridPane.rowIndex="6">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label fx:id="headerFileHashTableLengthHexLbl" text="-" GridPane.columnIndex="4" GridPane.rowIndex="7">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label fx:id="headerFileMetadataTableOffsetHexLbl" text="-" GridPane.columnIndex="4" GridPane.rowIndex="8">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label fx:id="headerFileMetadataTableLengthHexLbl" text="-" GridPane.columnIndex="4" GridPane.rowIndex="9">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
<Label fx:id="headerFileDataOffsetHexLbl" text="-" GridPane.columnIndex="4" GridPane.rowIndex="10">
<padding>
<Insets left="5.0" right="5.0" />
</padding>
</Label>
</children>
</GridPane>
</content>
</TitledPane>
<SplitPane dividerPositions="0.2" VBox.vgrow="ALWAYS">
<items>
<TreeView fx:id="filesTreeView" />
<VBox fx:id="folderContentVBox">
</VBox>
<fx:include fx:id="RFSTableView" source="RFSTableView.fxml" />
</items>
</SplitPane>
</children>
<padding>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
</padding>
</VBox>

View file

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.AnchorPane?>
<!--
~ 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/>.
-->
<AnchorPane xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1" fx:controller="konogonka.Controllers.RFS.RFSFolderTableViewController">
<children>
<TableView fx:id="table" editable="true" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
</children>
</AnchorPane>

View file

@ -43,7 +43,7 @@
</Button>
</items>
</ToolBar>
<SplitPane fx:id="splitPane" dividerPositions="0.5" VBox.vgrow="ALWAYS">
<SplitPane fx:id="splitPane" onDragDropped="#handleDrop" onDragOver="#handleDragOver" dividerPositions="0.5" VBox.vgrow="ALWAYS">
<items>
<TabPane fx:id="tabPane" side="LEFT" tabClosingPolicy="UNAVAILABLE" tabMinHeight="35.0" tabMinWidth="25.0">
<tabs>

View file

@ -434,3 +434,9 @@
-fx-min-height: 17.0;
-fx-min-width: 17.5;
}
.regionFile {
-fx-shape: "M13,9V3.5L18.5,9M6,2C4.89,2 4,2.89 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2H6Z";
-fx-background-color: #e54d1e;
-fx-min-height: 17.0;
-fx-min-width: 13;
}