Skip to content

Commit

Permalink
BAM Converter: Add option to disable overwriting existing PVRZ files
Browse files Browse the repository at this point in the history
Originally, existing PVRZ files in the destination folder were overwritten
without further confirmation prompts when exporting PVRZ-based BAM files.
  • Loading branch information
Argent77 committed Nov 7, 2024
1 parent 971be9e commit 888a784
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 40 deletions.
4 changes: 2 additions & 2 deletions src/org/infinity/gui/converter/BamFilterBaseOutput.java
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ public static boolean convertBam(ConvertToBam converter, Path outFileName, Pseud
DxtEncoder.DxtType dxtType = converter.getDxtType();
int pvrzIndex = converter.getPvrzIndex();
try {
return decoder.exportBamV2(outFileName, dxtType, pvrzIndex, converter.getProgressMonitor(),
converter.getProgressMonitorStage());
return decoder.exportBamV2(outFileName, dxtType, pvrzIndex, BamOptionsDialog.getOverwritePvrzIndices(),
converter.getProgressMonitor(), converter.getProgressMonitorStage());
} catch (Exception e) {
Logger.error(e);
throw e;
Expand Down
63 changes: 40 additions & 23 deletions src/org/infinity/gui/converter/BamOptionsDialog.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,34 +63,37 @@ class BamOptionsDialog extends JDialog implements ActionListener, FocusListener,
private static final String PREFS_COMPRESSBAM = "BCCompressBam";
private static final String PREFS_COMPRESSTYPE = "BCCompressionType";
private static final String PREFS_PVRZINDEX = "BCPvrzIndex";
private static final String PREFS_OVERWRITE_PVRZ_INDICES = "BCOverwritePvrzIndices";
private static final String PREFS_RECENT_SESSIONS = "RecentSessions";

// Default settings
private static final int DEFAULT_BAM_VERSION = ConvertToBam.VERSION_BAMV1;
private static final String DEFAULT_PATH = "";
private static final boolean DEFAULT_AUTO_CLEAR = true;
private static final boolean DEFAULT_CLOSE_ON_EXIT = false;
private static final int DEFAULT_TRANSPARENCY_THRESHOLD = 5; // in percent
private static final int DEFAULT_USE_ALPHA = ConvertToBam.ALPHA_AUTO;
private static final String DEFAULT_SORT_PALETTE = ColorConvert.SortType.None.toString();
private static final boolean DEFAULT_COMPRESS_BAM = false;
private static final int DEFAULT_COMPRESSION_TYPE = ConvertToBam.COMPRESSION_AUTO;
private static final int DEFAULT_PVRZ_INDEX = 1000;
private static final int DEFAULT_RECENT_SESSIONS_MAX = 10;
private static final int DEFAULT_BAM_VERSION = ConvertToBam.VERSION_BAMV1;
private static final String DEFAULT_PATH = "";
private static final boolean DEFAULT_AUTO_CLEAR = true;
private static final boolean DEFAULT_CLOSE_ON_EXIT = false;
private static final int DEFAULT_TRANSPARENCY_THRESHOLD = 5; // in percent
private static final int DEFAULT_USE_ALPHA = ConvertToBam.ALPHA_AUTO;
private static final String DEFAULT_SORT_PALETTE = ColorConvert.SortType.None.toString();
private static final boolean DEFAULT_COMPRESS_BAM = false;
private static final int DEFAULT_COMPRESSION_TYPE = ConvertToBam.COMPRESSION_AUTO;
private static final int DEFAULT_PVRZ_INDEX = 1000;
private static final boolean DEFAULT_OVERWRITE_PVRZ_INDICES = false;
private static final int DEFAULT_RECENT_SESSIONS_MAX = 10;

// Current settings
private static final List<Path> recentSessions = new ArrayList<>();
private static boolean settingsLoaded = false;
private static int bamVersion = DEFAULT_BAM_VERSION;
private static String path = DEFAULT_PATH;
private static boolean autoClear = DEFAULT_AUTO_CLEAR;
private static boolean closeOnExit = DEFAULT_CLOSE_ON_EXIT;
private static int transparencyThreshold = DEFAULT_TRANSPARENCY_THRESHOLD;
private static int useAlpha = DEFAULT_USE_ALPHA;
private static String sortPalette = DEFAULT_SORT_PALETTE;
private static boolean compressBam = DEFAULT_COMPRESS_BAM;
private static int compressionType = DEFAULT_COMPRESSION_TYPE;
private static int pvrzIndex = DEFAULT_PVRZ_INDEX;
private static boolean settingsLoaded = false;
private static int bamVersion = DEFAULT_BAM_VERSION;
private static String path = DEFAULT_PATH;
private static boolean autoClear = DEFAULT_AUTO_CLEAR;
private static boolean closeOnExit = DEFAULT_CLOSE_ON_EXIT;
private static int transparencyThreshold = DEFAULT_TRANSPARENCY_THRESHOLD;
private static int useAlpha = DEFAULT_USE_ALPHA;
private static String sortPalette = DEFAULT_SORT_PALETTE;
private static boolean compressBam = DEFAULT_COMPRESS_BAM;
private static int compressionType = DEFAULT_COMPRESSION_TYPE;
private static int pvrzIndex = DEFAULT_PVRZ_INDEX;
private static boolean overwritePvrzIndices = DEFAULT_OVERWRITE_PVRZ_INDICES;

private final ConvertToBam converter;

Expand All @@ -105,6 +108,7 @@ class BamOptionsDialog extends JDialog implements ActionListener, FocusListener,
private JCheckBox cbCloseOnExit;
private JCheckBox cbAutoClear;
private JCheckBox cbCompressBam;
private JCheckBox cbOverwritePvrzIndices;
private JSpinner sTransparency;
private JSpinner sPvrzIndex;
private JTextField tfPath;
Expand All @@ -127,6 +131,7 @@ public static void loadSettings(boolean force) {
compressBam = prefs.getBoolean(PREFS_COMPRESSBAM, DEFAULT_COMPRESS_BAM);
compressionType = prefs.getInt(PREFS_COMPRESSTYPE, DEFAULT_COMPRESSION_TYPE);
pvrzIndex = prefs.getInt(PREFS_PVRZINDEX, DEFAULT_PVRZ_INDEX);
overwritePvrzIndices = prefs.getBoolean(PREFS_OVERWRITE_PVRZ_INDICES, DEFAULT_OVERWRITE_PVRZ_INDICES);
loadRecentSessions(prefs.node(PREFS_RECENT_SESSIONS));

validateSettings();
Expand Down Expand Up @@ -161,6 +166,7 @@ public static void saveSettings() {
prefs.putBoolean(PREFS_COMPRESSBAM, compressBam);
prefs.putInt(PREFS_COMPRESSTYPE, compressionType);
prefs.putInt(PREFS_PVRZINDEX, pvrzIndex);
prefs.putBoolean(PREFS_OVERWRITE_PVRZ_INDICES, overwritePvrzIndices);
}

// Makes sure that all settings are valid.
Expand Down Expand Up @@ -246,6 +252,11 @@ public static int getPvrzIndex() {
return pvrzIndex;
}

/** Returns whether to overwrite PVRZ indices of existing files in the target folder. */
public static boolean getOverwritePvrzIndices() {
return overwritePvrzIndices;
}

/** Returns list of recently accessed session paths. */
public static List<Path> getRecentSessions() {
return recentSessions;
Expand Down Expand Up @@ -481,6 +492,7 @@ private void init() {
cbCompressionType.setSelectedIndex(getCompressionType());
model = new SpinnerNumberModel(getPvrzIndex(), 0, 99999, 1);
sPvrzIndex = new JSpinner(model);
cbOverwritePvrzIndices = new JCheckBox("Overwrite existing PVRZ files", getOverwritePvrzIndices());
JPanel pBamV2 = new JPanel(new GridBagLayout());
pBamV2.setBorder(BorderFactory.createTitledBorder("PVRZ-based BAM "));
c = ViewerUtil.setGBC(c, 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE,
Expand All @@ -495,7 +507,10 @@ private void init() {
c = ViewerUtil.setGBC(c, 1, 1, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL,
new Insets(4, 4, 4, 4), 0, 0);
pBamV2.add(sPvrzIndex, c);
c = ViewerUtil.setGBC(c, 0, 2, 2, 1, 1.0, 1.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH,
c = ViewerUtil.setGBC(c, 0, 2, 2, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE,
new Insets(4, 4, 0, 0), 0, 0);
pBamV2.add(cbOverwritePvrzIndices, c);
c = ViewerUtil.setGBC(c, 0, 3, 2, 1, 1.0, 1.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH,
new Insets(0, 0, 0, 0), 0, 0);
pBamV2.add(new JPanel(), c); // filler component

Expand Down Expand Up @@ -565,6 +580,7 @@ private void setDefaults() {
cbCompressBam.setSelected(DEFAULT_COMPRESS_BAM);
cbCompressionType.setSelectedIndex(DEFAULT_COMPRESSION_TYPE);
sPvrzIndex.setValue(DEFAULT_PVRZ_INDEX);
cbOverwritePvrzIndices.setSelected(DEFAULT_OVERWRITE_PVRZ_INDICES);
}

// Fetches the values from the dialog controls
Expand All @@ -579,6 +595,7 @@ private void updateSettings() {
compressBam = cbCompressBam.isSelected();
compressionType = cbCompressionType.getSelectedIndex();
pvrzIndex = (Integer) sPvrzIndex.getValue();
overwritePvrzIndices = cbOverwritePvrzIndices.isSelected();
validateSettings();

// transparency options may have changed: force palette generation
Expand Down
71 changes: 56 additions & 15 deletions src/org/infinity/resource/graphics/PseudoBamDecoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import java.awt.image.IndexColorModel;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
Expand Down Expand Up @@ -885,8 +886,8 @@ public boolean exportBamV1(Path fileName, ProgressMonitor progress, int curProgr
* @return {@code true} if the export was successful, {@code false} otherwise.
* @throws Exception If an unrecoverable error occured.
*/
public boolean exportBamV2(Path fileName, DxtEncoder.DxtType dxtType, int pvrzIndex, ProgressMonitor progress,
int curProgress) throws Exception {
public boolean exportBamV2(Path fileName, DxtEncoder.DxtType dxtType, int pvrzIndex, boolean overwrite,
ProgressMonitor progress, int curProgress) throws Exception {
final int FrameEntrySize = 12;
final int CycleEntrySize = 4;
final int BlockEntrySize = 28;
Expand Down Expand Up @@ -924,6 +925,28 @@ public boolean exportBamV2(Path fileName, DxtEncoder.DxtType dxtType, int pvrzIn
return false;
}

if (!overwrite) {
// adjusting pvrz indices
final HashMap<Integer, Integer> indexMap = new HashMap<>(Math.max(4, listGrid.size() + listGrid.size() / 2));
for (final FrameDataV2 frame : listFrameData) {
final int newIndex = indexMap.computeIfAbsent(frame.page, index -> {
for (int i = index; i < 100_000; i++) {
if (!indexMap.containsValue(i)) {
final Path pvrzPath = pvrzFilePath.resolve(getPvrzFileName(i));
if (!Files.exists(pvrzPath)) {
return i;
}
}
}
return -1;
});
if (newIndex < 0) {
throw new Exception("Effective PVRZ index is out of range [0..99999].");
}
frame.page = newIndex;
}
}

// generating remaining info blocks
List<FrameDataV2> listFrameDataBlocks = new ArrayList<>();
List<PseudoBamFrameEntry> listFrameEntries = new ArrayList<>();
Expand Down Expand Up @@ -1216,6 +1239,17 @@ public static void unregisterColors(HashMap<Integer, Integer> colorMap, Buffered
}
}

/**
* Returns a PVRZ filename (without path) for the specified index. Throws an {@link IndexOutOfBoundsException}
* if the index is out of bounds.
*/
public static String getPvrzFileName(int index) throws IndexOutOfBoundsException {
if (index < 0 || index > 99999) {
throw new IndexOutOfBoundsException("Pvrz index is out of bounds: " + index);
}
return String.format("MOS%04d.PVRZ", index);
}

// Calculates the locations of all frames on PVRZ textures and stores the results in framesList and gridList.
private boolean buildFrameDataList(List<FrameDataV2> framesList, List<BinPack2D> gridList, int pvrzPageIndex)
throws Exception {
Expand Down Expand Up @@ -1271,50 +1305,51 @@ private boolean createPvrzPages(Path path, DxtEncoder.DxtType dxtType, List<BinP
}
int dxtCode = (dxtType == DxtEncoder.DxtType.DXT5) ? 11 : 7;
byte[] output = new byte[DxtEncoder.calcImageSize(1024, 1024, dxtType)];
int pageMin = Integer.MAX_VALUE;
int pageMax = -1;
final HashSet<Integer> pageSet = new HashSet<>();
for (FrameDataV2 entry : framesList) {
pageMin = Math.min(pageMin, entry.page);
pageMax = Math.max(pageMax, entry.page);
pageSet.add(entry.page);
}
final List<Integer> pageList = new ArrayList<>(pageSet);
pageList.sort(null);

String note = "Generating PVRZ file %s / %s";
if (progress != null) {
if (curProgress < 0) {
curProgress = 0;
}
progress.setMaximum(curProgress + pageMax - pageMin + 1);
progress.setMaximum(curProgress + pageList.size());
progress.setProgress(curProgress++);
}

// processing each PVRZ page
for (int i = pageMin; i <= pageMax; i++) {
for (int i = 0; i < pageList.size(); i++) {
if (progress != null) {
if (progress.isCanceled()) {
throw new Exception("Conversion has been cancelled by the user.");
}
progress.setProgress(curProgress);
progress.setNote(String.format(note, curProgress, pageMax - pageMin + 1));
progress.setNote(String.format(note, curProgress, pageList.size()));
curProgress++;
}

Path pvrzName = path.resolve(String.format("MOS%04d.PVRZ", i));
BinPack2D packer = gridList.get(i - pageMin);
final int pageIndex = pageList.get(i);
final Path pvrzName = path.resolve(getPvrzFileName(pageIndex));
final BinPack2D packer = gridList.get(i);
packer.shrinkBin(true);

// generating texture image
int tw = packer.getBinWidth();
int th = packer.getBinHeight();
BufferedImage texture = ColorConvert.createCompatibleImage(tw, th, true);
final BufferedImage texture = ColorConvert.createCompatibleImage(tw, th, true);
Graphics2D g = texture.createGraphics();
try {
g.setComposite(AlphaComposite.Src);
g.setColor(ColorConvert.TRANSPARENT_COLOR);
g.fillRect(0, 0, texture.getWidth(), texture.getHeight());
for (int frameIdx = 0; frameIdx < listFrames.size(); frameIdx++) {
BufferedImage image = listFrames.get(frameIdx).frame;
FrameDataV2 frame = framesList.get(frameIdx);
if (frame.page == i) {
final BufferedImage image = listFrames.get(frameIdx).frame;
final FrameDataV2 frame = framesList.get(frameIdx);
if (frame.page == pageIndex) {
int sx = frame.dx, sy = frame.dy;
int dx = frame.sx, dy = frame.sy;
int w = frame.width, h = frame.height;
Expand Down Expand Up @@ -2181,5 +2216,11 @@ public boolean equals(Object obj) {
return dx == other.dx && dy == other.dy && height == other.height && page == other.page && sx == other.sx
&& sy == other.sy && width == other.width;
}

@Override
public String toString() {
return "FrameDataV2 [page=" + page + ", sx=" + sx + ", sy=" + sy + ", width=" + width + ", height=" + height
+ ", dx=" + dx + ", dy=" + dy + "]";
}
}
}

0 comments on commit 888a784

Please sign in to comment.