From bad2924c02b318a21cf56da229a10df0a990e202 Mon Sep 17 00:00:00 2001 From: Dmitry Isaenko Date: Sun, 26 Apr 2020 05:06:17 +0300 Subject: [PATCH] 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. --- pom.xml | 2 +- .../konogonka/Controllers/MainController.java | 63 ++- .../NSP/Pfs0TableViewController.java | 23 +- .../konogonka/Controllers/RFS/RFSEntry.java | 59 +++ .../Controllers/RFS/RFSFolderEntry.java | 37 -- .../RFS/RFSFolderTableViewController.java | 172 ++++++++ .../Controllers/RFS/RomFsController.java | 137 ++++++- .../Controllers/TIK/TIKController.java | 4 +- .../XCI/Hfs0TableViewController.java | 4 - src/main/java/konogonka/MainFx.java | 2 +- .../java/konogonka/Tools/RomFs/FileMeta.java | 29 -- .../konogonka/Tools/RomFs/FileMeta4Debug.java | 88 +++++ .../Tools/RomFs/FileSystemEntry.java | 192 +++++++++ .../konogonka/Tools/RomFs/FolderMeta.java | 29 -- .../Tools/RomFs/FolderMeta4Debug.java | 85 ++++ .../konogonka/Tools/RomFs/Level6Header.java | 75 ++-- .../Tools/RomFs/RomFsDecryptedProvider.java | 69 +++- src/main/resources/FXML/NSP/TableView.fxml | 19 + src/main/resources/FXML/RomFS/RFSTab.fxml | 369 +++++++++++++++++- .../resources/FXML/RomFS/RFSTableView.fxml | 29 ++ src/main/resources/FXML/landingPage.fxml | 2 +- src/main/resources/res/app_light.css | 6 + 22 files changed, 1300 insertions(+), 195 deletions(-) create mode 100644 src/main/java/konogonka/Controllers/RFS/RFSEntry.java delete mode 100644 src/main/java/konogonka/Controllers/RFS/RFSFolderEntry.java create mode 100644 src/main/java/konogonka/Controllers/RFS/RFSFolderTableViewController.java delete mode 100644 src/main/java/konogonka/Tools/RomFs/FileMeta.java create mode 100644 src/main/java/konogonka/Tools/RomFs/FileMeta4Debug.java create mode 100644 src/main/java/konogonka/Tools/RomFs/FileSystemEntry.java delete mode 100644 src/main/java/konogonka/Tools/RomFs/FolderMeta.java create mode 100644 src/main/java/konogonka/Tools/RomFs/FolderMeta4Debug.java create mode 100644 src/main/resources/FXML/RomFS/RFSTableView.fxml diff --git a/pom.xml b/pom.xml index c8a5a5b..fa5f880 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ konogonka konogonka - 0.0.2-SNAPSHOT + 0.0.3-SNAPSHOT https://github.com/developersu/${project.name}}/ diff --git a/src/main/java/konogonka/Controllers/MainController.java b/src/main/java/konogonka/Controllers/MainController.java index bd112c4..2c92c43 100644 --- a/src/main/java/konogonka/Controllers/MainController.java +++ b/src/main/java/konogonka/Controllers/MainController.java @@ -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 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); diff --git a/src/main/java/konogonka/Controllers/NSP/Pfs0TableViewController.java b/src/main/java/konogonka/Controllers/NSP/Pfs0TableViewController.java index 31ca473..a0cdd86 100644 --- a/src/main/java/konogonka/Controllers/NSP/Pfs0TableViewController.java +++ b/src/main/java/konogonka/Controllers/NSP/Pfs0TableViewController.java @@ -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 fileNameColumn = new TableColumn<>(resourceBundle.getString("tableFileNameLbl")); TableColumn fileOffsetColumn = new TableColumn<>(resourceBundle.getString("tableOffsetLbl")); TableColumn fileSizeColumn = new TableColumn<>(resourceBundle.getString("tableSizeLbl")); - TableColumn uploadColumn = new TableColumn<>(resourceBundle.getString("tableUploadLbl")); + TableColumn 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 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 diff --git a/src/main/java/konogonka/Controllers/RFS/RFSEntry.java b/src/main/java/konogonka/Controllers/RFS/RFSEntry.java new file mode 100644 index 0000000..e72e2d0 --- /dev/null +++ b/src/main/java/konogonka/Controllers/RFS/RFSEntry.java @@ -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 . +*/ +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; } +} \ No newline at end of file diff --git a/src/main/java/konogonka/Controllers/RFS/RFSFolderEntry.java b/src/main/java/konogonka/Controllers/RFS/RFSFolderEntry.java deleted file mode 100644 index bfcea98..0000000 --- a/src/main/java/konogonka/Controllers/RFS/RFSFolderEntry.java +++ /dev/null @@ -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 . -*/ -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; - } - -} \ No newline at end of file diff --git a/src/main/java/konogonka/Controllers/RFS/RFSFolderTableViewController.java b/src/main/java/konogonka/Controllers/RFS/RFSFolderTableViewController.java new file mode 100644 index 0000000..b793539 --- /dev/null +++ b/src/main/java/konogonka/Controllers/RFS/RFSFolderTableViewController.java @@ -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 . +*/ +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 table; + private ObservableList 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 numberColumn = new TableColumn<>(resourceBundle.getString("tableNumberLbl")); + TableColumn fileNameColumn = new TableColumn<>(resourceBundle.getString("tableFileNameLbl")); + TableColumn fileOffsetColumn = new TableColumn<>(resourceBundle.getString("tableOffsetLbl")); + TableColumn fileSizeColumn = new TableColumn<>(resourceBundle.getString("tableSizeLbl")); + TableColumn 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 row = new TableRow<>(); + ContextMenu contextMenu = new ContextMenu(); + /* // TODO: CHANGE TO 'Export' or something + MenuItem openMenuItem = new MenuItem("Open"); + openMenuItem.setOnAction(new EventHandler() { + @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 containerTreeItem){ + rowsObsLst.clear(); + + if (containerTreeItem == null) { + table.refresh(); + return; + } + + for (TreeItem childTreeItem : containerTreeItem.getChildren()) + rowsObsLst.add(childTreeItem.getValue()); + + table.refresh(); + } +} \ No newline at end of file diff --git a/src/main/java/konogonka/Controllers/RFS/RomFsController.java b/src/main/java/konogonka/Controllers/RFS/RomFsController.java index f4bff08..8a664ce 100644 --- a/src/main/java/konogonka/Controllers/RFS/RomFsController.java +++ b/src/main/java/konogonka/Controllers/RFS/RomFsController.java @@ -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 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 filesTreeView; private RomFsDecryptedProvider RomFsProvider; + @FXML + private RFSFolderTableViewController RFSTableViewController; + @Override public void initialize(URL url, ResourceBundle resourceBundle) { - TreeItem rootTest = getEmptyRoot(); - TreeItem test = new TreeItem<>(new RFSFolderEntry("WIP"), getFolderImage()); + filesTreeView.setOnMouseClicked(mouseEvent -> { + TreeItem 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{ - 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 rootItem = getTreeFolderItem(RomFsProvider.getRootEntry()); + + filesTreeView.setRoot(rootItem); + + RFSTableViewController.setContent(rootItem); } catch (Exception e){ // TODO: FIX e.printStackTrace(); } - TreeItem rootItem = getEmptyRoot(); + } - filesTreeView.setRoot(rootItem); + private TreeItem getTreeFolderItem(FileSystemEntry childEntry){ + TreeItem 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 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 getEmptyRoot(){ - return new TreeItem<>(new RFSFolderEntry("/")); + private String getHexString(long value){ + return String.format("0x%x", value); } } diff --git a/src/main/java/konogonka/Controllers/TIK/TIKController.java b/src/main/java/konogonka/Controllers/TIK/TIKController.java index 3558f36..039bb82 100644 --- a/src/main/java/konogonka/Controllers/TIK/TIKController.java +++ b/src/main/java/konogonka/Controllers/TIK/TIKController.java @@ -87,9 +87,9 @@ public class TIKController implements ITabController { } @Override public void analyze(File file, long offset) { - Task analyzer = Analyzer.analyzeTIK(file, offset); + Task analyzer = Analyzer.analyzeTIK(file, offset); analyzer.setOnSucceeded(e->{ - TIKProvider tik = (TIKProvider) analyzer.getValue(); + TIKProvider tik = analyzer.getValue(); if (offset == 0) setData(tik, file); else diff --git a/src/main/java/konogonka/Controllers/XCI/Hfs0TableViewController.java b/src/main/java/konogonka/Controllers/XCI/Hfs0TableViewController.java index 6569ee3..2f1516f 100644 --- a/src/main/java/konogonka/Controllers/XCI/Hfs0TableViewController.java +++ b/src/main/java/konogonka/Controllers/XCI/Hfs0TableViewController.java @@ -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; diff --git a/src/main/java/konogonka/MainFx.java b/src/main/java/konogonka/MainFx.java index 0f55cb0..a3de8e2 100644 --- a/src/main/java/konogonka/MainFx.java +++ b/src/main/java/konogonka/MainFx.java @@ -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{ diff --git a/src/main/java/konogonka/Tools/RomFs/FileMeta.java b/src/main/java/konogonka/Tools/RomFs/FileMeta.java deleted file mode 100644 index e4f4d0d..0000000 --- a/src/main/java/konogonka/Tools/RomFs/FileMeta.java +++ /dev/null @@ -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 . -*/ -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; -} diff --git a/src/main/java/konogonka/Tools/RomFs/FileMeta4Debug.java b/src/main/java/konogonka/Tools/RomFs/FileMeta4Debug.java new file mode 100644 index 0000000..6287cd4 --- /dev/null +++ b/src/main/java/konogonka/Tools/RomFs/FileMeta4Debug.java @@ -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 . +*/ +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 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; + } +} diff --git a/src/main/java/konogonka/Tools/RomFs/FileSystemEntry.java b/src/main/java/konogonka/Tools/RomFs/FileSystemEntry.java new file mode 100644 index 0000000..98470b0 --- /dev/null +++ b/src/main/java/konogonka/Tools/RomFs/FileSystemEntry.java @@ -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 . + */ + +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 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 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)); + } +} diff --git a/src/main/java/konogonka/Tools/RomFs/FolderMeta.java b/src/main/java/konogonka/Tools/RomFs/FolderMeta.java deleted file mode 100644 index 0c6cd7e..0000000 --- a/src/main/java/konogonka/Tools/RomFs/FolderMeta.java +++ /dev/null @@ -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 . -*/ -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; -} diff --git a/src/main/java/konogonka/Tools/RomFs/FolderMeta4Debug.java b/src/main/java/konogonka/Tools/RomFs/FolderMeta4Debug.java new file mode 100644 index 0000000..48d5b97 --- /dev/null +++ b/src/main/java/konogonka/Tools/RomFs/FolderMeta4Debug.java @@ -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 . +*/ +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 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; + } +} diff --git a/src/main/java/konogonka/Tools/RomFs/Level6Header.java b/src/main/java/konogonka/Tools/RomFs/Level6Header.java index c73921a..6ae0171 100644 --- a/src/main/java/konogonka/Tools/RomFs/Level6Header.java +++ b/src/main/java/konogonka/Tools/RomFs/Level6Header.java @@ -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; } } diff --git a/src/main/java/konogonka/Tools/RomFs/RomFsDecryptedProvider.java b/src/main/java/konogonka/Tools/RomFs/RomFsDecryptedProvider.java index ff57205..d0586b0 100644 --- a/src/main/java/konogonka/Tools/RomFs/RomFsDecryptedProvider.java +++ b/src/main/java/konogonka/Tools/RomFs/RomFsDecryptedProvider.java @@ -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(); + } } diff --git a/src/main/resources/FXML/NSP/TableView.fxml b/src/main/resources/FXML/NSP/TableView.fxml index 5b8c2b8..843949a 100644 --- a/src/main/resources/FXML/NSP/TableView.fxml +++ b/src/main/resources/FXML/NSP/TableView.fxml @@ -3,6 +3,25 @@ + + diff --git a/src/main/resources/FXML/RomFS/RFSTab.fxml b/src/main/resources/FXML/RomFS/RFSTab.fxml index bd209f1..0f7a327 100644 --- a/src/main/resources/FXML/RomFS/RFSTab.fxml +++ b/src/main/resources/FXML/RomFS/RFSTab.fxml @@ -1,15 +1,366 @@ + + + + + + + + - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/FXML/RomFS/RFSTableView.fxml b/src/main/resources/FXML/RomFS/RFSTableView.fxml new file mode 100644 index 0000000..d180775 --- /dev/null +++ b/src/main/resources/FXML/RomFS/RFSTableView.fxml @@ -0,0 +1,29 @@ + + + + + + + + + + + + diff --git a/src/main/resources/FXML/landingPage.fxml b/src/main/resources/FXML/landingPage.fxml index 3e2f2a0..8c8496f 100644 --- a/src/main/resources/FXML/landingPage.fxml +++ b/src/main/resources/FXML/landingPage.fxml @@ -43,7 +43,7 @@ - + diff --git a/src/main/resources/res/app_light.css b/src/main/resources/res/app_light.css index 7375d8c..124270d 100644 --- a/src/main/resources/res/app_light.css +++ b/src/main/resources/res/app_light.css @@ -433,4 +433,10 @@ -fx-background-color: #ffbf00; -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; } \ No newline at end of file