From 713ad1a5440bd05711b16f62130b862d8a986f8b Mon Sep 17 00:00:00 2001 From: rihi <19492038+rihi@users.noreply.github.com> Date: Sat, 6 May 2023 02:26:56 +0200 Subject: [PATCH] Reimplement bspinfo --- LICENSE.md | 2 + bspsrc-app/pom.xml | 16 + bspsrc-app/scripts/bspinfo.bat | 2 +- bspsrc-app/scripts/bspinfo.sh | 2 +- .../app/info/{gui => }/BspFileException.java | 2 +- .../app/info/{gui => }/BspFileUtils.java | 2 +- .../info/ata4/bspsrc/app/info/BspInfo.java | 27 + .../bspsrc/app/info/gui/BspInfoFrame.form | 1246 ------------ .../bspsrc/app/info/gui/BspInfoFrame.java | 1693 +++-------------- .../info/ata4/bspsrc/app/info/gui/Util.java | 58 + .../app/info/gui/data/EmbeddedInfo.java | 12 + .../app/info/gui/data/GameLumpInfo.java | 14 + .../bspsrc/app/info/gui/data/LumpInfo.java | 15 + .../app/info/gui/models/BspInfoModel.java | 180 ++ .../info/gui/models/EmbeddedTableModel.java | 56 - .../app/info/gui/models/EntityTableModel.java | 44 - .../info/gui/models/GameLumpTableModel.java | 57 - .../app/info/gui/models/LumpTableModel.java | 59 - .../app/info/gui/panel/DependenciesPanel.java | 62 + .../app/info/gui/panel/EmbeddedPanel.java | 88 + .../app/info/gui/panel/EntitiesPanel.java | 84 + .../app/info/gui/panel/GameLumpsPanel.java | 88 + .../app/info/gui/panel/GeneralPanel.java | 198 ++ .../bspsrc/app/info/gui/panel/LumpsPanel.java | 99 + .../app/info/gui/panel/ProtectionPanel.java | 115 ++ .../app/util/GridBagConstraintsBuilder.java | 123 ++ .../info/ata4/bspsrc/app/util/GuiUtil.java | 106 ++ .../app/util/ReadonlyListTableModel.java | 47 + bspsrc-app/src/main/java/module-info.java | 2 + pom.xml | 19 + 30 files changed, 1595 insertions(+), 2923 deletions(-) rename bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/{gui => }/BspFileException.java (95%) rename bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/{gui => }/BspFileUtils.java (98%) create mode 100644 bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/BspInfo.java delete mode 100644 bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/BspInfoFrame.form create mode 100644 bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/Util.java create mode 100644 bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/data/EmbeddedInfo.java create mode 100644 bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/data/GameLumpInfo.java create mode 100644 bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/data/LumpInfo.java create mode 100644 bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/models/BspInfoModel.java delete mode 100644 bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/models/EmbeddedTableModel.java delete mode 100644 bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/models/EntityTableModel.java delete mode 100644 bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/models/GameLumpTableModel.java delete mode 100644 bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/models/LumpTableModel.java create mode 100644 bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/panel/DependenciesPanel.java create mode 100644 bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/panel/EmbeddedPanel.java create mode 100644 bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/panel/EntitiesPanel.java create mode 100644 bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/panel/GameLumpsPanel.java create mode 100644 bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/panel/GeneralPanel.java create mode 100644 bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/panel/LumpsPanel.java create mode 100644 bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/panel/ProtectionPanel.java create mode 100644 bspsrc-app/src/main/java/info/ata4/bspsrc/app/util/GridBagConstraintsBuilder.java create mode 100644 bspsrc-app/src/main/java/info/ata4/bspsrc/app/util/GuiUtil.java create mode 100644 bspsrc-app/src/main/java/info/ata4/bspsrc/app/util/ReadonlyListTableModel.java diff --git a/LICENSE.md b/LICENSE.md index 89f01475..daad7d72 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -31,5 +31,7 @@ BSPSource uses the following libraries: * [Apache Commons Compress](http://commons.apache.org/compress/) * [picocli](https://github.com/remkop/picocli) +* [FlatLaf](https://github.com/JFormDesigner/FlatLaf) +* [jSystemThemeDetector](https://github.com/Dansoftowner/jSystemThemeDetector) which are licensed under the [Apache License version 2.0](http://www.apache.org/licenses/LICENSE-2.0). diff --git a/bspsrc-app/pom.xml b/bspsrc-app/pom.xml index 3c433adc..da399c22 100644 --- a/bspsrc-app/pom.xml +++ b/bspsrc-app/pom.xml @@ -27,10 +27,26 @@ ioutils + + + org.slf4j + slf4j-jdk14 + + info.picocli picocli + + + com.formdev + flatlaf + + + + com.github.Dansoftowner + jSystemThemeDetector + diff --git a/bspsrc-app/scripts/bspinfo.bat b/bspsrc-app/scripts/bspinfo.bat index 984601b6..1129a993 100644 --- a/bspsrc-app/scripts/bspinfo.bat +++ b/bspsrc-app/scripts/bspinfo.bat @@ -1,2 +1,2 @@ @echo off -start javaw -cp %~dp0\bspsrc.jar info.ata4.bspsrc.app.info.gui.BspInfoFrame %* \ No newline at end of file +start javaw -cp %~dp0\bspsrc.jar info.ata4.bspsrc.app.info.BspInfo %* \ No newline at end of file diff --git a/bspsrc-app/scripts/bspinfo.sh b/bspsrc-app/scripts/bspinfo.sh index 6ff55c29..0c6fd67e 100644 --- a/bspsrc-app/scripts/bspinfo.sh +++ b/bspsrc-app/scripts/bspinfo.sh @@ -1,3 +1,3 @@ #!/bin/sh BASEDIR=$(dirname "$0") -java -cp "$BASEDIR/bspsrc.jar" info.ata4.bspsrc.app.info.gui.BspInfoFrame $* +java -cp "$BASEDIR/bspsrc.jar" info.ata4.bspsrc.app.info.BspInfo $* diff --git a/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/BspFileException.java b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/BspFileException.java similarity index 95% rename from bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/BspFileException.java rename to bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/BspFileException.java index 6b64e904..183bc88e 100644 --- a/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/BspFileException.java +++ b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/BspFileException.java @@ -7,7 +7,7 @@ ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. */ -package info.ata4.bspsrc.app.info.gui; +package info.ata4.bspsrc.app.info; import java.io.IOException; diff --git a/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/BspFileUtils.java b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/BspFileUtils.java similarity index 98% rename from bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/BspFileUtils.java rename to bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/BspFileUtils.java index b13149eb..ab4dff91 100644 --- a/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/BspFileUtils.java +++ b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/BspFileUtils.java @@ -7,7 +7,7 @@ ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. */ -package info.ata4.bspsrc.app.info.gui; +package info.ata4.bspsrc.app.info; import info.ata4.bspsrc.lib.BspFile; import info.ata4.bspsrc.lib.lump.AbstractLump; diff --git a/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/BspInfo.java b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/BspInfo.java new file mode 100644 index 00000000..94841d0d --- /dev/null +++ b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/BspInfo.java @@ -0,0 +1,27 @@ +package info.ata4.bspsrc.app.info; + +import info.ata4.bspsrc.app.info.gui.BspInfoFrame; +import info.ata4.bspsrc.app.info.gui.models.BspInfoModel; +import info.ata4.bspsrc.app.util.GuiUtil; +import info.ata4.log.LogUtils; + +import javax.swing.*; +import java.util.logging.Logger; + +public class BspInfo { + + private static final Logger L = LogUtils.getLogger(); + + /** + * @param args the command line arguments + */ + public static void main(String[] args) { + LogUtils.configure(); + GuiUtil.setupFlatlaf(); + + SwingUtilities.invokeLater(() -> { + var frame = new BspInfoFrame(new BspInfoModel()); + frame.setVisible(true); + }); + } +} diff --git a/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/BspInfoFrame.form b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/BspInfoFrame.form deleted file mode 100644 index 14bbaa90..00000000 --- a/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/BspInfoFrame.form +++ /dev/null @@ -1,1246 +0,0 @@ - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/BspInfoFrame.java b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/BspInfoFrame.java index c447e681..fb61a5a3 100644 --- a/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/BspInfoFrame.java +++ b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/BspInfoFrame.java @@ -1,1477 +1,256 @@ -/* - ** 2012 May 27 - ** - ** The author disclaims copyright to this source code. In place of - ** a legal notice, here is a blessing: - ** May you do good and not evil. - ** May you find forgiveness for yourself and forgive others. - ** May you share freely, never taking more than you give. - */ package info.ata4.bspsrc.app.info.gui; -import info.ata4.bspsrc.app.info.gui.models.EmbeddedTableModel; -import info.ata4.bspsrc.app.info.gui.models.EntityTableModel; -import info.ata4.bspsrc.app.info.gui.models.GameLumpTableModel; -import info.ata4.bspsrc.app.info.gui.models.LumpTableModel; +import info.ata4.bspsrc.app.info.gui.models.BspInfoModel; +import info.ata4.bspsrc.app.info.gui.panel.*; import info.ata4.bspsrc.app.info.log.DialogHandler; -import info.ata4.bspsrc.app.util.FileDrop; import info.ata4.bspsrc.app.util.FileExtensionFilter; -import info.ata4.bspsrc.app.util.components.ByteSizeCellRenderer; -import info.ata4.bspsrc.app.util.components.DecimalFormatCellRenderer; -import info.ata4.bspsrc.app.util.components.ProgressCellRenderer; import info.ata4.bspsrc.decompiler.BspSource; -import info.ata4.bspsrc.decompiler.modules.BspChecksum; -import info.ata4.bspsrc.decompiler.modules.BspCompileParams; -import info.ata4.bspsrc.decompiler.modules.BspDependencies; -import info.ata4.bspsrc.decompiler.modules.BspProtection; -import info.ata4.bspsrc.decompiler.modules.texture.TextureSource; -import info.ata4.bspsrc.lib.BspFile; -import info.ata4.bspsrc.lib.BspFileFilter; -import info.ata4.bspsrc.lib.BspFileReader; -import info.ata4.bspsrc.lib.app.SourceAppDB; -import info.ata4.bspsrc.lib.entity.Entity; -import info.ata4.bspsrc.lib.lump.LumpType; -import info.ata4.bspsrc.lib.struct.BspData; import info.ata4.log.LogUtils; import javax.swing.*; -import javax.swing.table.TableColumnModel; -import javax.swing.table.TableModel; import java.awt.*; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; import java.io.File; import java.io.IOException; -import java.net.URI; import java.net.URL; -import java.nio.ByteOrder; import java.nio.file.Path; -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.Collection; +import java.util.Arrays; import java.util.List; +import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; -/** - * - * @author Nico Bergemann - */ -public class BspInfoFrame extends javax.swing.JFrame { - - private static final Logger L = LogUtils.getLogger(); - - public static final String NAME = "BSPInfo"; - public static final String VERSION = BspSource.VERSION; - - private File currentFile; - private BspFile bspFile; - private BspFileReader bspReader; - private FileDrop fdrop; - - /** - * @param args the command line arguments - */ - public static void main(String args[]) { - LogUtils.configure(); - - // set the system look and feel - try { - UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); - } catch (Exception ex) { - L.warning("Failed to set SystemLookAndFeel"); - } - - // create and display the form - java.awt.EventQueue.invokeLater(new Runnable() { - @Override - public void run() { - new BspInfoFrame().setVisible(true); - } - }); - } - - /** - * Creates new form BspToolFrame - */ - public BspInfoFrame() { - initComponents(); - initComponentsCustom(); - - // init file dropper - fdrop = new FileDrop(this, files -> { - if (new BspFileFilter().accept(files[0])) { - loadFile(files[0]); - } +import static info.ata4.bspsrc.app.info.gui.Util.wrapWithAlign; +import static info.ata4.bspsrc.app.util.GridBagConstraintsBuilder.Anchor.FIRST_LINE_START; +import static info.ata4.bspsrc.app.util.GridBagConstraintsBuilder.Anchor.PAGE_START; +import static info.ata4.bspsrc.app.util.GridBagConstraintsBuilder.Fill.HORIZONTAL; +import static info.ata4.bspsrc.app.util.GridBagConstraintsBuilder.Fill.NONE; +import static java.util.Objects.requireNonNull; +import static javax.swing.BorderFactory.createCompoundBorder; + +public class BspInfoFrame extends JFrame { + + private static final Logger L = LogUtils.getLogger(); + + public static final String NAME = "BSPInfo"; + public static final String VERSION = BspSource.VERSION; + + private final BspInfoModel model; + + private final JFileChooser fileChooser = new JFileChooser(); + private final JFileChooser lumpDstChooser = new JFileChooser(); + private final JFileChooser embeddedFileDstChooser = new JFileChooser(); + private final JFileChooser embeddedRawDstChooser = new JFileChooser(); + + private final JTabbedPane tabbedPane = new JTabbedPane(); + private final GeneralPanel generalPanel = new GeneralPanel(); + private final LumpsPanel lumpsPanel = new LumpsPanel(this::extractLumps); + private final GameLumpsPanel gameLumpsPanel = new GameLumpsPanel(this::extractGameLumps); + private final EntitiesPanel entitiesPanel = new EntitiesPanel(); + private final DependenciesPanel dependenciesPanel = new DependenciesPanel(); + private final EmbeddedPanel embeddedPanel = new EmbeddedPanel(this::extractFiles, this::extractFilesRaw); + private final ProtectionPanel protectionPanel = new ProtectionPanel(); + + + public BspInfoFrame(BspInfoModel model) { + this.model = model; + model.addListener(this::onChanges); + + // add dialog log handler + L.addHandler(new DialogHandler(this)); + + fileChooser.setFileFilter(new FileExtensionFilter("Source engine map file", "bsp")); + lumpDstChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + embeddedFileDstChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + embeddedRawDstChooser.setAcceptAllFileFilterUsed(false); + embeddedRawDstChooser.setFileFilter(new FileExtensionFilter("Zip file", "zip")); + + var tabbedPanePaddingBorder = BorderFactory.createEmptyBorder(5, 5, 5, 5); + + var wrappedGeneralPanel = wrapWithAlign(generalPanel, PAGE_START, HORIZONTAL); + var wrappedProtectionPanel = wrapWithAlign(protectionPanel, FIRST_LINE_START, NONE); + + wrappedGeneralPanel.setBorder(tabbedPanePaddingBorder); + lumpsPanel.setBorder(createCompoundBorder(tabbedPanePaddingBorder, lumpsPanel.getBorder())); + gameLumpsPanel.setBorder(createCompoundBorder(tabbedPanePaddingBorder, gameLumpsPanel.getBorder())); + entitiesPanel.setBorder(createCompoundBorder(tabbedPanePaddingBorder, entitiesPanel.getBorder())); + dependenciesPanel.setBorder(createCompoundBorder(tabbedPanePaddingBorder, dependenciesPanel.getBorder())); + embeddedPanel.setBorder(createCompoundBorder(tabbedPanePaddingBorder, embeddedPanel.getBorder())); + wrappedProtectionPanel.setBorder(tabbedPanePaddingBorder); + + tabbedPane.addTab("General", wrappedGeneralPanel); + tabbedPane.addTab("Lumps", lumpsPanel); + tabbedPane.addTab("Game lumps", gameLumpsPanel); + tabbedPane.addTab("Entities", entitiesPanel); + tabbedPane.addTab("Dependencies", dependenciesPanel); + tabbedPane.addTab("Embedded files", embeddedPanel); + tabbedPane.addTab("Protection", wrappedProtectionPanel); + + setContentPane(tabbedPane); + initMenuBar(); + initTransferHandler(); + + onChanges(); + + setTitle(NAME + " " + VERSION); + + URL iconUrl = requireNonNull(getClass().getResource("resources/icon.png")); + Image icon = Toolkit.getDefaultToolkit().createImage(iconUrl); + setIconImage(icon); + + pack(); + setMinimumSize(getSize()); + setDefaultCloseOperation(DISPOSE_ON_CLOSE); + } + + private void initMenuBar() { + var menuItemOpenFile = new JMenuItem("Open"); + menuItemOpenFile.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, InputEvent.CTRL_DOWN_MASK)); + menuItemOpenFile.addActionListener(e -> { + int result = fileChooser.showOpenDialog(this); + if (result != JFileChooser.APPROVE_OPTION) + return; + + lumpDstChooser.setCurrentDirectory(fileChooser.getCurrentDirectory()); + + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + + try { + model.load(fileChooser.getSelectedFile().toPath()); + } catch (IOException ex) { + L.log(Level.SEVERE, "Error occurred loading file", ex); + } finally { + setCursor(Cursor.getDefaultCursor()); + } }); - // add dialog log handler - L.addHandler(new DialogHandler(this)); - } - - public final void reset() { - // general - textFieldName.setText(null); - textFieldVersion.setText(null); - textFieldRevision.setText(null); - textFieldCompressed.setText(null); - textFieldEndian.setText(null); - textFieldAppID.setText(null); - textFieldGame.setText(null); - linkLabelAppURL.setText(null); - textFieldFileCRC.setText(null); - textFieldMapCRC.setText(null); - - textFieldVbspParams.setText(null); - textFieldVvisParams.setText(null); - textFieldVradParams.setText(null); - - // protection - checkBoxVmexEntity.setSelected(false); - checkBoxVmexTexture.setSelected(false); - checkBoxVmexBrush.setSelected(false); - - checkBoxIIDObfs.setSelected(false); - checkBoxIIDTexHack.setSelected(false); - - checkBoxBSPProtect.setSelected(false); - - // lumps - tableLumps.setModel(new LumpTableModel()); - - // entities - textFieldTotalEnts.setText(null); - textFieldBrushEnts.setText(null); - textFieldPointEnts.setText(null); - - tableEntities.setModel(new EntityTableModel()); - - // dependencies - textAreaMaterials.setText(null); - textAreaSounds.setText(null); - textAreaSoundScripts.setText(null); - textAreaSoundscapes.setText(null); - textAreaModels.setText(null); - textAreaParticles.setText(null); - - // embedded files - tableEmbedded.setModel(new EmbeddedTableModel()); - - // disable buttons - extractLumpButton.setEnabled(false); - extractAllLumpsButton.setEnabled(false); - - extractGameLumpButton.setEnabled(false); - extractAllGameLumpsButton.setEnabled(false); - - extractEmbeddedButton.setEnabled(false); - extractAllEmbeddedButton.setEnabled(false); - extractEmbeddedZipButton.setEnabled(false); - } - - public void loadFile(File file) { - currentFile = file; - - setTitle(NAME + " " + VERSION + " - " + file.getName()); - - new Thread(new Runnable() { - @Override - public void run() { - // clear form fields - reset(); - - // set waiting cursor - setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - - try { - // load BSP file - bspFile = new BspFile(); - bspFile.load(currentFile.toPath()); - - boolean compressed = bspFile.isCompressed(); - - bspReader = new BspFileReader(bspFile); - bspReader.loadEntities(); - - BspData data = bspReader.getData(); - - // general - textFieldName.setText(bspFile.getName()); - textFieldVersion.setText(String.valueOf(bspFile.getVersion())); - textFieldRevision.setText(String.valueOf(bspFile.getRevision())); - textFieldCompressed.setText(compressed ? "Yes" : "No"); - textFieldEndian.setText(bspFile.getByteOrder() == ByteOrder.LITTLE_ENDIAN ? "Little endian" : "Big endian"); - - if (data.entities != null && !data.entities.isEmpty()) { - Entity worldspawn = data.entities.get(0); - textFieldComment.setText(worldspawn.getValue("comment")); - } - - int appId = bspFile.getAppId(); - - textFieldAppID.setText(appId > 0 ? String.valueOf(appId) : "n/a"); - textFieldGame.setText(SourceAppDB.getInstance().getName(appId).orElse("Unknown")); - - URI steamStoreURI = SourceAppDB.getSteamStoreURI(appId); - - if (steamStoreURI != null) { - linkLabelAppURL.setURI("Steam store link", steamStoreURI); - } - - BspCompileParams cparams = new BspCompileParams(bspReader); - - textFieldVbspParams.setText(String.join(" ", cparams.getVbspParams())); - - if (cparams.isVvisRun()) { - textFieldVvisParams.setText(String.join(" ", cparams.getVvisParams())); - } else { - textFieldVvisParams.setText("(not run)"); - } - - if (cparams.isVradRun()) { - textFieldVradParams.setText(String.join(" ", cparams.getVradParams())); - } else { - textFieldVradParams.setText("(not run)"); - } - - // protection - TextureSource texsrc = new TextureSource(bspReader); - BspProtection prot = new BspProtection(bspReader, texsrc); - prot.check(); - - checkBoxVmexEntity.setSelected(prot.hasEntityFlag()); - checkBoxVmexTexture.setSelected(prot.hasTextureFlag()); - checkBoxVmexBrush.setSelected(prot.hasBrushFlag()); - - checkBoxIIDObfs.setSelected(prot.hasObfuscatedEntities()); - checkBoxIIDTexHack.setSelected(prot.hasModifiedTexinfo()); - - checkBoxBSPProtect.setSelected(prot.hasEncryptedEntities()); - - // lumps - tableLumps.setModel(new LumpTableModel(bspFile)); - - // game lumps - tableGameLumps.setModel(new GameLumpTableModel(bspFile)); - - // entities - int brushEnts = 0; - int pointEnts = 0; - - List entities = bspReader.getData().entities; - - for (Entity ent : entities) { - if (ent.getModelNum() > 0) { - brushEnts++; - } else { - pointEnts++; - } - } - - int totalEnts = pointEnts + brushEnts; - - DecimalFormat df = new DecimalFormat("#,##0"); - - textFieldTotalEnts.setText(df.format(totalEnts)); - textFieldBrushEnts.setText(df.format(brushEnts)); - textFieldPointEnts.setText(df.format(pointEnts)); - tableEntities.setModel(new EntityTableModel(bspReader)); - - // dependencies - BspDependencies bspres = new BspDependencies(bspReader); - - fillTextArea(textAreaMaterials, bspres.getMaterials()); - fillTextArea(textAreaSounds, bspres.getSoundFiles()); - fillTextArea(textAreaSoundScripts, bspres.getSoundScripts()); - fillTextArea(textAreaSoundscapes, bspres.getSoundscapes()); - fillTextArea(textAreaModels, bspres.getModels()); - fillTextArea(textAreaParticles, bspres.getParticles()); - - // embedded files - tableEmbedded.setModel(new EmbeddedTableModel(bspFile)); - - // checksum (last step, takes most time) - BspChecksum checksum = new BspChecksum(bspReader); - - textFieldFileCRC.setText(String.format("%x", checksum.getFileCRC())); - textFieldMapCRC.setText(String.format("%x", checksum.getMapCRC())); - - // enable buttons - extractLumpButton.setEnabled(true); - extractAllLumpsButton.setEnabled(true); - - extractGameLumpButton.setEnabled(true); - extractAllGameLumpsButton.setEnabled(true); - - extractEmbeddedButton.setEnabled(true); - extractAllEmbeddedButton.setEnabled(true); - extractEmbeddedZipButton.setEnabled(true); - } catch (Exception ex) { - L.log(Level.SEVERE, "Couldn't read BSP file", ex); - } finally { - // free previously opened files and resources - System.gc(); - - // reset cursor - setCursor(Cursor.getDefaultCursor()); - } - } - }).start(); - } - - private void fillTextArea(JTextArea textArea, Collection strings) { - for (String string : strings) { - textArea.append(string); - textArea.append("\n"); - } - } - - /** - * This method is called from within the constructor to initialize the form. - * WARNING: Do NOT modify this code. The content of this method is always - * regenerated by the Form Editor. - */ - @SuppressWarnings("unchecked") - // //GEN-BEGIN:initComponents - private void initComponents() { - - openFileChooser = new javax.swing.JFileChooser(); - saveDirectoryChooser = new javax.swing.JFileChooser(); - saveZipFileChooser = new javax.swing.JFileChooser(); - tabbedPane = new javax.swing.JTabbedPane(); - panelGeneral = new javax.swing.JPanel(); - panelGame = new javax.swing.JPanel(); - linkLabelAppURL = new info.ata4.bspsrc.app.util.components.URILabel(); - textFieldAppID = new javax.swing.JTextField(); - textFieldGame = new javax.swing.JTextField(); - labelAppID = new javax.swing.JLabel(); - labelGame = new javax.swing.JLabel(); - panelHeaders = new javax.swing.JPanel(); - textFieldEndian = new javax.swing.JTextField(); - textFieldCompressed = new javax.swing.JTextField(); - labelEndian = new javax.swing.JLabel(); - labelCompressed = new javax.swing.JLabel(); - textFieldVersion = new javax.swing.JTextField(); - labelVersion = new javax.swing.JLabel(); - textFieldName = new javax.swing.JTextField(); - labelName = new javax.swing.JLabel(); - textFieldRevision = new javax.swing.JTextField(); - labelRevision = new javax.swing.JLabel(); - labelComment = new javax.swing.JLabel(); - textFieldComment = new javax.swing.JTextField(); - panelChecksums = new javax.swing.JPanel(); - labelFileCRC = new javax.swing.JLabel(); - textFieldFileCRC = new javax.swing.JTextField(); - labelMapCRC = new javax.swing.JLabel(); - textFieldMapCRC = new javax.swing.JTextField(); - panelCompileParams = new javax.swing.JPanel(); - labelVbsp = new javax.swing.JLabel(); - textFieldVbspParams = new javax.swing.JTextField(); - labelVvis = new javax.swing.JLabel(); - textFieldVvisParams = new javax.swing.JTextField(); - labelVrad = new javax.swing.JLabel(); - textFieldVradParams = new javax.swing.JTextField(); - panelLumps = new javax.swing.JPanel(); - scrollPaneLumps = new javax.swing.JScrollPane(); - tableLumps = new javax.swing.JTable(); - extractLumpButton = new javax.swing.JButton(); - extractAllLumpsButton = new javax.swing.JButton(); - panelGameLumps = new javax.swing.JPanel(); - scrollPaneGameLumps = new javax.swing.JScrollPane(); - tableGameLumps = new javax.swing.JTable(); - extractGameLumpButton = new javax.swing.JButton(); - extractAllGameLumpsButton = new javax.swing.JButton(); - panelEntities = new javax.swing.JPanel(); - jLabel3 = new javax.swing.JLabel(); - textFieldPointEnts = new javax.swing.JTextField(); - jLabel2 = new javax.swing.JLabel(); - textFieldBrushEnts = new javax.swing.JTextField(); - textFieldTotalEnts = new javax.swing.JTextField(); - jLabel1 = new javax.swing.JLabel(); - jScrollPane1 = new javax.swing.JScrollPane(); - tableEntities = new javax.swing.JTable(); - tabbedPaneDependencies = new javax.swing.JTabbedPane(); - scrollPaneMaterials = new javax.swing.JScrollPane(); - textAreaMaterials = new javax.swing.JTextArea(); - scrollPaneSounds = new javax.swing.JScrollPane(); - textAreaSounds = new javax.swing.JTextArea(); - scrollPaneSoundScripts = new javax.swing.JScrollPane(); - textAreaSoundScripts = new javax.swing.JTextArea(); - scrollPaneSoundscapes = new javax.swing.JScrollPane(); - textAreaSoundscapes = new javax.swing.JTextArea(); - scrollPaneModels = new javax.swing.JScrollPane(); - textAreaModels = new javax.swing.JTextArea(); - scrollPaneParticles = new javax.swing.JScrollPane(); - textAreaParticles = new javax.swing.JTextArea(); - panelEmbedded = new javax.swing.JPanel(); - scrollPaneEmbedded = new javax.swing.JScrollPane(); - tableEmbedded = new javax.swing.JTable(); - extractEmbeddedButton = new javax.swing.JButton(); - extractAllEmbeddedButton = new javax.swing.JButton(); - extractEmbeddedZipButton = new javax.swing.JButton(); - panelProt = new javax.swing.JPanel(); - panelVmex = new javax.swing.JPanel(); - checkBoxVmexEntity = new info.ata4.bspsrc.app.util.components.ReadOnlyCheckBox(); - checkBoxVmexTexture = new info.ata4.bspsrc.app.util.components.ReadOnlyCheckBox(); - checkBoxVmexBrush = new info.ata4.bspsrc.app.util.components.ReadOnlyCheckBox(); - panelIID = new javax.swing.JPanel(); - checkBoxIIDObfs = new info.ata4.bspsrc.app.util.components.ReadOnlyCheckBox(); - checkBoxIIDTexHack = new info.ata4.bspsrc.app.util.components.ReadOnlyCheckBox(); - panelOther = new javax.swing.JPanel(); - checkBoxBSPProtect = new info.ata4.bspsrc.app.util.components.ReadOnlyCheckBox(); - menuBar = new javax.swing.JMenuBar(); - menuFile = new javax.swing.JMenu(); - openFileMenuItem = new javax.swing.JMenuItem(); - - openFileChooser.setFileFilter(new FileExtensionFilter("Source engine map file", "bsp")); - - saveDirectoryChooser.setDialogType(javax.swing.JFileChooser.SAVE_DIALOG); - saveDirectoryChooser.setFileSelectionMode(javax.swing.JFileChooser.DIRECTORIES_ONLY); - - saveZipFileChooser.setAcceptAllFileFilterUsed(false); - saveZipFileChooser.setDialogType(javax.swing.JFileChooser.SAVE_DIALOG); - saveZipFileChooser.setFileFilter(new FileExtensionFilter("Zip file", "zip")); - - setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); - - panelGame.setBorder(javax.swing.BorderFactory.createTitledBorder("Game")); - - linkLabelAppURL.setHorizontalAlignment(javax.swing.SwingConstants.LEFT); - linkLabelAppURL.setText(" "); - - textFieldAppID.setEditable(false); - - textFieldGame.setEditable(false); - - labelAppID.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); - labelAppID.setText("App-ID"); - - labelGame.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); - labelGame.setText("Name"); - - javax.swing.GroupLayout panelGameLayout = new javax.swing.GroupLayout(panelGame); - panelGame.setLayout(panelGameLayout); - panelGameLayout.setHorizontalGroup( - panelGameLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelGameLayout.createSequentialGroup() - .addContainerGap() - .addGroup(panelGameLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(labelGame) - .addComponent(labelAppID)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addGroup(panelGameLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelGameLayout.createSequentialGroup() - .addComponent(textFieldAppID, javax.swing.GroupLayout.PREFERRED_SIZE, 57, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(linkLabelAppURL, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addComponent(textFieldGame)) - .addContainerGap()) - ); - panelGameLayout.setVerticalGroup( - panelGameLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelGameLayout.createSequentialGroup() - .addContainerGap() - .addGroup(panelGameLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(labelGame) - .addComponent(textFieldGame, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(panelGameLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(labelAppID) - .addComponent(textFieldAppID, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(linkLabelAppURL, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - ); - - panelHeaders.setBorder(javax.swing.BorderFactory.createTitledBorder("Headers")); - - textFieldEndian.setEditable(false); - - textFieldCompressed.setEditable(false); - - labelEndian.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); - labelEndian.setText("Endianness"); - - labelCompressed.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); - labelCompressed.setText("Compressed"); - - textFieldVersion.setEditable(false); - textFieldVersion.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - textFieldVersionActionPerformed(evt); - } - }); - - labelVersion.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); - labelVersion.setText("Version"); - - textFieldName.setEditable(false); - - labelName.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); - labelName.setText("Name"); - - textFieldRevision.setEditable(false); - - labelRevision.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); - labelRevision.setText("Revision"); - - labelComment.setText("Comment"); - - textFieldComment.setEditable(false); - - javax.swing.GroupLayout panelHeadersLayout = new javax.swing.GroupLayout(panelHeaders); - panelHeaders.setLayout(panelHeadersLayout); - panelHeadersLayout.setHorizontalGroup( - panelHeadersLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelHeadersLayout.createSequentialGroup() - .addContainerGap() - .addGroup(panelHeadersLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelHeadersLayout.createSequentialGroup() - .addGroup(panelHeadersLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(labelComment) - .addComponent(labelCompressed)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addGroup(panelHeadersLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelHeadersLayout.createSequentialGroup() - .addComponent(textFieldCompressed, javax.swing.GroupLayout.PREFERRED_SIZE, 39, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(22, 22, 22) - .addComponent(labelEndian) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(textFieldEndian)) - .addComponent(textFieldComment))) - .addGroup(panelHeadersLayout.createSequentialGroup() - .addGap(25, 25, 25) - .addGroup(panelHeadersLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(labelName) - .addComponent(labelVersion)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addGroup(panelHeadersLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelHeadersLayout.createSequentialGroup() - .addComponent(textFieldVersion, javax.swing.GroupLayout.PREFERRED_SIZE, 38, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(36, 36, 36) - .addComponent(labelRevision) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(textFieldRevision)) - .addComponent(textFieldName)))) - .addContainerGap()) - ); - panelHeadersLayout.setVerticalGroup( - panelHeadersLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelHeadersLayout.createSequentialGroup() - .addContainerGap() - .addGroup(panelHeadersLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(labelName) - .addComponent(textFieldName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(panelHeadersLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(labelVersion) - .addComponent(textFieldVersion, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(labelRevision) - .addComponent(textFieldRevision, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(panelHeadersLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(labelCompressed) - .addComponent(textFieldCompressed, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(labelEndian) - .addComponent(textFieldEndian, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(panelHeadersLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(labelComment) - .addComponent(textFieldComment, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - ); - - panelChecksums.setBorder(javax.swing.BorderFactory.createTitledBorder("Checksums")); - - labelFileCRC.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT); - labelFileCRC.setText("File CRC"); - labelFileCRC.setHorizontalTextPosition(javax.swing.SwingConstants.RIGHT); - - textFieldFileCRC.setEditable(false); - - labelMapCRC.setText("Map CRC"); - - textFieldMapCRC.setEditable(false); - - javax.swing.GroupLayout panelChecksumsLayout = new javax.swing.GroupLayout(panelChecksums); - panelChecksums.setLayout(panelChecksumsLayout); - panelChecksumsLayout.setHorizontalGroup( - panelChecksumsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelChecksumsLayout.createSequentialGroup() - .addContainerGap() - .addComponent(labelFileCRC) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(textFieldFileCRC, javax.swing.GroupLayout.PREFERRED_SIZE, 66, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(labelMapCRC) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(textFieldMapCRC, javax.swing.GroupLayout.PREFERRED_SIZE, 66, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(37, Short.MAX_VALUE)) - ); - panelChecksumsLayout.setVerticalGroup( - panelChecksumsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelChecksumsLayout.createSequentialGroup() - .addContainerGap() - .addGroup(panelChecksumsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelChecksumsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(labelMapCRC) - .addComponent(textFieldMapCRC, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGroup(panelChecksumsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(labelFileCRC) - .addComponent(textFieldFileCRC, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - ); - - panelCompileParams.setBorder(javax.swing.BorderFactory.createTitledBorder("Detected compile parameters")); - - labelVbsp.setText("vbsp"); - - textFieldVbspParams.setEditable(false); - - labelVvis.setText("vvis"); - - textFieldVvisParams.setEditable(false); - - labelVrad.setText("vrad"); - - textFieldVradParams.setEditable(false); - - javax.swing.GroupLayout panelCompileParamsLayout = new javax.swing.GroupLayout(panelCompileParams); - panelCompileParams.setLayout(panelCompileParamsLayout); - panelCompileParamsLayout.setHorizontalGroup( - panelCompileParamsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, panelCompileParamsLayout.createSequentialGroup() - .addGap(20, 20, 20) - .addGroup(panelCompileParamsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(labelVrad) - .addComponent(labelVbsp) - .addComponent(labelVvis)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addGroup(panelCompileParamsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(textFieldVradParams) - .addComponent(textFieldVvisParams) - .addComponent(textFieldVbspParams)) - .addContainerGap()) - ); - panelCompileParamsLayout.setVerticalGroup( - panelCompileParamsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelCompileParamsLayout.createSequentialGroup() - .addContainerGap() - .addGroup(panelCompileParamsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(labelVbsp) - .addComponent(textFieldVbspParams, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(panelCompileParamsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(textFieldVvisParams, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(labelVvis)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(panelCompileParamsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(labelVrad) - .addComponent(textFieldVradParams, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - ); - - javax.swing.GroupLayout panelGeneralLayout = new javax.swing.GroupLayout(panelGeneral); - panelGeneral.setLayout(panelGeneralLayout); - panelGeneralLayout.setHorizontalGroup( - panelGeneralLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelGeneralLayout.createSequentialGroup() - .addContainerGap() - .addGroup(panelGeneralLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(panelHeaders, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(panelGame, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(panelChecksums, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(panelCompileParams, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addContainerGap()) - ); - panelGeneralLayout.setVerticalGroup( - panelGeneralLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelGeneralLayout.createSequentialGroup() - .addContainerGap() - .addComponent(panelHeaders, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(panelGame, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(panelChecksums, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(panelCompileParams, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(18, Short.MAX_VALUE)) - ); - - tabbedPane.addTab("General", panelGeneral); - - tableLumps.setAutoCreateRowSorter(true); - tableLumps.setModel(new LumpTableModel()); - tableLumps.getTableHeader().setReorderingAllowed(false); - scrollPaneLumps.setViewportView(tableLumps); - - extractLumpButton.setText("Extract"); - extractLumpButton.setEnabled(false); - extractLumpButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - extractLumpButtonActionPerformed(evt); - } - }); - - extractAllLumpsButton.setText("Extract all"); - extractAllLumpsButton.setEnabled(false); - extractAllLumpsButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - extractAllLumpsButtonActionPerformed(evt); - } - }); - - javax.swing.GroupLayout panelLumpsLayout = new javax.swing.GroupLayout(panelLumps); - panelLumps.setLayout(panelLumpsLayout); - panelLumpsLayout.setHorizontalGroup( - panelLumpsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelLumpsLayout.createSequentialGroup() - .addContainerGap() - .addGroup(panelLumpsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(scrollPaneLumps, javax.swing.GroupLayout.DEFAULT_SIZE, 305, Short.MAX_VALUE) - .addGroup(panelLumpsLayout.createSequentialGroup() - .addComponent(extractLumpButton) - .addGap(18, 18, 18) - .addComponent(extractAllLumpsButton) - .addGap(0, 0, Short.MAX_VALUE))) - .addContainerGap()) - ); - panelLumpsLayout.setVerticalGroup( - panelLumpsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelLumpsLayout.createSequentialGroup() - .addContainerGap() - .addComponent(scrollPaneLumps, javax.swing.GroupLayout.DEFAULT_SIZE, 412, Short.MAX_VALUE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(panelLumpsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(extractLumpButton) - .addComponent(extractAllLumpsButton)) - .addContainerGap()) - ); - - tabbedPane.addTab("Lumps", panelLumps); - - tableGameLumps.setAutoCreateRowSorter(true); - tableGameLumps.setModel(new GameLumpTableModel()); - tableGameLumps.getTableHeader().setReorderingAllowed(false); - scrollPaneGameLumps.setViewportView(tableGameLumps); - - extractGameLumpButton.setText("Extract"); - extractGameLumpButton.setEnabled(false); - extractGameLumpButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - extractGameLumpButtonActionPerformed(evt); - } - }); - - extractAllGameLumpsButton.setText("Extract all"); - extractAllGameLumpsButton.setEnabled(false); - extractAllGameLumpsButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - extractAllGameLumpsButtonActionPerformed(evt); - } - }); - - javax.swing.GroupLayout panelGameLumpsLayout = new javax.swing.GroupLayout(panelGameLumps); - panelGameLumps.setLayout(panelGameLumpsLayout); - panelGameLumpsLayout.setHorizontalGroup( - panelGameLumpsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelGameLumpsLayout.createSequentialGroup() - .addContainerGap() - .addGroup(panelGameLumpsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(scrollPaneGameLumps, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE) - .addGroup(panelGameLumpsLayout.createSequentialGroup() - .addComponent(extractGameLumpButton) - .addGap(18, 18, 18) - .addComponent(extractAllGameLumpsButton) - .addGap(0, 139, Short.MAX_VALUE))) - .addContainerGap()) - ); - panelGameLumpsLayout.setVerticalGroup( - panelGameLumpsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelGameLumpsLayout.createSequentialGroup() - .addContainerGap() - .addComponent(scrollPaneGameLumps, javax.swing.GroupLayout.DEFAULT_SIZE, 412, Short.MAX_VALUE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(panelGameLumpsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(extractGameLumpButton) - .addComponent(extractAllGameLumpsButton)) - .addContainerGap()) - ); - - tabbedPane.addTab("Game lumps", panelGameLumps); - - jLabel3.setText("Point"); - - textFieldPointEnts.setEditable(false); - - jLabel2.setText("Brush"); - - textFieldBrushEnts.setEditable(false); - - textFieldTotalEnts.setEditable(false); - - jLabel1.setText("Total"); - - tableEntities.setAutoCreateRowSorter(true); - tableEntities.setModel(new EntityTableModel()); - jScrollPane1.setViewportView(tableEntities); - - javax.swing.GroupLayout panelEntitiesLayout = new javax.swing.GroupLayout(panelEntities); - panelEntities.setLayout(panelEntitiesLayout); - panelEntitiesLayout.setHorizontalGroup( - panelEntitiesLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelEntitiesLayout.createSequentialGroup() - .addContainerGap() - .addGroup(panelEntitiesLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelEntitiesLayout.createSequentialGroup() - .addComponent(jLabel3) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(textFieldPointEnts, javax.swing.GroupLayout.PREFERRED_SIZE, 60, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(jLabel2) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(textFieldBrushEnts, javax.swing.GroupLayout.PREFERRED_SIZE, 60, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(jLabel1) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(textFieldTotalEnts, javax.swing.GroupLayout.PREFERRED_SIZE, 60, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(0, 0, Short.MAX_VALUE)) - .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)) - .addContainerGap()) - ); - panelEntitiesLayout.setVerticalGroup( - panelEntitiesLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelEntitiesLayout.createSequentialGroup() - .addContainerGap() - .addGroup(panelEntitiesLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jLabel3) - .addComponent(textFieldPointEnts, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(jLabel2) - .addComponent(textFieldBrushEnts, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(jLabel1) - .addComponent(textFieldTotalEnts, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 410, Short.MAX_VALUE) - .addContainerGap()) - ); - - tabbedPane.addTab("Entities", panelEntities); - - textAreaMaterials.setColumns(20); - textAreaMaterials.setEditable(false); - textAreaMaterials.setFont(new java.awt.Font("Monospaced", 0, 12)); // NOI18N - textAreaMaterials.setRows(5); - scrollPaneMaterials.setViewportView(textAreaMaterials); - - tabbedPaneDependencies.addTab("Materials", scrollPaneMaterials); - - textAreaSounds.setColumns(20); - textAreaSounds.setFont(new java.awt.Font("Monospaced", 0, 12)); // NOI18N - textAreaSounds.setRows(5); - scrollPaneSounds.setViewportView(textAreaSounds); - - tabbedPaneDependencies.addTab("Sounds", scrollPaneSounds); - - textAreaSoundScripts.setColumns(20); - textAreaSoundScripts.setFont(new java.awt.Font("Monospaced", 0, 12)); // NOI18N - textAreaSoundScripts.setRows(5); - scrollPaneSoundScripts.setViewportView(textAreaSoundScripts); - - tabbedPaneDependencies.addTab("Sound scripts", scrollPaneSoundScripts); - - textAreaSoundscapes.setColumns(20); - textAreaSoundscapes.setFont(new java.awt.Font("Monospaced", 0, 12)); // NOI18N - textAreaSoundscapes.setRows(5); - scrollPaneSoundscapes.setViewportView(textAreaSoundscapes); - - tabbedPaneDependencies.addTab("Soundscapes", scrollPaneSoundscapes); - - textAreaModels.setColumns(20); - textAreaModels.setFont(new java.awt.Font("Monospaced", 0, 12)); // NOI18N - textAreaModels.setRows(5); - scrollPaneModels.setViewportView(textAreaModels); - - tabbedPaneDependencies.addTab("Models", scrollPaneModels); - - textAreaParticles.setColumns(20); - textAreaParticles.setFont(new java.awt.Font("Monospaced", 0, 12)); // NOI18N - textAreaParticles.setRows(5); - scrollPaneParticles.setViewportView(textAreaParticles); - - tabbedPaneDependencies.addTab("Particles", scrollPaneParticles); - - tabbedPane.addTab("Dependencies", tabbedPaneDependencies); - - tableEmbedded.setAutoCreateRowSorter(true); - tableEmbedded.setModel(new EmbeddedTableModel()); - tableEmbedded.setSelectionMode(javax.swing.ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); - scrollPaneEmbedded.setViewportView(tableEmbedded); - - extractEmbeddedButton.setText("Extract"); - extractEmbeddedButton.setEnabled(false); - extractEmbeddedButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - extractEmbeddedButtonActionPerformed(evt); - } - }); - - extractAllEmbeddedButton.setText("Extract all"); - extractAllEmbeddedButton.setEnabled(false); - extractAllEmbeddedButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - extractAllEmbeddedButtonActionPerformed(evt); - } - }); - - extractEmbeddedZipButton.setText("Extract Zip file"); - extractEmbeddedZipButton.setEnabled(false); - extractEmbeddedZipButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - extractEmbeddedZipButtonActionPerformed(evt); - } - }); - - javax.swing.GroupLayout panelEmbeddedLayout = new javax.swing.GroupLayout(panelEmbedded); - panelEmbedded.setLayout(panelEmbeddedLayout); - panelEmbeddedLayout.setHorizontalGroup( - panelEmbeddedLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelEmbeddedLayout.createSequentialGroup() - .addContainerGap() - .addGroup(panelEmbeddedLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(scrollPaneEmbedded, javax.swing.GroupLayout.DEFAULT_SIZE, 305, Short.MAX_VALUE) - .addGroup(panelEmbeddedLayout.createSequentialGroup() - .addComponent(extractEmbeddedButton) - .addGap(18, 18, 18) - .addComponent(extractAllEmbeddedButton) - .addGap(18, 18, 18) - .addComponent(extractEmbeddedZipButton) - .addGap(0, 0, Short.MAX_VALUE))) - .addContainerGap()) - ); - panelEmbeddedLayout.setVerticalGroup( - panelEmbeddedLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelEmbeddedLayout.createSequentialGroup() - .addContainerGap() - .addComponent(scrollPaneEmbedded, javax.swing.GroupLayout.DEFAULT_SIZE, 412, Short.MAX_VALUE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(panelEmbeddedLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(extractEmbeddedButton) - .addComponent(extractAllEmbeddedButton) - .addComponent(extractEmbeddedZipButton)) - .addContainerGap()) - ); - - tabbedPane.addTab("Embedded files", panelEmbedded); - - panelVmex.setBorder(javax.swing.BorderFactory.createTitledBorder("VMEX")); - - checkBoxVmexEntity.setText("Entity flag"); - checkBoxVmexEntity.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT); - checkBoxVmexEntity.setIconTextGap(6); - - checkBoxVmexTexture.setText("Texture flag"); - checkBoxVmexTexture.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT); - checkBoxVmexTexture.setIconTextGap(6); - - checkBoxVmexBrush.setText("Protector brush"); - checkBoxVmexBrush.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT); - checkBoxVmexBrush.setIconTextGap(6); - - javax.swing.GroupLayout panelVmexLayout = new javax.swing.GroupLayout(panelVmex); - panelVmex.setLayout(panelVmexLayout); - panelVmexLayout.setHorizontalGroup( - panelVmexLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, panelVmexLayout.createSequentialGroup() - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGroup(panelVmexLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(checkBoxVmexBrush, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(checkBoxVmexTexture, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(checkBoxVmexEntity, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addContainerGap()) - ); - panelVmexLayout.setVerticalGroup( - panelVmexLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelVmexLayout.createSequentialGroup() - .addComponent(checkBoxVmexEntity, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(checkBoxVmexTexture, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(checkBoxVmexBrush, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - ); - - panelIID.setBorder(javax.swing.BorderFactory.createTitledBorder("IID")); - - checkBoxIIDObfs.setText("Entity obfuscation"); - checkBoxIIDObfs.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT); - checkBoxIIDObfs.setIconTextGap(6); - - checkBoxIIDTexHack.setText("Nodraw texture hack"); - checkBoxIIDTexHack.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT); - checkBoxIIDTexHack.setIconTextGap(6); - - javax.swing.GroupLayout panelIIDLayout = new javax.swing.GroupLayout(panelIID); - panelIID.setLayout(panelIIDLayout); - panelIIDLayout.setHorizontalGroup( - panelIIDLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelIIDLayout.createSequentialGroup() - .addContainerGap(17, Short.MAX_VALUE) - .addGroup(panelIIDLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(checkBoxIIDTexHack, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(checkBoxIIDObfs, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addContainerGap()) - ); - panelIIDLayout.setVerticalGroup( - panelIIDLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelIIDLayout.createSequentialGroup() - .addComponent(checkBoxIIDObfs, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(checkBoxIIDTexHack, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - ); - - panelOther.setBorder(javax.swing.BorderFactory.createTitledBorder("Other")); - - checkBoxBSPProtect.setText("BSPProtect"); - checkBoxBSPProtect.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT); - checkBoxBSPProtect.setIconTextGap(6); - - javax.swing.GroupLayout panelOtherLayout = new javax.swing.GroupLayout(panelOther); - panelOther.setLayout(panelOtherLayout); - panelOtherLayout.setHorizontalGroup( - panelOtherLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, panelOtherLayout.createSequentialGroup() - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(checkBoxBSPProtect, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap()) - ); - panelOtherLayout.setVerticalGroup( - panelOtherLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(checkBoxBSPProtect, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - ); - - javax.swing.GroupLayout panelProtLayout = new javax.swing.GroupLayout(panelProt); - panelProt.setLayout(panelProtLayout); - panelProtLayout.setHorizontalGroup( - panelProtLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelProtLayout.createSequentialGroup() - .addContainerGap() - .addGroup(panelProtLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) - .addComponent(panelVmex, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(panelIID, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(panelOther, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addContainerGap(151, Short.MAX_VALUE)) - ); - panelProtLayout.setVerticalGroup( - panelProtLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(panelProtLayout.createSequentialGroup() - .addContainerGap() - .addComponent(panelVmex, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(panelIID, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(panelOther, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(233, Short.MAX_VALUE)) - ); - - tabbedPane.addTab("Protection", panelProt); - - menuFile.setText("File"); - - openFileMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_O, java.awt.event.InputEvent.CTRL_MASK)); - openFileMenuItem.setText("Open"); - openFileMenuItem.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - openFileMenuItemActionPerformed(evt); - } - }); - menuFile.add(openFileMenuItem); - - menuBar.add(menuFile); - - setJMenuBar(menuBar); - - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); - getContentPane().setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addComponent(tabbedPane) - .addContainerGap()) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addComponent(tabbedPane) - .addContainerGap()) - ); - - pack(); - }// //GEN-END:initComponents - - private void initComponentsCustom() { - // add version to title - setTitle(NAME + " " + VERSION); - - // instant awesome, just add icons! - try { - URL iconUrl = getClass().getResource("resources/icon.png"); - Image icon = Toolkit.getDefaultToolkit().createImage(iconUrl); - setIconImage(icon); - } catch (Exception ex) { - // meh, don't care - } - - DecimalFormat largeFormat = new DecimalFormat("#,##0"); - - // set table column widths and special renderers - TableColumnModel tcm; - - // lump table - tcm = tableLumps.getColumnModel(); - tcm.getColumn(0).setPreferredWidth(30); - tcm.getColumn(1).setPreferredWidth(150); - tcm.getColumn(4).setPreferredWidth(40); - tcm.getColumn(2).setCellRenderer(new ByteSizeCellRenderer()); - tcm.getColumn(3).setCellRenderer(new ProgressCellRenderer()); - tableLumps.setAutoCreateColumnsFromModel(false); - - // game lump table - tcm = tableGameLumps.getColumnModel(); - tcm.getColumn(0).setPreferredWidth(30); - tcm.getColumn(3).setPreferredWidth(40); - tcm.getColumn(1).setCellRenderer(new ByteSizeCellRenderer()); - tcm.getColumn(2).setCellRenderer(new ProgressCellRenderer()); - tableGameLumps.setAutoCreateColumnsFromModel(false); - - // entity table - tcm = tableEntities.getColumnModel(); - tcm.getColumn(0).setPreferredWidth(250); - tcm.getColumn(1).setPreferredWidth(50); - tcm.getColumn(1).setCellRenderer(new DecimalFormatCellRenderer(largeFormat)); - tableEntities.setAutoCreateColumnsFromModel(false); - - // embedded table - tcm = tableEmbedded.getColumnModel(); - tcm.getColumn(0).setPreferredWidth(250); - tcm.getColumn(1).setPreferredWidth(50); - tcm.getColumn(1).setCellRenderer(new ByteSizeCellRenderer()); - tableEmbedded.setAutoCreateColumnsFromModel(false); - } - - private void openFileMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_openFileMenuItemActionPerformed - int result = openFileChooser.showOpenDialog(this); - if (result != JFileChooser.APPROVE_OPTION) { - return; - } - - loadFile(openFileChooser.getSelectedFile()); - }//GEN-LAST:event_openFileMenuItemActionPerformed - - private void extractLumpButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_extractLumpButtonActionPerformed - int[] selected = tableLumps.getSelectedRows(); - - if (selected.length == 0) { - return; - } - - saveDirectoryChooser.setCurrentDirectory(currentFile); - int result = saveDirectoryChooser.showSaveDialog(this); - if (result != JFileChooser.APPROVE_OPTION) { - return; - } - - Path dest = saveDirectoryChooser.getSelectedFile().toPath(); - - // set waiting cursor - setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - - try { - int files = 0; - TableModel model = tableLumps.getModel(); - RowSorter sorter = tableLumps.getRowSorter(); - - for (int index : selected) { - index = sorter.convertRowIndexToModel(index); - int lumpIndex = (Integer) model.getValueAt(index, 0); - LumpType lumpType = LumpType.get(lumpIndex, bspFile.getVersion()); - - try { - BspFileUtils.extractLump(bspFile, lumpType, dest); - files++; - } catch (IOException ex) { - L.log(Level.WARNING, "Couldn't extract lump " + lumpType, ex); - } - } - - JOptionPane.showMessageDialog(this, "Successfully extracted " + files + " lumps."); - } finally { - // reset cursor - setCursor(Cursor.getDefaultCursor()); - } - }//GEN-LAST:event_extractLumpButtonActionPerformed - - private void extractAllLumpsButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_extractAllLumpsButtonActionPerformed - saveDirectoryChooser.setCurrentDirectory(currentFile); - int result = saveDirectoryChooser.showSaveDialog(this); - if (result != JFileChooser.APPROVE_OPTION) { - return; - } - - Path dest = saveDirectoryChooser.getSelectedFile().toPath(); - - // set waiting cursor - setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - - try { - BspFileUtils.extractLumps(bspFile, dest); - JOptionPane.showMessageDialog(this, "Successfully extracted all lumps."); - } catch (IOException ex) { - L.log(Level.WARNING, "Couldn't extract lumps", ex); - } finally { - // reset cursor - setCursor(Cursor.getDefaultCursor()); - } - }//GEN-LAST:event_extractAllLumpsButtonActionPerformed - - private void extractGameLumpButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_extractGameLumpButtonActionPerformed - int[] selected = tableGameLumps.getSelectedRows(); - - if (selected.length == 0) { - return; - } - - saveDirectoryChooser.setCurrentDirectory(currentFile); - int result = saveDirectoryChooser.showSaveDialog(this); - if (result != JFileChooser.APPROVE_OPTION) { - return; - } - - Path dest = saveDirectoryChooser.getSelectedFile().toPath(); - - // set waiting cursor - setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - - try { - int files = 0; - TableModel model = tableGameLumps.getModel(); - RowSorter sorter = tableGameLumps.getRowSorter(); - - for (int index : selected) { - index = sorter.convertRowIndexToModel(index); - String id = (String) model.getValueAt(index, 0); - - try { - BspFileUtils.extractGameLump(bspFile, id, dest); - files++; - } catch (IOException ex) { - L.log(Level.WARNING, "Couldn't extract game lump " + id, ex); - } - } - - JOptionPane.showMessageDialog(this, "Successfully extracted " + files + " game lumps."); - } finally { - // reset cursor - setCursor(Cursor.getDefaultCursor()); - } - }//GEN-LAST:event_extractGameLumpButtonActionPerformed - - private void extractAllGameLumpsButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_extractAllGameLumpsButtonActionPerformed - saveDirectoryChooser.setCurrentDirectory(currentFile); - int result = saveDirectoryChooser.showSaveDialog(this); - if (result != JFileChooser.APPROVE_OPTION) { - return; - } - - Path dest = saveDirectoryChooser.getSelectedFile().toPath(); - - // set waiting cursor - setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - - try { - BspFileUtils.extractGameLumps(bspFile, dest); - JOptionPane.showMessageDialog(this, "Successfully extracted all game lumps."); - } catch (IOException ex) { - L.log(Level.WARNING, "Couldn't extract lumps", ex); - } finally { - // reset cursor - setCursor(Cursor.getDefaultCursor()); - } - }//GEN-LAST:event_extractAllGameLumpsButtonActionPerformed - - private void extractEmbeddedButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_extractEmbeddedButtonActionPerformed - int[] selected = tableEmbedded.getSelectedRows(); - - if (selected.length == 0) { - return; - } - - saveDirectoryChooser.setCurrentDirectory(currentFile); - int result = saveDirectoryChooser.showSaveDialog(this); - if (result != JFileChooser.APPROVE_OPTION) { - return; - } - - File dest = saveDirectoryChooser.getSelectedFile(); - - // set waiting cursor - setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - - List names = new ArrayList<>(); - TableModel model = tableEmbedded.getModel(); - RowSorter sorter = tableEmbedded.getRowSorter(); - - for (int index : selected) { - index = sorter.convertRowIndexToModel(index); - names.add((String) model.getValueAt(index, 0)); - } - - try { - bspFile.getPakFile().unpack(dest.toPath(), names::contains); - - JOptionPane.showMessageDialog(this, "Successfully extracted " + names.size() + " embedded files."); - } catch (IOException ex) { - L.log(Level.WARNING, "Couldn't extract embedded files", ex); - } finally { - // reset cursor - setCursor(Cursor.getDefaultCursor()); - } - }//GEN-LAST:event_extractEmbeddedButtonActionPerformed - - private void extractAllEmbeddedButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_extractAllEmbeddedButtonActionPerformed - saveDirectoryChooser.setCurrentDirectory(currentFile); - int result = saveDirectoryChooser.showSaveDialog(this); - if (result != JFileChooser.APPROVE_OPTION) { - return; - } - - File dest = saveDirectoryChooser.getSelectedFile(); - - // set waiting cursor - setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - - try { - bspFile.getPakFile().unpack(dest.toPath(), false); - JOptionPane.showMessageDialog(this, "Successfully extracted all embedded files."); - } catch (IOException ex) { - L.log(Level.WARNING, "Couldn't extract embedded files", ex); - } finally { - // reset cursor - setCursor(Cursor.getDefaultCursor()); - } - }//GEN-LAST:event_extractAllEmbeddedButtonActionPerformed - - private void extractEmbeddedZipButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_extractEmbeddedZipButtonActionPerformed - saveZipFileChooser.setSelectedFile(new File(currentFile.getParent(), bspFile.getName() + ".zip")); - int result = saveZipFileChooser.showSaveDialog(this); - if (result != JFileChooser.APPROVE_OPTION) { - return; - } - - File dest = saveZipFileChooser.getSelectedFile(); - - // set waiting cursor - setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); - - try { - bspFile.getPakFile().unpack(dest.toPath(), true); - JOptionPane.showMessageDialog(this, "Successfully extracted embedded Zip file."); - } catch (IOException ex) { - L.log(Level.WARNING, "Couldn't extract embedded Zip file", ex); - } finally { - // reset cursor - setCursor(Cursor.getDefaultCursor()); - } - }//GEN-LAST:event_extractEmbeddedZipButtonActionPerformed - - private void textFieldVersionActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_textFieldVersionActionPerformed - // TODO add your handling code here: - }//GEN-LAST:event_textFieldVersionActionPerformed - - // Variables declaration - do not modify//GEN-BEGIN:variables - private info.ata4.bspsrc.app.util.components.ReadOnlyCheckBox checkBoxBSPProtect; - private info.ata4.bspsrc.app.util.components.ReadOnlyCheckBox checkBoxIIDObfs; - private info.ata4.bspsrc.app.util.components.ReadOnlyCheckBox checkBoxIIDTexHack; - private info.ata4.bspsrc.app.util.components.ReadOnlyCheckBox checkBoxVmexBrush; - private info.ata4.bspsrc.app.util.components.ReadOnlyCheckBox checkBoxVmexEntity; - private info.ata4.bspsrc.app.util.components.ReadOnlyCheckBox checkBoxVmexTexture; - private javax.swing.JButton extractAllEmbeddedButton; - private javax.swing.JButton extractAllGameLumpsButton; - private javax.swing.JButton extractAllLumpsButton; - private javax.swing.JButton extractEmbeddedButton; - private javax.swing.JButton extractEmbeddedZipButton; - private javax.swing.JButton extractGameLumpButton; - private javax.swing.JButton extractLumpButton; - private javax.swing.JLabel jLabel1; - private javax.swing.JLabel jLabel2; - private javax.swing.JLabel jLabel3; - private javax.swing.JScrollPane jScrollPane1; - private javax.swing.JLabel labelAppID; - private javax.swing.JLabel labelComment; - private javax.swing.JLabel labelCompressed; - private javax.swing.JLabel labelEndian; - private javax.swing.JLabel labelFileCRC; - private javax.swing.JLabel labelGame; - private javax.swing.JLabel labelMapCRC; - private javax.swing.JLabel labelName; - private javax.swing.JLabel labelRevision; - private javax.swing.JLabel labelVbsp; - private javax.swing.JLabel labelVersion; - private javax.swing.JLabel labelVrad; - private javax.swing.JLabel labelVvis; - private info.ata4.bspsrc.app.util.components.URILabel linkLabelAppURL; - private javax.swing.JMenuBar menuBar; - private javax.swing.JMenu menuFile; - private javax.swing.JFileChooser openFileChooser; - private javax.swing.JMenuItem openFileMenuItem; - private javax.swing.JPanel panelChecksums; - private javax.swing.JPanel panelCompileParams; - private javax.swing.JPanel panelEmbedded; - private javax.swing.JPanel panelEntities; - private javax.swing.JPanel panelGame; - private javax.swing.JPanel panelGameLumps; - private javax.swing.JPanel panelGeneral; - private javax.swing.JPanel panelHeaders; - private javax.swing.JPanel panelIID; - private javax.swing.JPanel panelLumps; - private javax.swing.JPanel panelOther; - private javax.swing.JPanel panelProt; - private javax.swing.JPanel panelVmex; - private javax.swing.JFileChooser saveDirectoryChooser; - private javax.swing.JFileChooser saveZipFileChooser; - private javax.swing.JScrollPane scrollPaneEmbedded; - private javax.swing.JScrollPane scrollPaneGameLumps; - private javax.swing.JScrollPane scrollPaneLumps; - private javax.swing.JScrollPane scrollPaneMaterials; - private javax.swing.JScrollPane scrollPaneModels; - private javax.swing.JScrollPane scrollPaneParticles; - private javax.swing.JScrollPane scrollPaneSoundScripts; - private javax.swing.JScrollPane scrollPaneSounds; - private javax.swing.JScrollPane scrollPaneSoundscapes; - private javax.swing.JTabbedPane tabbedPane; - private javax.swing.JTabbedPane tabbedPaneDependencies; - private javax.swing.JTable tableEmbedded; - private javax.swing.JTable tableEntities; - private javax.swing.JTable tableGameLumps; - private javax.swing.JTable tableLumps; - private javax.swing.JTextArea textAreaMaterials; - private javax.swing.JTextArea textAreaModels; - private javax.swing.JTextArea textAreaParticles; - private javax.swing.JTextArea textAreaSoundScripts; - private javax.swing.JTextArea textAreaSounds; - private javax.swing.JTextArea textAreaSoundscapes; - private javax.swing.JTextField textFieldAppID; - private javax.swing.JTextField textFieldBrushEnts; - private javax.swing.JTextField textFieldComment; - private javax.swing.JTextField textFieldCompressed; - private javax.swing.JTextField textFieldEndian; - private javax.swing.JTextField textFieldFileCRC; - private javax.swing.JTextField textFieldGame; - private javax.swing.JTextField textFieldMapCRC; - private javax.swing.JTextField textFieldName; - private javax.swing.JTextField textFieldPointEnts; - private javax.swing.JTextField textFieldRevision; - private javax.swing.JTextField textFieldTotalEnts; - private javax.swing.JTextField textFieldVbspParams; - private javax.swing.JTextField textFieldVersion; - private javax.swing.JTextField textFieldVradParams; - private javax.swing.JTextField textFieldVvisParams; - // End of variables declaration//GEN-END:variables + var menuFile = new JMenu("File"); + menuFile.add(menuItemOpenFile); + + var menuBar = new JMenuBar(); + menuBar.add(menuFile); + + setJMenuBar(menuBar); + } + + private void initTransferHandler() { + setTransferHandler(new TransferHandler() { + @Override + public boolean canImport(TransferSupport support) { + return Arrays.stream(support.getDataFlavors()) + .anyMatch(DataFlavor::isFlavorJavaFileListType); + } + + @Override + public boolean importData(TransferSupport support) { + try { + List files = (List) support.getTransferable() + .getTransferData(DataFlavor.javaFileListFlavor); + + model.load(files.get(files.size() - 1).toPath()); + return true; + } catch (UnsupportedFlavorException | IOException e) { + L.log(Level.WARNING, "Error in drag and drop", e); + return false; + } + } + }); + } + + private void onChanges() { + generalPanel.update(model); + lumpsPanel.update(model); + gameLumpsPanel.update(model); + entitiesPanel.update(model); + dependenciesPanel.update(model); + embeddedPanel.update(model); + protectionPanel.update(model); + } + + private void extractLumps(Set lumpIndices) { + Path lumpDst = chooseDstDialog(lumpDstChooser); + if (lumpDst == null) + return; + + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + + try { + model.extractLumps(lumpIndices, lumpDst); + } catch (IOException e) { + L.log(Level.SEVERE, "Error occurred extracting lump(s)", e); + return; + } finally { + setCursor(Cursor.getDefaultCursor()); + } + + JOptionPane.showMessageDialog(this, "Successfully extracted lump(s)."); + } + + private void extractGameLumps(Set lumpIndices) { + Path lumpDst = chooseDstDialog(lumpDstChooser); + if (lumpDst == null) + return; + + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + + try { + model.extractGameLumps(lumpIndices, lumpDst); + } catch (IOException e) { + L.log(Level.SEVERE, "Error occurred extracting game lump(s)", e); + return; + } finally { + setCursor(Cursor.getDefaultCursor()); + } + + JOptionPane.showMessageDialog(this, "Successfully extracted game lump(s)."); + } + + private void extractFiles(Set fileIndices) { + Path filesDst = chooseDstDialog(embeddedFileDstChooser); + if (filesDst == null) + return; + + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + + try { + model.extractEmbeddedFiles(fileIndices, filesDst); + } catch (IOException e) { + L.log(Level.SEVERE, "Error occurred extracting embedded file(s)", e); + return; + } finally { + setCursor(Cursor.getDefaultCursor()); + } + + JOptionPane.showMessageDialog(this, "Successfully extracted embedded file(s)."); + } + + private void extractFilesRaw() { + Path filesDst = chooseDstDialog(embeddedRawDstChooser); + if (filesDst == null) + return; + + setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); + + try { + model.extractEmbeddedFilesRaw(filesDst); + } catch (IOException e) { + L.log(Level.SEVERE, "Error occurred extracting embedded files", e); + return; + } finally { + setCursor(Cursor.getDefaultCursor()); + } + + JOptionPane.showMessageDialog(this, "Successfully extracted embedded files."); + } + + private Path chooseDstDialog(JFileChooser fileChooser) { + int result = fileChooser.showSaveDialog(this); + if (result == JFileChooser.APPROVE_OPTION) + return fileChooser.getSelectedFile().toPath(); + else + return null; + } } diff --git a/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/Util.java b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/Util.java new file mode 100644 index 00000000..fbc66e65 --- /dev/null +++ b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/Util.java @@ -0,0 +1,58 @@ +package info.ata4.bspsrc.app.info.gui; + +import info.ata4.bspsrc.app.util.GridBagConstraintsBuilder; + +import javax.swing.*; +import javax.swing.border.Border; +import java.awt.*; + +public class Util { + + public static final int PANEL_INTERNAL_PADDING = 8; + public static final Insets PANEL_COMPONENT_INSETS = new Insets(3, 3, 3, 3); + + public static Border createPanelBorder(String title) { + return BorderFactory.createCompoundBorder( + BorderFactory.createTitledBorder(title), + createInternalPaddingBorder() + ); + } + public static Border createInternalPaddingBorder() { + return BorderFactory.createEmptyBorder( + PANEL_INTERNAL_PADDING, + PANEL_INTERNAL_PADDING, + PANEL_INTERNAL_PADDING, + PANEL_INTERNAL_PADDING + ); + } + + public static JTextField createDisplayTxtField(int columns) { + var textField = new JTextField(columns); + textField.setEditable(false); + textField.setTransferHandler(null); + return textField; + } + + public static JCheckBox createDisplayCheckbox() { + var checkBox = new JCheckBox(); + checkBox.setEnabled(false); + return checkBox; + } + + public static JPanel wrapWithAlign( + Container container, + GridBagConstraintsBuilder.Anchor anchor, + GridBagConstraintsBuilder.Fill fill + ) { + var panel = new JPanel(new GridBagLayout()); + GridBagConstraints constraints = new GridBagConstraintsBuilder() + .weightX(1) + .weightY(1) + .anchor(anchor) + .fill(fill) + .build(); + + panel.add(container, constraints); + return panel; + } +} diff --git a/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/data/EmbeddedInfo.java b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/data/EmbeddedInfo.java new file mode 100644 index 00000000..3f81544c --- /dev/null +++ b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/data/EmbeddedInfo.java @@ -0,0 +1,12 @@ +package info.ata4.bspsrc.app.info.gui.data; + +import static java.util.Objects.requireNonNull; + +public record EmbeddedInfo( + String name, + long size +) { + public EmbeddedInfo { + requireNonNull(name); + } +} diff --git a/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/data/GameLumpInfo.java b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/data/GameLumpInfo.java new file mode 100644 index 00000000..061880ef --- /dev/null +++ b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/data/GameLumpInfo.java @@ -0,0 +1,14 @@ +package info.ata4.bspsrc.app.info.gui.data; + +import static java.util.Objects.requireNonNull; + +public record GameLumpInfo( + String name, + int size, + int sizePercentage, + int version +) { + public GameLumpInfo { + requireNonNull(name); + } +} diff --git a/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/data/LumpInfo.java b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/data/LumpInfo.java new file mode 100644 index 00000000..57755d7e --- /dev/null +++ b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/data/LumpInfo.java @@ -0,0 +1,15 @@ +package info.ata4.bspsrc.app.info.gui.data; + +import static java.util.Objects.requireNonNull; + +public record LumpInfo( + int id, + String name, + int size, + int sizePercentage, + int version +) { + public LumpInfo { + requireNonNull(name); + } +} diff --git a/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/models/BspInfoModel.java b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/models/BspInfoModel.java new file mode 100644 index 00000000..f10793ea --- /dev/null +++ b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/models/BspInfoModel.java @@ -0,0 +1,180 @@ +package info.ata4.bspsrc.app.info.gui.models; + +import info.ata4.bspsrc.app.info.BspFileUtils; +import info.ata4.bspsrc.app.info.gui.data.EmbeddedInfo; +import info.ata4.bspsrc.app.info.gui.data.GameLumpInfo; +import info.ata4.bspsrc.app.info.gui.data.LumpInfo; +import info.ata4.bspsrc.decompiler.modules.BspChecksum; +import info.ata4.bspsrc.decompiler.modules.BspCompileParams; +import info.ata4.bspsrc.decompiler.modules.BspDependencies; +import info.ata4.bspsrc.decompiler.modules.BspProtection; +import info.ata4.bspsrc.decompiler.modules.texture.TextureSource; +import info.ata4.bspsrc.lib.BspFile; +import info.ata4.bspsrc.lib.BspFileReader; +import info.ata4.bspsrc.lib.lump.AbstractLump; +import info.ata4.bspsrc.lib.struct.BspData; +import info.ata4.log.LogUtils; +import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; +import org.apache.commons.compress.archivers.zip.ZipFile; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.*; +import java.util.logging.Level; +import java.util.logging.Logger; + +public class BspInfoModel { + + private static final Logger L = LogUtils.getLogger(); + + private final List listeners = new ArrayList<>(); + + private BspFile bspFile; + private BspData bspData; + private BspCompileParams cparams; + private BspProtection prot; + private BspDependencies bspres; + + private Long fileCrc; + private Long mapCrc; + + private List lumps = List.of(); + private List gameLumps = List.of(); + private List embeddedInfos = List.of(); + + public void load(Path filePath) throws IOException { + bspFile = new BspFile(); + bspFile.load(filePath); + + int lumpSizeSum = bspFile.getLumps().stream() + .mapToInt(AbstractLump::getLength) + .sum(); + + lumps = bspFile.getLumps().stream() + .map(lump -> new LumpInfo( + lump.getIndex(), + lump.getName(), + lump.getLength(), + (int) Math.round(lump.getLength() * 100.0 / lumpSizeSum), + lump.getVersion() + )) + .toList(); + + int gameLumpSizeSum = bspFile.getGameLumps().stream() + .mapToInt(AbstractLump::getLength) + .sum(); + + gameLumps = bspFile.getGameLumps().stream() + .map(lump -> new GameLumpInfo( + lump.getName(), + lump.getLength(), + (int) Math.round(lump.getLength() * 100.0 / gameLumpSizeSum), + lump.getVersion() + )) + .toList(); + + var bspReader = new BspFileReader(bspFile); + bspReader.loadEntities(); + + bspData = bspReader.getData(); + cparams = new BspCompileParams(bspReader); + + var texsrc = new TextureSource(bspReader); + prot = new BspProtection(bspReader, texsrc); + prot.check(); + + bspres = new BspDependencies(bspReader); + + var checksum = new BspChecksum(bspReader); + fileCrc = checksum.getFileCRC(); + mapCrc = checksum.getMapCRC(); + + try (ZipFile zip = bspFile.getPakFile().getZipFile()) { + var files = new ArrayList(); + + Enumeration enumeration = zip.getEntries(); + while (enumeration.hasMoreElements()) { + ZipArchiveEntry ze = enumeration.nextElement(); + files.add(new EmbeddedInfo(ze.getName(), ze.getSize())); + } + + embeddedInfos = files; + } catch (IOException ex) { + L.log(Level.WARNING, "Can't read pak"); + } + + listeners.forEach(Runnable::run); + } + + public void extractLumps(Set lumpIndices, Path lumpsDst) throws IOException { + for (int lumpIndex : lumpIndices) { + var lump = bspFile.getLumps().get(lumpIndex); + BspFileUtils.extractLump(lump, lumpsDst); + } + } + + public void extractGameLumps(Set lumpIndices, Path lumpsDst) throws IOException { + for (int lumpIndex : lumpIndices) { + var lump = bspFile.getGameLumps().get(lumpIndex); + BspFileUtils.extractGameLump(lump, lumpsDst); + } + } + + public void extractEmbeddedFiles(Set fileIndices, Path filesDst) throws IOException { + + // this is maybe a little bit weird of doing this, but i can't be bothered + // to change the PakFile api + + var fileNames = new ArrayList(); + try (ZipFile zip = bspFile.getPakFile().getZipFile()) { + Enumeration enumeration = zip.getEntries(); + for (int i = 0; enumeration.hasMoreElements(); i++) { + ZipArchiveEntry ze = enumeration.nextElement(); + + if (fileIndices.contains(i)) + fileNames.add(ze.getName()); + } + } + + bspFile.getPakFile().unpack(filesDst, fileNames::contains); + } + + public void extractEmbeddedFilesRaw(Path filesDst) throws IOException { + bspFile.getPakFile().unpack(filesDst, true); + } + + public void addListener(Runnable listener) { + listeners.add(listener); + } + + public Optional getBspFile() { + return Optional.ofNullable(bspFile); + } + public Optional getBspData() { + return Optional.ofNullable(bspData); + } + public Optional getCparams() { + return Optional.ofNullable(cparams); + } + public Optional getProt() { + return Optional.ofNullable(prot); + } + public Optional getBspres() { + return Optional.ofNullable(bspres); + } + public Optional getFileCrc() { + return Optional.ofNullable(fileCrc); + } + public Optional getMapCrc() { + return Optional.ofNullable(mapCrc); + } + public List getLumps() { + return lumps; + } + public List getGameLumps() { + return gameLumps; + } + public List getEmbeddedInfos() { + return embeddedInfos; + } +} diff --git a/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/models/EmbeddedTableModel.java b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/models/EmbeddedTableModel.java deleted file mode 100644 index 41ac740e..00000000 --- a/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/models/EmbeddedTableModel.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - ** 2012 June 7 - ** - ** The author disclaims copyright to this source code. In place of - ** a legal notice, here is a blessing: - ** May you do good and not evil. - ** May you find forgiveness for yourself and forgive others. - ** May you share freely, never taking more than you give. - */ -package info.ata4.bspsrc.app.info.gui.models; - -import info.ata4.bspsrc.app.util.ListTableModel; -import info.ata4.bspsrc.lib.BspFile; -import info.ata4.log.LogUtils; -import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; -import org.apache.commons.compress.archivers.zip.ZipFile; - -import java.io.IOException; -import java.util.Arrays; -import java.util.Enumeration; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * - * @author Nico Bergemann - */ -public class EmbeddedTableModel extends ListTableModel { - - private static final Logger L = LogUtils.getLogger(); - - public EmbeddedTableModel() { - super(3); - columnNames = Arrays.asList(new String[]{"Name", "Size"}); - columnClasses = new Class[] {String.class, Long.class}; - } - - public EmbeddedTableModel(BspFile bspFile) { - this(); - - try (ZipFile zip = bspFile.getPakFile().getZipFile()) { - Enumeration enumeration = zip.getEntries(); - while (enumeration.hasMoreElements()) { - ZipArchiveEntry ze = enumeration.nextElement(); - addRow(Arrays.asList(ze.getName(), ze.getSize())); - } - } catch (IOException ex) { - L.log(Level.WARNING, "Can't read pak"); - } - } - - @Override - public boolean isCellEditable(int row, int column) { - return false; - } -} diff --git a/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/models/EntityTableModel.java b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/models/EntityTableModel.java deleted file mode 100644 index 22e50ae2..00000000 --- a/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/models/EntityTableModel.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - ** 2012 June 2 - ** - ** The author disclaims copyright to this source code. In place of - ** a legal notice, here is a blessing: - ** May you do good and not evil. - ** May you find forgiveness for yourself and forgive others. - ** May you share freely, never taking more than you give. - */ -package info.ata4.bspsrc.app.info.gui.models; - -import info.ata4.bspsrc.app.util.ListTableModel; -import info.ata4.bspsrc.lib.BspFileReader; -import info.ata4.bspsrc.lib.entity.Entity; - -import java.util.Arrays; -import java.util.stream.Collectors; - -/** - * - * @author Nico Bergemann - */ -public class EntityTableModel extends ListTableModel { - - public EntityTableModel() { - super(2); - columnNames = Arrays.asList("Class", "Entities"); - columnClasses = new Class[] {String.class, Integer.class}; - } - - public EntityTableModel(BspFileReader bspReader) { - this(); - - bspReader.getData().entities.stream() - .map(Entity::getClassName) - .collect(Collectors.groupingBy(s -> s, Collectors.counting())) - .forEach((cls, count) -> addRow(Arrays.asList(cls, count))); - } - - @Override - public boolean isCellEditable(int row, int column) { - return false; - } -} diff --git a/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/models/GameLumpTableModel.java b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/models/GameLumpTableModel.java deleted file mode 100644 index d982d2f2..00000000 --- a/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/models/GameLumpTableModel.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - ** 2012 Juli 9 - ** - ** The author disclaims copyright to this source code. In place of - ** a legal notice, here is a blessing: - ** May you do good and not evil. - ** May you find forgiveness for yourself and forgive others. - ** May you share freely, never taking more than you give. - */ -package info.ata4.bspsrc.app.info.gui.models; - -import info.ata4.bspsrc.app.util.ListTableModel; -import info.ata4.bspsrc.lib.BspFile; -import info.ata4.bspsrc.lib.lump.GameLump; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * - * @author Nico Bergemann - */ -public class GameLumpTableModel extends ListTableModel { - - public GameLumpTableModel() { - super(4); - columnNames = Arrays.asList(new String[]{ "Name", "Size", "Size usage", "Version"}); - columnClasses = new Class[] {String.class, Integer.class, Integer.class, Integer.class}; - } - - public GameLumpTableModel(BspFile bspFile) { - this(); - - List lumps = bspFile.getGameLumps(); - - float lumpSize = 0; - - for (GameLump l : lumps) { - lumpSize += l.getLength(); - } - - for (GameLump l : lumps) { - List row = new ArrayList<>(); - row.add(l.getName()); - row.add(l.getLength()); - row.add(Math.round(l.getLength() / lumpSize * 100f)); - row.add(l.getVersion()); - addRow(row); - } - } - - @Override - public boolean isCellEditable(int row, int column) { - return false; - } -} diff --git a/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/models/LumpTableModel.java b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/models/LumpTableModel.java deleted file mode 100644 index d11eb495..00000000 --- a/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/models/LumpTableModel.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - ** 2012 June 2 - ** - ** The author disclaims copyright to this source code. In place of - ** a legal notice, here is a blessing: - ** May you do good and not evil. - ** May you find forgiveness for yourself and forgive others. - ** May you share freely, never taking more than you give. - */ -package info.ata4.bspsrc.app.info.gui.models; - -import info.ata4.bspsrc.app.util.ListTableModel; -import info.ata4.bspsrc.lib.BspFile; -import info.ata4.bspsrc.lib.lump.Lump; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - * Table data model for lumps. - * - * @author Nico Bergemann - */ -public class LumpTableModel extends ListTableModel { - - public LumpTableModel() { - super(5); - columnNames = Arrays.asList(new String[]{"ID", "Name", "Size", "Size usage", "Version"}); - columnClasses = new Class[] {Integer.class, String.class, Integer.class, Integer.class, Integer.class}; - } - - public LumpTableModel(BspFile bspFile) { - this(); - - List lumps = bspFile.getLumps(); - - float lumpSize = 0; - - for (Lump l : lumps) { - lumpSize += l.getLength(); - } - - for (Lump l : lumps) { - List row = new ArrayList<>(); - row.add(l.getIndex()); - row.add(l.getName()); - row.add(l.getLength()); - row.add(Math.round(l.getLength() / lumpSize * 100f)); - row.add(l.getVersion()); - addRow(row); - } - } - - @Override - public boolean isCellEditable(int row, int column) { - return false; - } -} diff --git a/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/panel/DependenciesPanel.java b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/panel/DependenciesPanel.java new file mode 100644 index 00000000..afe1c328 --- /dev/null +++ b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/panel/DependenciesPanel.java @@ -0,0 +1,62 @@ +package info.ata4.bspsrc.app.info.gui.panel; + +import info.ata4.bspsrc.app.info.gui.models.BspInfoModel; +import info.ata4.bspsrc.decompiler.modules.BspDependencies; + +import javax.swing.*; +import java.awt.*; +import java.util.Optional; +import java.util.Set; + +import static javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER; +import static javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED; + +public class DependenciesPanel extends JTabbedPane { + + public final JTextArea txtMaterials = new JTextArea(5, 20); + public final JTextArea txtSounds = new JTextArea(5, 20); + public final JTextArea txtSoundScripts = new JTextArea(5, 20); + public final JTextArea txtSoundscapes = new JTextArea(5, 20); + public final JTextArea txtModels = new JTextArea(5, 20); + public final JTextArea txtParticles = new JTextArea(5, 20); + + public DependenciesPanel() { + var font = new Font("Monospaced", Font.PLAIN, 12); + + txtMaterials.setEditable(false); + txtSounds.setEditable(false); + txtSoundScripts.setEditable(false); + txtSoundscapes.setEditable(false); + txtModels.setEditable(false); + txtParticles.setEditable(false); + + txtMaterials.setFont(font); + txtSounds.setFont(font); + txtSoundScripts.setFont(font); + txtSoundscapes.setFont(font); + txtModels.setFont(font); + txtParticles.setFont(font); + + addTab("Materials", new JScrollPane(txtMaterials, VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_NEVER)); + addTab("Sounds", new JScrollPane(txtSounds, VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_NEVER)); + addTab("Sound scripts", new JScrollPane(txtSoundScripts, VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_NEVER)); + addTab("Soundscapes", new JScrollPane(txtSoundscapes, VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_NEVER)); + addTab("Models", new JScrollPane(txtModels, VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_NEVER)); + addTab("Particles", new JScrollPane(txtParticles, VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_NEVER)); + } + + public void update(BspInfoModel model) { + txtMaterials.setText(optionalSetToString(model.getBspres().map(BspDependencies::getMaterials))); + txtSounds.setText(optionalSetToString(model.getBspres().map(BspDependencies::getSoundFiles))); + txtSoundScripts.setText(optionalSetToString(model.getBspres().map(BspDependencies::getSoundScripts))); + txtSoundscapes.setText(optionalSetToString(model.getBspres().map(BspDependencies::getSoundscapes))); + txtModels.setText(optionalSetToString(model.getBspres().map(BspDependencies::getModels))); + txtParticles.setText(optionalSetToString(model.getBspres().map(BspDependencies::getParticles))); + } + + private static String optionalSetToString(Optional> set) { + return set + .map(strings -> String.join("\n", strings)) + .orElse(""); + } +} diff --git a/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/panel/EmbeddedPanel.java b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/panel/EmbeddedPanel.java new file mode 100644 index 00000000..7c5f6ab2 --- /dev/null +++ b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/panel/EmbeddedPanel.java @@ -0,0 +1,88 @@ +package info.ata4.bspsrc.app.info.gui.panel; + +import info.ata4.bspsrc.app.info.gui.data.EmbeddedInfo; +import info.ata4.bspsrc.app.info.gui.models.BspInfoModel; +import info.ata4.bspsrc.app.util.GuiUtil; +import info.ata4.bspsrc.app.util.ReadonlyListTableModel; +import info.ata4.bspsrc.app.util.components.ByteSizeCellRenderer; + +import javax.swing.*; +import java.awt.*; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import static info.ata4.bspsrc.app.util.GuiUtil.setColumnWidth; +import static javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER; +import static javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED; + +public class EmbeddedPanel extends JPanel { + + public final JTable tblFiles = new JTable(); + public final ReadonlyListTableModel tableModel = new ReadonlyListTableModel<>(List.of( + new ReadonlyListTableModel.Column<>("Name", String.class, EmbeddedInfo::name), + new ReadonlyListTableModel.Column<>("Size", Long.class, EmbeddedInfo::size) + )); + public final JButton btnExtract = new JButton("Extract"); + public final JButton btnExtractAll = new JButton("Extract all"); + public final JButton btnExtractRaw = new JButton("Extract raw Zip file"); + + public EmbeddedPanel( + Consumer> onExtractFiles, + Runnable onExtractRaw + ) { + setLayout(new BorderLayout()); + + tblFiles.setModel(tableModel); + tblFiles.setAutoCreateRowSorter(true); + tblFiles.getColumnModel().getColumn(1).setCellRenderer(new ByteSizeCellRenderer()); + + setColumnWidth(tblFiles, 0, "-".repeat(20), false); + setColumnWidth(tblFiles, 1, 100_000, true); + + tblFiles.setPreferredScrollableViewportSize(new Dimension(tblFiles.getPreferredSize().width, -1)); + + var scrlTable = new JScrollPane(tblFiles, VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_NEVER); + add(scrlTable, BorderLayout.CENTER); + + btnExtract.addActionListener(e -> { + var fileIndices = Arrays.stream(tblFiles.getSelectedRows()) + .map(tblFiles::convertRowIndexToModel) + .boxed() + .collect(Collectors.toSet()); + + if (!fileIndices.isEmpty()) + onExtractFiles.accept(fileIndices); + }); + btnExtractAll.addActionListener(e -> { + var fileIndices = IntStream.range(0, tblFiles.getModel().getRowCount()) + .boxed() + .collect(Collectors.toSet()); + + onExtractFiles.accept(fileIndices); + }); + btnExtractRaw.addActionListener(e -> onExtractRaw.run()); + + var pnlButtons = new JPanel(new FlowLayout(FlowLayout.LEADING)); + pnlButtons.add(btnExtract); + pnlButtons.add(btnExtractAll); + pnlButtons.add(btnExtractRaw); + add(pnlButtons, BorderLayout.SOUTH); + } + + public void update(BspInfoModel model) { + tableModel.setData(model.getEmbeddedInfos()); + + boolean buttonsEnabled = !model.getEmbeddedInfos().isEmpty(); + btnExtract.setEnabled(buttonsEnabled); + btnExtractAll.setEnabled(buttonsEnabled); + btnExtractRaw.setEnabled(buttonsEnabled); + } + + public static void main(String[] args) { + GuiUtil.debugDisplay(() -> new EmbeddedPanel(indices -> {}, () -> {})); + } +} diff --git a/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/panel/EntitiesPanel.java b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/panel/EntitiesPanel.java new file mode 100644 index 00000000..90daa284 --- /dev/null +++ b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/panel/EntitiesPanel.java @@ -0,0 +1,84 @@ +package info.ata4.bspsrc.app.info.gui.panel; + +import info.ata4.bspsrc.app.info.gui.models.BspInfoModel; +import info.ata4.bspsrc.app.util.GuiUtil; +import info.ata4.bspsrc.app.util.ReadonlyListTableModel; +import info.ata4.bspsrc.app.util.ReadonlyListTableModel.Column; +import info.ata4.bspsrc.lib.entity.Entity; + +import javax.swing.*; +import java.awt.*; +import java.util.List; +import java.util.stream.Collectors; + +import static info.ata4.bspsrc.app.info.gui.Util.createDisplayTxtField; +import static info.ata4.bspsrc.app.util.GuiUtil.setColumnWidth; +import static javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER; +import static javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED; + +public class EntitiesPanel extends JPanel { + + public final JTextField txtPoint = createDisplayTxtField(2); + public final JTextField txtBrush = createDisplayTxtField(2); + public final JTextField txtTotal = createDisplayTxtField(2); + public final JTable tblEntities = new JTable(); + public final ReadonlyListTableModel tableModel = new ReadonlyListTableModel<>(List.of( + new Column<>("Class", String.class, EntityInfo::className), + new Column<>("Count", Long.class, EntityInfo::occurrences) + )); + + public EntitiesPanel() { + setLayout(new BorderLayout()); + + var pnlHeader = new JPanel(new FlowLayout(FlowLayout.LEADING)); + pnlHeader.add(new JLabel("Point")); + pnlHeader.add(txtPoint); + pnlHeader.add(new JLabel("Brush")); + pnlHeader.add(txtBrush); + pnlHeader.add(new JLabel("Total")); + pnlHeader.add(txtTotal); + add(pnlHeader, BorderLayout.NORTH); + + tblEntities.setModel(tableModel); + tblEntities.setAutoCreateRowSorter(true); + + setColumnWidth(tblEntities, 0, "-".repeat(20), false); + setColumnWidth(tblEntities, 1, 100, true); + + var scrlTable = new JScrollPane(tblEntities, VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_NEVER); + add(scrlTable, BorderLayout.CENTER); + } + + public void update(BspInfoModel model) { + var bspData = model.getBspData().orElse(null); + if (bspData != null) { + long brushEntsCount = bspData.entities.stream() + .filter(entity -> entity.getModelNum() > 0) + .count(); + long pointEntsCount = bspData.entities.size() - brushEntsCount; + + txtPoint.setText("%,d".formatted(pointEntsCount)); + txtBrush.setText("%,d".formatted(brushEntsCount)); + txtTotal.setText("%,d".formatted(bspData.entities.size())); + + tableModel.setData(bspData.entities.stream() + .collect(Collectors.groupingBy(Entity::getClassName, Collectors.counting())) + .entrySet() + .stream() + .map(entry -> new EntityInfo(entry.getKey(), entry.getValue())) + .toList()); + } else { + txtPoint.setText(""); + txtBrush.setText(""); + txtTotal.setText(""); + + tableModel.setData(List.of()); + } + } + + public record EntityInfo(String className, long occurrences) {} + + public static void main(String[] args) { + GuiUtil.debugDisplay(() -> new EntitiesPanel()); + } +} diff --git a/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/panel/GameLumpsPanel.java b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/panel/GameLumpsPanel.java new file mode 100644 index 00000000..e67ecf22 --- /dev/null +++ b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/panel/GameLumpsPanel.java @@ -0,0 +1,88 @@ +package info.ata4.bspsrc.app.info.gui.panel; + +import info.ata4.bspsrc.app.info.gui.data.GameLumpInfo; +import info.ata4.bspsrc.app.info.gui.models.BspInfoModel; +import info.ata4.bspsrc.app.util.GuiUtil; +import info.ata4.bspsrc.app.util.ReadonlyListTableModel; +import info.ata4.bspsrc.app.util.ReadonlyListTableModel.Column; +import info.ata4.bspsrc.app.util.components.ByteSizeCellRenderer; +import info.ata4.bspsrc.app.util.components.ProgressCellRenderer; + +import javax.swing.*; +import java.awt.*; +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import static info.ata4.bspsrc.app.util.GuiUtil.setColumnWidth; +import static javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER; +import static javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED; + +public class GameLumpsPanel extends JPanel { + + public final JTable tblLumps = new JTable(); + public final ReadonlyListTableModel tableModel = new ReadonlyListTableModel<>(List.of( + new Column<>("Name", String.class, GameLumpInfo::name), + new Column<>("Size", Integer.class, GameLumpInfo::size), + new Column<>("Size usage", Integer.class, GameLumpInfo::sizePercentage), + new Column<>("Version", Integer.class, GameLumpInfo::version) + )); + public final JButton btnExtract = new JButton("Extract"); + public final JButton btnExtractAll = new JButton("Extract all"); + + public GameLumpsPanel( + Consumer> onExtractLumps + ) { + setLayout(new BorderLayout()); + + tblLumps.setModel(tableModel); + tblLumps.setAutoCreateRowSorter(true); + tblLumps.getColumnModel().getColumn(1).setCellRenderer(new ByteSizeCellRenderer()); + tblLumps.getColumnModel().getColumn(2).setCellRenderer(new ProgressCellRenderer()); + + setColumnWidth(tblLumps, 0, "----", false); + setColumnWidth(tblLumps, 1, 100_100, true); + setColumnWidth(tblLumps, 2, 100, true); + setColumnWidth(tblLumps, 3, 10, true); + + var scrlTable = new JScrollPane(tblLumps, VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_NEVER); + add(scrlTable, BorderLayout.CENTER); + + btnExtract.addActionListener(e -> { + var lumpIndices = Arrays.stream(tblLumps.getSelectedRows()) + .map(tblLumps::convertRowIndexToModel) + .boxed() + .collect(Collectors.toSet()); + + if (!lumpIndices.isEmpty()) + onExtractLumps.accept(lumpIndices); + }); + btnExtractAll.addActionListener(e -> { + var lumpIndices = IntStream.range(0, tblLumps.getModel().getRowCount()) + .boxed() + .collect(Collectors.toSet()); + + onExtractLumps.accept(lumpIndices); + }); + + var pnlButtons = new JPanel(new FlowLayout(FlowLayout.LEADING)); + pnlButtons.add(btnExtract); + pnlButtons.add(btnExtractAll); + add(pnlButtons, BorderLayout.SOUTH); + } + + public void update(BspInfoModel model) { + tableModel.setData(model.getGameLumps()); + + boolean buttonsEnabled = !model.getGameLumps().isEmpty(); + btnExtract.setEnabled(buttonsEnabled); + btnExtractAll.setEnabled(buttonsEnabled); + } + + public static void main(String[] args) { + GuiUtil.debugDisplay(() -> new LumpsPanel(indices -> {})); + } +} diff --git a/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/panel/GeneralPanel.java b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/panel/GeneralPanel.java new file mode 100644 index 00000000..89974698 --- /dev/null +++ b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/panel/GeneralPanel.java @@ -0,0 +1,198 @@ +package info.ata4.bspsrc.app.info.gui.panel; + +import info.ata4.bspsrc.app.info.gui.models.BspInfoModel; +import info.ata4.bspsrc.app.util.GridBagConstraintsBuilder; +import info.ata4.bspsrc.app.util.GuiUtil; +import info.ata4.bspsrc.app.util.components.URILabel; +import info.ata4.bspsrc.decompiler.modules.BspCompileParams; +import info.ata4.bspsrc.lib.BspFile; +import info.ata4.bspsrc.lib.app.SourceAppDB; + +import javax.swing.*; +import java.awt.*; +import java.net.URI; +import java.nio.ByteOrder; + +import static info.ata4.bspsrc.app.info.gui.Util.*; +import static info.ata4.bspsrc.app.util.GridBagConstraintsBuilder.Anchor.LINE_END; +import static info.ata4.bspsrc.app.util.GridBagConstraintsBuilder.Anchor.LINE_START; +import static info.ata4.bspsrc.app.util.GridBagConstraintsBuilder.Fill.BOTH; +import static info.ata4.bspsrc.app.util.GridBagConstraintsBuilder.Fill.HORIZONTAL; + +public class GeneralPanel extends JPanel { + + // Headers + public final JTextField txtName = createDisplayTxtField(0); + public final JTextField txtVersion = createDisplayTxtField(3); + public final JTextField txtRevision = createDisplayTxtField(4); + public final JTextField txtCompressed = createDisplayTxtField(5); + public final JTextField txtEndianness = createDisplayTxtField(13); + public final JTextField txtComment = createDisplayTxtField(0); + + // Game + public final JTextField txtGameName = createDisplayTxtField(0); + public final JTextField txtAppId = createDisplayTxtField(8); + public final URILabel lblSteamLink = new URILabel(); + + // Checksums + public final JTextField txtFileCrc = createDisplayTxtField(8); + public final JTextField txtMapCrc = createDisplayTxtField(8); + + // Compiler Parameters + public final JTextField txtVbsp = createDisplayTxtField(0); + public final JTextField txtVvis = createDisplayTxtField(0); + public final JTextField txtVrad = createDisplayTxtField(0); + + public GeneralPanel() { + setLayout(new GridBagLayout()); + + var builder = new GridBagConstraintsBuilder() + .insets(PANEL_COMPONENT_INSETS) + .weightX(1); + + add(createHeadersPanel(), builder.position(0, 0).fill(BOTH).build()); + add(createGamePanel(), builder.position(0, 1).fill(BOTH).build()); + add(createChecksumsPanel(), builder.position(0, 2).fill(BOTH).build()); + add(createCompilerParams(), builder.position(0, 3).fill(BOTH).build()); + } + + private JPanel createHeadersPanel() { + var panel = new JPanel(); + panel.setBorder(createPanelBorder("Headers")); + panel.setLayout(new GridBagLayout()); + + var builder = new GridBagConstraintsBuilder() + .insets(PANEL_COMPONENT_INSETS); + + panel.add(new JLabel("Name"), builder.position(0, 0).anchor(LINE_END).build()); + panel.add(txtName, builder.position(1, 0).anchor(LINE_START).width(4).weightX(1).fill(BOTH).build()); + + panel.add(new JLabel("Version"), builder.position(0, 1).anchor(LINE_END).build()); + panel.add(txtVersion, builder.position(1, 1).anchor(LINE_START).fill(BOTH).build()); + panel.add(new JLabel("Revision"), builder.position(2, 1).anchor(LINE_END).build()); + panel.add(txtRevision, builder.position(3, 1).anchor(LINE_START).fill(BOTH).build()); + panel.add(Box.createGlue(), builder.position(4, 1).weightX(1).build()); + + panel.add(new JLabel("Compressed"), builder.position(0, 2).anchor(LINE_END).build()); + panel.add(txtCompressed, builder.position(1, 2).anchor(LINE_START).fill(BOTH).build()); + panel.add(new JLabel("Endianness"), builder.position(2, 2).anchor(LINE_END).build()); + panel.add(txtEndianness, builder.position(3, 2).anchor(LINE_START).fill(BOTH).build()); + panel.add(Box.createGlue(), builder.position(4, 2).weightX(1).build()); + + panel.add(new JLabel("Comment"), builder.position(0, 3).anchor(LINE_END).build()); + panel.add(txtComment, builder.position(1, 3).anchor(LINE_START).width(4).weightX(1).fill(BOTH).build()); + return panel; + } + + private JPanel createGamePanel() { + var panel = new JPanel(); + panel.setBorder(createPanelBorder("Game")); + panel.setLayout(new GridBagLayout()); + + var builder = new GridBagConstraintsBuilder() + .insets(PANEL_COMPONENT_INSETS); + + panel.add(new JLabel("Name"), builder.position(0, 0).anchor(LINE_END).build()); + panel.add(txtGameName, builder.position(1, 0).width(2).anchor(LINE_START).weightX(1).fill(HORIZONTAL).build()); + panel.add(new JLabel("App-ID"), builder.position(0, 1).anchor(LINE_END).build()); + panel.add(txtAppId, builder.position(1, 1).fill(HORIZONTAL).build()); + panel.add(lblSteamLink, builder.position(2, 1).anchor(LINE_START).build()); + return panel; + } + + private JPanel createChecksumsPanel() { + var panel = new JPanel(); + panel.setBorder(createPanelBorder("Checksums")); + panel.setLayout(new GridBagLayout()); + + var builder = new GridBagConstraintsBuilder() + .insets(PANEL_COMPONENT_INSETS); + + panel.add(new JLabel("File CRC"), builder.position(0, 0).anchor(LINE_END).build()); + panel.add(txtFileCrc, builder.position(1, 0).anchor(LINE_START).build()); + panel.add(new JLabel("Map CRC"), builder.position(2, 0).anchor(LINE_END).build()); + panel.add(txtMapCrc, builder.position(3, 0).anchor(LINE_START).build()); + panel.add(Box.createGlue(), builder.position(4, 0).weightX(1).build()); + return panel; + } + + private JPanel createCompilerParams() { + var panel = new JPanel(); + panel.setBorder(createPanelBorder("Detected compile parameters")); + panel.setLayout(new GridBagLayout()); + + var builder = new GridBagConstraintsBuilder() + .insets(PANEL_COMPONENT_INSETS); + + panel.add(new JLabel("vbsp"), builder.position(0, 0).anchor(LINE_END).build()); + panel.add(txtVbsp, builder.position(1, 0).anchor(LINE_START).weightX(1).fill(HORIZONTAL).build()); + panel.add(new JLabel("vvis"), builder.position(0, 1).anchor(LINE_END).build()); + panel.add(txtVvis, builder.position(1, 1).anchor(LINE_START).weightX(1).fill(HORIZONTAL).build()); + panel.add(new JLabel("vrad"), builder.position(0, 2).anchor(LINE_END).build()); + panel.add(txtVrad, builder.position(1, 2).anchor(LINE_START).weightX(1).fill(HORIZONTAL).build()); + return panel; + } + + public void update(BspInfoModel model) { + updateGeneral(model); + updateGame(model); + updateChecksums(model); + updateCompileParameters(model); + } + + private void updateGeneral(BspInfoModel model) { + String comment = model.getBspData() + .flatMap(bspData -> bspData.entities.stream().findFirst()) + .map(entity -> entity.getValue("comment")) + .orElse(""); + + txtName.setText(model.getBspFile().map(BspFile::getName).orElse("")); + txtVersion.setText(model.getBspFile().map(BspFile::getVersion).map(Object::toString).orElse("")); + txtRevision.setText(model.getBspFile().map(BspFile::getRevision).map(Object::toString).orElse("")); + txtCompressed.setText(model.getBspFile().map(BspFile::isCompressed).map(bool -> bool ? "Yes" : "No").orElse("")); + txtEndianness.setText(model.getBspFile().map(bspFile -> bspFile.getByteOrder() == ByteOrder.LITTLE_ENDIAN ? "Little endian" : "Big endian").orElse(null)); + txtComment.setText(comment); + } + + private void updateGame(BspInfoModel model) { + String gameName = model.getBspFile() + .map(bspFile -> SourceAppDB.getInstance().getName(bspFile.getAppId()).orElse("Unknown")) + .orElse(""); + + txtGameName.setText(gameName); + txtAppId.setText(model.getBspFile().map(BspFile::getAppId).map(Object::toString).orElse("")); + + model.getBspFile().ifPresentOrElse( + bspFile -> lblSteamLink.setURI("Steam store link", SourceAppDB.getSteamStoreURI(bspFile.getAppId())), + () -> lblSteamLink.setURI("", (URI) null) + ); + } + + private void updateChecksums(BspInfoModel model) { + txtFileCrc.setText(model.getFileCrc().map(checksum -> String.format("%x", checksum)).orElse("")); + txtMapCrc.setText(model.getMapCrc().map(checksum -> String.format("%x", checksum)).orElse("")); + } + + private void updateCompileParameters(BspInfoModel model) { + String vbsp = model.getCparams() + .map(BspCompileParams::getVbspParams) + .map(strings -> String.join(" ", strings)) + .orElse(""); + + String vvis = model.getCparams() + .map(params -> params.isVvisRun() ? String.join(" ", params.getVvisParams()) : "") + .orElse(""); + + String vrad = model.getCparams() + .map(params -> params.isVradRun() ? String.join(" ", params.getVradParams()) : "") + .orElse(""); + + txtVbsp.setText(vbsp); + txtVvis.setText(vvis); + txtVrad.setText(vrad); + } + + public static void main(String[] args) { + GuiUtil.debugDisplay(() -> new GeneralPanel()); + } +} diff --git a/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/panel/LumpsPanel.java b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/panel/LumpsPanel.java new file mode 100644 index 00000000..38bedcec --- /dev/null +++ b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/panel/LumpsPanel.java @@ -0,0 +1,99 @@ +package info.ata4.bspsrc.app.info.gui.panel; + +import info.ata4.bspsrc.app.info.gui.data.LumpInfo; +import info.ata4.bspsrc.app.info.gui.models.BspInfoModel; +import info.ata4.bspsrc.app.util.GuiUtil; +import info.ata4.bspsrc.app.util.ReadonlyListTableModel; +import info.ata4.bspsrc.app.util.components.ByteSizeCellRenderer; +import info.ata4.bspsrc.app.util.components.ProgressCellRenderer; +import info.ata4.bspsrc.lib.lump.LumpType; + +import javax.swing.*; +import java.awt.*; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.Set; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import static info.ata4.bspsrc.app.util.GuiUtil.setColumnWidth; +import static javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER; +import static javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED; + +public class LumpsPanel extends JPanel { + + private final JTable tblLumps = new JTable(); + private final ReadonlyListTableModel tableModel = new ReadonlyListTableModel<>(List.of( + new ReadonlyListTableModel.Column<>("ID", Integer.class, LumpInfo::id), + new ReadonlyListTableModel.Column<>("Name", String.class, LumpInfo::name), + new ReadonlyListTableModel.Column<>("Size", Integer.class, LumpInfo::size), + new ReadonlyListTableModel.Column<>("Size usage", Integer.class, LumpInfo::sizePercentage), + new ReadonlyListTableModel.Column<>("Version", Integer.class, LumpInfo::version) + )); + private final JButton btnExtract = new JButton("Extract"); + private final JButton btnExtractAll = new JButton("Extract all"); + + public LumpsPanel( + Consumer> onExtractLumps + ) { + setLayout(new BorderLayout()); + + tblLumps.setModel(tableModel); + tblLumps.setAutoCreateRowSorter(true); + tblLumps.getColumnModel().getColumn(2).setCellRenderer(new ByteSizeCellRenderer()); + tblLumps.getColumnModel().getColumn(3).setCellRenderer(new ProgressCellRenderer()); + + setColumnWidth(tblLumps, 0, Arrays.stream(LumpType.values()) + .map(LumpType::getIndex) + .max(Integer::compareTo) + .orElseThrow(), true); + setColumnWidth(tblLumps, 1, Arrays.stream(LumpType.values()) + .map(Enum::name) + .max(Comparator.comparingInt(String::length)) + .orElseThrow(), false); + setColumnWidth(tblLumps, 2, 100_100, true); + setColumnWidth(tblLumps, 3, 100, true); + setColumnWidth(tblLumps, 4, 10, true); + + tblLumps.setPreferredScrollableViewportSize(new Dimension(tblLumps.getPreferredSize().width, -1)); + + var scrlTable = new JScrollPane(tblLumps, VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_NEVER); + add(scrlTable, BorderLayout.CENTER); + + btnExtract.addActionListener(e -> { + var lumpIndices = Arrays.stream(tblLumps.getSelectedRows()) + .map(tblLumps::convertRowIndexToModel) + .boxed() + .collect(Collectors.toSet()); + + if (!lumpIndices.isEmpty()) + onExtractLumps.accept(lumpIndices); + }); + btnExtractAll.addActionListener(e -> { + var lumpIndices = IntStream.range(0, tblLumps.getModel().getRowCount()) + .boxed() + .collect(Collectors.toSet()); + + onExtractLumps.accept(lumpIndices); + }); + + var pnlButtons = new JPanel(new FlowLayout(FlowLayout.LEADING)); + pnlButtons.add(btnExtract); + pnlButtons.add(btnExtractAll); + add(pnlButtons, BorderLayout.SOUTH); + } + + public void update(BspInfoModel model) { + tableModel.setData(model.getLumps()); + + boolean buttonsEnabled = !model.getLumps().isEmpty(); + btnExtract.setEnabled(buttonsEnabled); + btnExtractAll.setEnabled(buttonsEnabled); + } + + public static void main(String[] args) { + GuiUtil.debugDisplay(() -> new LumpsPanel(indicies -> {})); + } +} diff --git a/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/panel/ProtectionPanel.java b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/panel/ProtectionPanel.java new file mode 100644 index 00000000..afa2338e --- /dev/null +++ b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/info/gui/panel/ProtectionPanel.java @@ -0,0 +1,115 @@ +package info.ata4.bspsrc.app.info.gui.panel; + +import info.ata4.bspsrc.app.info.gui.models.BspInfoModel; +import info.ata4.bspsrc.app.util.GridBagConstraintsBuilder; +import info.ata4.bspsrc.app.util.GuiUtil; +import info.ata4.bspsrc.decompiler.modules.BspProtection; + +import javax.swing.*; +import java.awt.*; + +import static info.ata4.bspsrc.app.info.gui.Util.*; +import static info.ata4.bspsrc.app.util.GridBagConstraintsBuilder.Anchor.LINE_END; +import static info.ata4.bspsrc.app.util.GridBagConstraintsBuilder.Fill.BOTH; + +public class ProtectionPanel extends JPanel { + + public final JCheckBox chkEntityFlag = createDisplayCheckbox(); + public final JCheckBox chkTextureFlag = createDisplayCheckbox(); + public final JCheckBox chkProtectorBrush = createDisplayCheckbox(); + public final JCheckBox chkEntityObfuscation = createDisplayCheckbox(); + public final JCheckBox chkNodrawTextureHack = createDisplayCheckbox(); + public final JCheckBox chkBspProtect = createDisplayCheckbox(); + + public ProtectionPanel() { + setLayout(new GridBagLayout()); + + var builder = new GridBagConstraintsBuilder() + .insets(PANEL_COMPONENT_INSETS) + .weightX(1) + .fill(BOTH); + + add(createVmexPanel(), builder.position(0, 0).build()); + add(createIidPanel(), builder.position(0, 1).build()); + add(createOtherPanel(), builder.position(0, 2).build()); + } + + private JPanel createVmexPanel() { + var panel = new JPanel(); + panel.setBorder(createPanelBorder("VMEX")); + panel.setLayout(new GridBagLayout()); + + var builder = new GridBagConstraintsBuilder() + .insets(PANEL_COMPONENT_INSETS); + + panel.add(Box.createGlue(), builder.position(0, 0).weightX(1).build()); + panel.add(new JLabel("Entity flag"), builder.position(1, 0).anchor(LINE_END).build()); + panel.add(chkEntityFlag, builder.position(2, 0).build()); + panel.add(new JLabel("Texture flag"), builder.position(1, 1).anchor(LINE_END).build()); + panel.add(chkTextureFlag, builder.position(2, 1).build()); + panel.add(new JLabel("Protector brush"), builder.position(1, 2).anchor(LINE_END).build()); + panel.add(chkProtectorBrush, builder.position(2, 2).build()); + + return panel; + } + + private JPanel createIidPanel() { + var panel = new JPanel(); + panel.setBorder(createPanelBorder("IID")); + panel.setLayout(new GridBagLayout()); + + var builder = new GridBagConstraintsBuilder() + .insets(PANEL_COMPONENT_INSETS) + .anchor(LINE_END); + + panel.add(Box.createGlue(), builder.position(0, 0).weightX(1).build()); + panel.add(new JLabel("Entity obfuscation"), builder.position(1, 0).anchor(LINE_END).build()); + panel.add(chkEntityObfuscation, builder.position(2, 0).build()); + panel.add(new JLabel("Nodraw texture hack"), builder.position(1, 1).anchor(LINE_END).build()); + panel.add(chkNodrawTextureHack, builder.position(2, 1).build()); + + return panel; + } + + private JPanel createOtherPanel() { + var panel = new JPanel(); + panel.setBorder(createPanelBorder("Other")); + panel.setLayout(new GridBagLayout()); + + var builder = new GridBagConstraintsBuilder() + .insets(PANEL_COMPONENT_INSETS) + .anchor(LINE_END); + + panel.add(Box.createGlue(), builder.position(0, 0).weightX(1).build()); + panel.add(new JLabel("BSPProtect"), builder.position(1, 0).anchor(LINE_END).build()); + panel.add(chkBspProtect, builder.position(2, 0).build()); + + return panel; + } + + public void update(BspInfoModel model) { + updateVmex(model); + updateIid(model); + updateOther(model); + } + + private void updateVmex(BspInfoModel model) { + chkEntityFlag.setSelected(model.getProt().map(BspProtection::hasEntityFlag).orElse(false)); + chkTextureFlag.setSelected(model.getProt().map(BspProtection::hasTextureFlag).orElse(false)); + chkProtectorBrush.setSelected(model.getProt().map(BspProtection::hasBrushFlag).orElse(false)); + } + + private void updateIid(BspInfoModel model) { + chkEntityObfuscation.setSelected(model.getProt().map(BspProtection::hasObfuscatedEntities).orElse(false)); + chkNodrawTextureHack.setSelected(model.getProt().map(BspProtection::hasModifiedTexinfo).orElse(false)); + } + + private void updateOther(BspInfoModel model) { + chkBspProtect.setSelected(model.getProt().map(BspProtection::hasEncryptedEntities).orElse(false)); + } + + public static void main(String[] args) { + GuiUtil.debugDisplay(() -> new ProtectionPanel()); + } +} + diff --git a/bspsrc-app/src/main/java/info/ata4/bspsrc/app/util/GridBagConstraintsBuilder.java b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/util/GridBagConstraintsBuilder.java new file mode 100644 index 00000000..7f362608 --- /dev/null +++ b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/util/GridBagConstraintsBuilder.java @@ -0,0 +1,123 @@ +package info.ata4.bspsrc.app.util; + +import java.awt.*; +import java.util.function.Consumer; + +public class GridBagConstraintsBuilder { + private final GridBagConstraints constraints; + + public GridBagConstraintsBuilder() { + this(new GridBagConstraints()); + } + + private GridBagConstraintsBuilder(GridBagConstraints constraints) { + this.constraints = constraints; + } + + public GridBagConstraintsBuilder position(int x, int y) { + return inferBuilder(constraints -> { + constraints.gridx = x; + constraints.gridy = y; + }); + } + + public GridBagConstraintsBuilder anchor(Anchor anchor) { + return inferBuilder(constraints -> { + constraints.anchor = anchor.id; + }); + } + + public GridBagConstraintsBuilder width(int width) { + return inferBuilder(constraints -> { + constraints.gridwidth = width; + }); + } + public GridBagConstraintsBuilder height(int height) { + return inferBuilder(constraints -> { + constraints.gridheight = height; + }); + } + + public GridBagConstraintsBuilder weightX(int weight) { + return inferBuilder(constraints -> { + constraints.weightx = weight; + }); + } + public GridBagConstraintsBuilder weightY(int weight) { + return inferBuilder(constraints -> { + constraints.weighty = weight; + }); + } + + public GridBagConstraintsBuilder fill(Fill fill) { + return inferBuilder(constraints -> { + constraints.fill = fill.id; + }); + } + + public GridBagConstraintsBuilder insets(Insets insets) { + return inferBuilder(constraints -> { + constraints.insets = insets; + }); + } + + public GridBagConstraints build() { + return constraints; + } + + private GridBagConstraintsBuilder inferBuilder(Consumer action) { + var constraints = (GridBagConstraints) this.constraints.clone(); + action.accept(constraints); + return new GridBagConstraintsBuilder(constraints); + } + + public enum Anchor { + CENTER(GridBagConstraints.CENTER), + NORTH(GridBagConstraints.NORTH), + NORTHEAST(GridBagConstraints.NORTHEAST), + EAST(GridBagConstraints.EAST), + SOUTHEAST(GridBagConstraints.SOUTHEAST), + SOUTH(GridBagConstraints.SOUTH), + SOUTHWEST(GridBagConstraints.SOUTHWEST), + WEST(GridBagConstraints.WEST), + NORTHWEST(GridBagConstraints.NORTHWEST), + + PAGE_START(GridBagConstraints.PAGE_START), + PAGE_END(GridBagConstraints.PAGE_END), + LINE_START(GridBagConstraints.LINE_START), + LINE_END(GridBagConstraints.LINE_END), + FIRST_LINE_START(GridBagConstraints.FIRST_LINE_START), + FIRST_LINE_END(GridBagConstraints.FIRST_LINE_END), + LAST_LINE_START(GridBagConstraints.LAST_LINE_START), + LAST_LINE_END(GridBagConstraints.LAST_LINE_END), + + BASELINE(GridBagConstraints.BASELINE), + BASELINE_LEADING(GridBagConstraints.BASELINE_LEADING), + BASELINE_TRAILING(GridBagConstraints.BASELINE_TRAILING), + ABOVE_BASELINE(GridBagConstraints.ABOVE_BASELINE), + ABOVE_BASELINE_LEADING(GridBagConstraints.ABOVE_BASELINE_LEADING), + ABOVE_BASELINE_TRAILING(GridBagConstraints.ABOVE_BASELINE_TRAILING), + BELOW_BASELINE(GridBagConstraints.BELOW_BASELINE), + BELOW_BASELINE_LEADING(GridBagConstraints.BELOW_BASELINE_LEADING), + BELOW_BASELINE_TRAILING(GridBagConstraints.BELOW_BASELINE_TRAILING); + + public final int id; + + Anchor(int id) { + this.id = id; + } + } + + public enum Fill { + NONE(GridBagConstraints.NONE), + HORIZONTAL(GridBagConstraints.HORIZONTAL), + VERTICAL(GridBagConstraints.VERTICAL), + BOTH(GridBagConstraints.BOTH); + + public final int id; + + Fill(int id) { + this.id = id; + } + } +} diff --git a/bspsrc-app/src/main/java/info/ata4/bspsrc/app/util/GuiUtil.java b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/util/GuiUtil.java new file mode 100644 index 00000000..da8086cc --- /dev/null +++ b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/util/GuiUtil.java @@ -0,0 +1,106 @@ +package info.ata4.bspsrc.app.util; + +import com.formdev.flatlaf.FlatDarkLaf; +import com.formdev.flatlaf.FlatLaf; +import com.formdev.flatlaf.FlatLightLaf; +import com.jthemedetecor.OsThemeDetector; + +import javax.swing.*; +import javax.swing.table.TableColumn; +import java.awt.*; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Supplier; + +public class GuiUtil { + + public static void debugDisplay(Supplier containerSupplier) { + GuiUtil.setupFlatlaf(); + + SwingUtilities.invokeLater(() -> { + var frame = new JFrame(); + frame.setContentPane(containerSupplier.get()); + frame.pack(); + frame.setMinimumSize(frame.getSize()); + frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); + frame.setVisible(true); + }); + } + + public static void setupFlatlaf() { + Consumer changeTheme = isDark -> SwingUtilities.invokeLater(() -> { + if (isDark) + FlatDarkLaf.setup(); + else + FlatLightLaf.setup(); + + FlatLaf.updateUI(); + }); + + var detector = OsThemeDetector.getDetector(); + detector.registerListener(changeTheme); + changeTheme.accept(detector.isDark()); + } + + public static void setColumnWidth(JTable table, int columnIndex, Object value, boolean alsoSetMax) { + TableColumn column = table.getColumnModel().getColumn(columnIndex); + + var headerRenderer = column.getHeaderRenderer(); + if (headerRenderer == null) + headerRenderer = table.getTableHeader().getDefaultRenderer(); + + var headerCell = headerRenderer.getTableCellRendererComponent( + table, + column.getHeaderValue(), + false, + false, + -1, + columnIndex + ); + int headerCellWidth = headerCell.getPreferredSize().width; + + var rowSorter = table.getRowSorter(); + if (rowSorter != null) { + var sortKeys = rowSorter.getSortKeys(); + + for (SortOrder sortOrder : SortOrder.values()) + { + rowSorter.setSortKeys(List.of(new RowSorter.SortKey(columnIndex, sortOrder))); + + headerCell = headerRenderer.getTableCellRendererComponent( + table, + column.getHeaderValue(), + false, + false, + -1, + columnIndex + ); + headerCellWidth = Math.max(headerCellWidth, headerCell.getPreferredSize().width); + } + + rowSorter.setSortKeys(sortKeys); + } + + var cell = table.getCellRenderer(-1, columnIndex) + .getTableCellRendererComponent( + table, + value, + false, + false, + -1, + columnIndex + ); + + + + int width = Math.max( + cell.getPreferredSize().width, + headerCellWidth + ); + + column.setPreferredWidth(width); + if (alsoSetMax) { + column.setMaxWidth(width); + } + } +} diff --git a/bspsrc-app/src/main/java/info/ata4/bspsrc/app/util/ReadonlyListTableModel.java b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/util/ReadonlyListTableModel.java new file mode 100644 index 00000000..945165b3 --- /dev/null +++ b/bspsrc-app/src/main/java/info/ata4/bspsrc/app/util/ReadonlyListTableModel.java @@ -0,0 +1,47 @@ +package info.ata4.bspsrc.app.util; + +import javax.swing.table.AbstractTableModel; +import java.util.List; +import java.util.function.Function; + +public class ReadonlyListTableModel extends AbstractTableModel { + + private final List> columns; + private List data = List.of(); + + public ReadonlyListTableModel(List> columns) { + this.columns = List.copyOf(columns); + } + + public void setData(List data) { + this.data = List.copyOf(data); + fireTableDataChanged(); + } + + @Override + public String getColumnName(int column) { + return columns.get(column).name(); + } + + @Override + public Class getColumnClass(int columnIndex) { + return columns.get(columnIndex).cls(); + } + + @Override + public int getRowCount() { + return data.size(); + } + + @Override + public int getColumnCount() { + return columns.size(); + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + return columns.get(columnIndex).getter().apply(data.get(rowIndex)); + } + + public record Column(String name, Class cls, Function getter) {} +} diff --git a/bspsrc-app/src/main/java/module-info.java b/bspsrc-app/src/main/java/module-info.java index f699d570..74f761db 100644 --- a/bspsrc-app/src/main/java/module-info.java +++ b/bspsrc-app/src/main/java/module-info.java @@ -9,6 +9,8 @@ requires ioutils.b1f26588b5; requires org.apache.commons.compress; requires info.picocli; + requires com.formdev.flatlaf; + requires com.jthemedetector; opens info.ata4.bspsrc.app.src.cli to info.picocli; } \ No newline at end of file diff --git a/pom.xml b/pom.xml index db042f62..c7cd5bb4 100644 --- a/pom.xml +++ b/pom.xml @@ -38,6 +38,13 @@ + + + org.slf4j + slf4j-jdk14 + 2.0.7 + + org.apache.commons commons-compress @@ -58,6 +65,18 @@ 1.9 + + com.formdev + flatlaf + 3.1 + + + + com.github.Dansoftowner + jSystemThemeDetector + 3.6 + + com.github.ata4