From d7d39c8caaf12f627ff60b4da8ec5ad1d0886df7 Mon Sep 17 00:00:00 2001 From: Argent77 <4519923+Argent77@users.noreply.github.com> Date: Mon, 20 Apr 2020 12:44:24 +0200 Subject: [PATCH 01/25] Improve ARE animation appearance flags descriptions --- src/org/infinity/resource/are/Animation.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/infinity/resource/are/Animation.java b/src/org/infinity/resource/are/Animation.java index f40e39884..3539efae2 100644 --- a/src/org/infinity/resource/are/Animation.java +++ b/src/org/infinity/resource/are/Animation.java @@ -40,7 +40,7 @@ public final class Animation extends AbstractStruct implements AddRemovable {"Not shown", "Is shown", "No shadow", "Not light source", "Partial animation", "Synchronized draw", "Random start","Not covered by wall", "Static animation", "Draw as background", "Play all frames", "Recolored by palette", "Mirror Y axis", - "Don't remove in combat", "EE: Use WBM", "EE: Under ground", "EE: Use PVRZ"}; + "Don't remove in combat", "EE: Use WBM", "EE: Draw stenciled", "EE: Use PVRZ"}; Animation() throws Exception { From 3ca9ff7b8d8cc5cabf66aaca00e0b2c7d56bb1aa Mon Sep 17 00:00:00 2001 From: Argent77 <4519923+Argent77@users.noreply.github.com> Date: Mon, 20 Apr 2020 17:17:52 +0200 Subject: [PATCH 02/25] Fix overlay rendering issues in area viewer - Consider custom transparency from ARE > Overlay transparency - Fix overlay rendering issues in EET - Improve rendering performance --- src/org/infinity/resource/Profile.java | 2 +- .../resource/are/viewer/AreaViewer.java | 28 +- .../resource/are/viewer/TilesetRenderer.java | 305 ++++++++++-------- 3 files changed, 194 insertions(+), 141 deletions(-) diff --git a/src/org/infinity/resource/Profile.java b/src/org/infinity/resource/Profile.java index e5ce1c52b..759683be4 100644 --- a/src/org/infinity/resource/Profile.java +++ b/src/org/infinity/resource/Profile.java @@ -118,7 +118,7 @@ public enum Engine { IWD, /** Includes IWD2. */ IWD2, - /** Includes BG1EE, BG1SoD, BG2EE, IWDEE and EET. */ + /** Includes BG1EE, BG1SoD, BG2EE, IWDEE, PSTEE and EET. */ EE, } diff --git a/src/org/infinity/resource/are/viewer/AreaViewer.java b/src/org/infinity/resource/are/viewer/AreaViewer.java index 0068c5e5a..79b3af4a5 100644 --- a/src/org/infinity/resource/are/viewer/AreaViewer.java +++ b/src/org/infinity/resource/are/viewer/AreaViewer.java @@ -81,6 +81,7 @@ import org.infinity.NearInfinity; import org.infinity.datatype.Flag; +import org.infinity.datatype.IsNumeric; import org.infinity.datatype.ResourceRef; import org.infinity.datatype.SectionOffset; import org.infinity.gui.ButtonPopupWindow; @@ -289,7 +290,7 @@ private void init() // Creating main view area pCanvas = new JPanel(new GridBagLayout()); - rcCanvas = new TilesetRenderer(); + rcCanvas = new TilesetRenderer(map.getOverlayTransparency()); rcCanvas.addComponentListener(getListeners()); rcCanvas.addMouseListener(getListeners()); rcCanvas.addMouseMotionListener(getListeners()); @@ -865,7 +866,7 @@ private synchronized void setVisualState(int hour) initProgressMonitor(this, "Loading tileset...", null, 1, 0, 0); } if (!rcCanvas.isMapLoaded() || rcCanvas.getWed() != map.getWed(ViewerConstants.AREA_DAY)) { - rcCanvas.loadMap(map.getWed(ViewerConstants.AREA_DAY)); + rcCanvas.loadMap(map.getOverlayTransparency(), map.getWed(ViewerConstants.AREA_DAY)); reloadWedLayers(true); } rcCanvas.setLighting(index); @@ -875,7 +876,7 @@ private synchronized void setVisualState(int hour) initProgressMonitor(this, "Loading tileset...", null, 1, 0, 0); } if (!rcCanvas.isMapLoaded() || rcCanvas.getWed() != map.getWed(ViewerConstants.AREA_DAY)) { - rcCanvas.loadMap(map.getWed(ViewerConstants.AREA_DAY)); + rcCanvas.loadMap(map.getOverlayTransparency(), map.getWed(ViewerConstants.AREA_DAY)); reloadWedLayers(true); } rcCanvas.setLighting(index); @@ -886,7 +887,7 @@ private synchronized void setVisualState(int hour) } if (!rcCanvas.isMapLoaded() || map.hasExtendedNight()) { if (rcCanvas.getWed() != map.getWed(ViewerConstants.AREA_NIGHT)) { - rcCanvas.loadMap(map.getWed(ViewerConstants.AREA_NIGHT)); + rcCanvas.loadMap(map.getOverlayTransparency(), map.getWed(ViewerConstants.AREA_NIGHT)); reloadWedLayers(true); } } @@ -2506,6 +2507,7 @@ private static class Map private final GraphicsResource[] mapLight = new GraphicsResource[]{null, null}; private final AreResource are; + private int overlayTransparency; private boolean hasDayNight, hasExtendedNight; private AbstractLayerItem areItem, songItem, restItem; private GraphicsResource mapSearch, mapHeight; @@ -2727,6 +2729,15 @@ public boolean hasExtendedNight() return hasExtendedNight; } + /** + * Returns the alpha blending strength of overlays in the range [0, 255] + * where 0 is fully opaque and 255 is fully transparent. + * Default for classic BG1: 0 (fully opaque); everything else: 128 (50% transparency) */ + public int getOverlayTransparency() + { + return overlayTransparency; + } + private static GraphicsResource loadMap(String mapName, GraphicsResource def) { try { @@ -2738,7 +2749,7 @@ private static GraphicsResource loadMap(String mapName, GraphicsResource def) private void init() { - // fetching important flags + // fetching important options final Flag flags = (Flag)are.getAttribute(AreResource.ARE_LOCATION); if (flags != null) { if (Profile.getEngine() == Profile.Engine.PST) { @@ -2749,6 +2760,13 @@ private void init() hasExtendedNight = flags.isFlagSet(6); } } + if (Profile.isEnhancedEdition()) { + overlayTransparency = ((IsNumeric)are.getAttribute(AreResource.ARE_OVERLAY_TRANSPARENCY)).getValue(); + if (overlayTransparency == 0) + overlayTransparency = 128; + } else { + overlayTransparency = (Profile.getEngine() == Profile.Engine.BG1) ? 0 : 128; + } // initializing pseudo layer items areItem = new IconLayerItem(are, are.getName()); diff --git a/src/org/infinity/resource/are/viewer/TilesetRenderer.java b/src/org/infinity/resource/are/viewer/TilesetRenderer.java index a710ae283..256358ff6 100644 --- a/src/org/infinity/resource/are/viewer/TilesetRenderer.java +++ b/src/org/infinity/resource/are/viewer/TilesetRenderer.java @@ -43,20 +43,22 @@ public class TilesetRenderer extends RenderCanvas { public static final String[] LabelVisualStates = {"Day", "Twilight", "Night"}; - // Rendering modes for tiles (affects how to render overlayed tiles) - public static final int MODE_AUTO = 0; // mode based on current game id - public static final int MODE_BG1 = 1; // forces BG1 rendering mode - public static final int MODE_BG2 = 2; // forces BG2 rendering mode + /** Available rendering modes for tiles. Affects how overlays are rendered. */ + public enum RenderMode { + /** Determine rendering mode based on detected game engine. */ + Auto, + /** Masked overlays supported by original BG1 engine. */ + Masked, + /** Blended overlays supported by IWD, BG2 and EE engines. */ + Blended, + } private static final int MaxOverlays = 8; // max. supported overlay entries private static final double MinZoomFactor = 1.0/64.0; // lower zoom factor limit private static final double MaxZoomFactor = 16.0; // upper zoom factor limit // Placeholder for missing tile data - private static final int[] DEFAULT_TILE_DATA = new int[64*64]; - static { - initDefaultTile(DEFAULT_TILE_DATA); - } + private static final int[] DEFAULT_TILE_DATA = createDefaultTile(); // Lighting adjustment for day/twilight/night times (multiplied by 10.24 for faster calculations) // Formula: @@ -77,7 +79,8 @@ public class TilesetRenderer extends RenderCanvas private final BufferedImage workingTile = ColorConvert.createCompatibleImage(64, 64, true); // internally used for drawing tile graphics private WedResource wed; // current wed resource - private int renderingMode = MODE_AUTO; // the rendering mode to use for processing overlayed tiles + private int overlayTransparency; // overlay transparency strength from 0 (opaque) to 255 (transparent) + private RenderMode renderingMode = RenderMode.Auto; // the rendering mode to use for processing overlayed tiles private boolean overlaysEnabled = true; // indicates whether to draw overlays private boolean blendedOverlays; // indicates whether to blend overlays with tile graphics private boolean hasChangedMap, hasChangedAppearance, hasChangedOverlays, hasChangedDoorState; @@ -98,15 +101,15 @@ public static int getLightingModesCount() return LabelVisualStates.length; } - public TilesetRenderer() + public TilesetRenderer(int overlayTransparency) { - this(null); + this(overlayTransparency, null); } - public TilesetRenderer(WedResource wed) + public TilesetRenderer(int overlayTransparency, WedResource wed) { super(); - init(wed); + init(overlayTransparency, wed); } /** @@ -149,10 +152,10 @@ public void removeChangeListener(TilesetChangeListener listener) * @param wed WED resource structure used to construct a map. * @return true if map has been initialized successfully, false otherwise. */ - public boolean loadMap(WedResource wed) + public boolean loadMap(int defaultTransparency, WedResource wed) { if (this.wed != wed) { - return init(wed); + return init(defaultTransparency, wed); } else { return true; } @@ -185,7 +188,7 @@ public void clear() /** * Returns the current mode for processing overlays. */ - public int getRenderingMode() + public RenderMode getRenderingMode() { return renderingMode; } @@ -194,9 +197,9 @@ public int getRenderingMode() * Specify how to draw overlayed tiles. Possible choices are MODE_AUTO, MODE_BG1 and MODE_BG2. * @param mode The new rendering mode */ - public void setRenderingMode(int mode) + public void setRenderingMode(RenderMode mode) { - if (mode < MODE_AUTO) mode = MODE_AUTO; else if (mode > MODE_BG2) mode = MODE_BG2; + if (mode == null) mode = RenderMode.Auto; if (mode != renderingMode) { renderingMode = mode; hasChangedOverlays = true; @@ -561,19 +564,19 @@ protected void paintCanvas(Graphics g) } } - private static void initDefaultTile(int[] buffer) + private static int[] createDefaultTile() { - if (buffer != null) { - BufferedImage image = new BufferedImage(64, 64, BufferedImage.TYPE_INT_ARGB); - Graphics2D g = image.createGraphics(); - g.setColor(Color.GRAY); - g.fillRect(0, 0, image.getWidth(), image.getHeight()); - g.dispose(); - int[] pixels = ((DataBufferInt)image.getRaster().getDataBuffer()).getData(); - for (int i = 0, cnt = Math.min(buffer.length, pixels.length); i < cnt; i++) { - buffer[i] = pixels[i]; - } + int[] buffer = new int[64*64]; + BufferedImage image = new BufferedImage(64, 64, BufferedImage.TYPE_INT_ARGB); + Graphics2D g = image.createGraphics(); + g.setColor(Color.GRAY); + g.fillRect(0, 0, image.getWidth(), image.getHeight()); + g.dispose(); + int[] pixels = ((DataBufferInt)image.getRaster().getDataBuffer()).getData(); + for (int i = 0, cnt = Math.min(buffer.length, pixels.length); i < cnt; i++) { + buffer[i] = pixels[i]; } + return buffer; } // Resizes the current image or creates a new one if needed @@ -591,15 +594,16 @@ private boolean updateImageSize() } // Initializes a new map - private boolean init(WedResource wed) + private boolean init(int overlayTransparency, WedResource wed) { release(false); // resetting states - blendedOverlays = Profile.getProperty(Profile.Key.IS_TILESET_STENCILED); + blendedOverlays = Profile.getEngine() != Profile.Engine.BG1; lighting = ViewerConstants.LIGHTING_DAY; // loading map data + this.overlayTransparency = overlayTransparency; if (wed != null) { if (initWed(wed)) { this.wed = wed; @@ -787,6 +791,131 @@ private void drawDoorTiles() } } + // render tile graphics without overlays + private void drawTileSimple(int[] sourceTile, int[] renderTarget) + { + if (sourceTile != null) { + int pixel, fr, fg, fb; + for (int ofs = 0; ofs < 4096; ofs++) { + pixel = sourceTile[ofs]; + fr = (pixel >>> 16) & 0xff; + fg = (pixel >>> 8) & 0xff; + fb = pixel & 0xff; + + // applying lighting conditions + fr = (fr * LightingAdjustment[lighting][0]) >>> LightingAdjustmentShift; + fg = (fg * LightingAdjustment[lighting][1]) >>> LightingAdjustmentShift; + fb = (fb * LightingAdjustment[lighting][2]) >>> LightingAdjustmentShift; + renderTarget[ofs] = 0xff000000 | (fr << 16) | (fg << 8) | fb; + } + } else { + // no tile = transparent pixel data (work-around for faulty tiles in BG1's WEDs) + for (int ofs = 0; ofs < 4096; ofs++) + renderTarget[ofs] = 0; + } + } + + // compose tile graphics in BG1 mode + private void drawTileMasked(int[] primaryTile, int[] secondaryTile, int[] overlayTile, int[] renderTarget, boolean isDoorTile, boolean isDoorClosed) + { + if (renderTarget != null) { + int[] src = (isDoorTile && isDoorClosed) ? secondaryTile : primaryTile; + int fr, fg, fb, pixel; + for (int ofs = 0; ofs < 4096; ofs++) { + // composing pixel data + if (src != null && (src[ofs] & 0xff000000) != 0) + pixel = src[ofs]; + else if (overlayTile != null) + pixel = overlayTile[ofs]; + else + pixel = 0; + fr = (pixel >>> 16) & 0xff; + fg = (pixel >>> 8) & 0xff; + fb = pixel & 0xff; + + // applying lighting conditions + fr = (fr * LightingAdjustment[lighting][0]) >>> LightingAdjustmentShift; + fg = (fg * LightingAdjustment[lighting][1]) >>> LightingAdjustmentShift; + fb = (fb * LightingAdjustment[lighting][2]) >>> LightingAdjustmentShift; + renderTarget[ofs] = 0xff000000 | (fr << 16) | (fg << 8) | fb; + } + } + } + + // compose tile graphics in BG2 mode + private void drawTileBlended(int[] primaryTile, int[] secondaryTile, int[] overlayTile, int[] renderTarget, boolean isPaletted) + { + if (renderTarget != null) { + int pixel, fr, fg, fb; + boolean pa = false, sa = false; + int pr = 0, pg = 0, pb = 0, sr = 0, sg = 0, sb = 0, or = 0, og = 0, ob = 0; + int alphaSrc = overlayTransparency, alphaDst = 255 - overlayTransparency; + for (int ofs = 0; ofs < 4096; ofs++) { + // getting source pixels + if (primaryTile != null) { + pixel = primaryTile[ofs]; + pa = (pixel & 0xff000000) != 0; + pr = (pixel >>> 16) & 0xff; + pg = (pixel >>> 8) & 0xff; + pb = pixel & 0xff; + } + + if (secondaryTile != null) { + pixel = secondaryTile[ofs]; + sa = (pixel & 0xff000000) != 0; + sr = (pixel >>> 16) & 0xff; + sg = (pixel >>> 8) & 0xff; + sb = pixel & 0xff; + } + + if (overlayTile != null) { + pixel = overlayTile[ofs]; + or = (pixel >>> 16) & 0xff; + og = (pixel >>> 8) & 0xff; + ob = pixel & 0xff; + } + + // composing pixel data + // blending modes depend on transparency states of primary and secondary pixels + if (pa && !sa) { + if (isPaletted) { + fr = (pr * alphaSrc) + (or * alphaDst) >>> 8; + fg = (pg * alphaSrc) + (og * alphaDst) >>> 8; + fb = (pb * alphaSrc) + (ob * alphaDst) >>> 8; + } else { + if (secondaryTile != null) { + fr = pr; + fg = pg; + fb = pb; + } else { + fr = (pr * alphaSrc) + (or * alphaDst) >>> 8; + fg = (pg * alphaSrc) + (og * alphaDst) >>> 8; + fb = (pb * alphaSrc) + (ob * alphaDst) >>> 8; + } + } + } else if (pa && sa) { + fr = (pr * alphaSrc) + (sr * alphaDst) >>> 8; + fg = (pg * alphaSrc) + (sg * alphaDst) >>> 8; + fb = (pb * alphaSrc) + (sb * alphaDst) >>> 8; + } else if (!pa && !sa) { + fr = or; + fg = og; + fb = ob; + } else { // !pa && sa + fr = (sr * alphaSrc) + (or * alphaDst) >>> 8; + fg = (sg * alphaSrc) + (og * alphaDst) >>> 8; + fb = (sb * alphaSrc) + (ob * alphaDst) >>> 8; + } + + // applying lighting conditions + fr = (fr * LightingAdjustment[lighting][0]) >>> LightingAdjustmentShift; + fg = (fg * LightingAdjustment[lighting][1]) >>> LightingAdjustmentShift; + fb = (fb * LightingAdjustment[lighting][2]) >>> LightingAdjustmentShift; + renderTarget[ofs] = 0xff000000 | (fr << 16) | (fg << 8) | fb; + } + } + } + // draws the specified tile into the target graphics buffer private synchronized void drawTile(Tile tile, boolean isDoorTile) { @@ -794,7 +923,6 @@ private synchronized void drawTile(Tile tile, boolean isDoorTile) boolean isDoorClosed = (Profile.getEngine() == Profile.Engine.PST) ? !isClosed : isClosed; int[] target = ((DataBufferInt)workingTile.getRaster().getDataBuffer()).getData(); - int fa = 255, fr = 0, fg = 0, fb = 0; if (overlaysEnabled && tile.hasOverlay() && hasOverlay(tile.getOverlayIndex())) { // overlayed tile // preparing graphics data int overlay = tile.getOverlayIndex(); @@ -816,92 +944,13 @@ private synchronized void drawTile(Tile tile, boolean isDoorTile) } // determining correct rendering mode - boolean blended = (renderingMode == MODE_AUTO && blendedOverlays) || (renderingMode == MODE_BG2); + boolean blended = (renderingMode == RenderMode.Auto && blendedOverlays) || (renderingMode == RenderMode.Blended); // drawing tile graphics - boolean pa, sa; - int pr, pg, pb, sr, sg, sb, or, og, ob; - for (int ofs = 0; ofs < 4096; ofs++) { - if (blended) { // BG2/BGEE mode overlays - // extracting color components - if (srcPri != null) { - pa = (srcPri[ofs] & 0xff000000) != 0; - pr = (srcPri[ofs] >>> 16) & 0xff; - pg = (srcPri[ofs] >>> 8) & 0xff; - pb = srcPri[ofs] & 0xff; - } else { - pa = false; - pr = pg = pb = 0; - } - if (srcSec != null) { - sa = (srcSec[ofs] & 0xff000000) != 0; - sr = (srcSec[ofs] >>> 16) & 0xff; - sg = (srcSec[ofs] >>> 8) & 0xff; - sb = srcSec[ofs] & 0xff; - } else { - sa = false; - sr = sg = sb = 0; - } - if (srcOvl != null) { - or = (srcOvl[ofs] >>> 16) & 0xff; - og = (srcOvl[ofs] >>> 8) & 0xff; - ob = srcOvl[ofs] & 0xff; - } else { - or = og = ob = 0; - } - - // blending modes depend on transparency states of primary and secondary pixels - if (pa && !sa) { - if (tile.isTisV1()) { - fr = (pr + or) >>> 1; - fg = (pg + og) >>> 1; - fb = (pb + ob) >>> 1; - } else { - if (srcSec != null) { - fr = pr; - fg = pg; - fb = pb; - } else { - fr = (pr + or) >>> 1; - fg = (pg + og) >>> 1; - fb = (pb + ob) >>> 1; - } - } - } else if (pa && sa) { - fr = (pr + sr) >>> 1; - fg = (pg + sg) >>> 1; - fb = (pb + sb) >>> 1; - } else if (!pa && !sa) { - fr = or; - fg = og; - fb = ob; - } else if (!pa && sa) { - fr = (sr + or) >>> 1; - fg = (sg + og) >>> 1; - fb = (sb + ob) >>> 1; - } - } else { // BG1 mode overlays - int[] src = (isDoorTile && isDoorClosed) ? srcSec : srcPri; - if (src != null) { - if ((src[ofs] & 0xff000000) != 0 && src != null) { - fr = (src[ofs] >>> 16) & 0xff; - fg = (src[ofs] >>> 8) & 0xff; - fb = src[ofs] & 0xff; - } else if (srcOvl != null) { - fr = (srcOvl[ofs] >>> 16) & 0xff; - fg = (srcOvl[ofs] >>> 8) & 0xff; - fb = srcOvl[ofs] & 0xff; - } - } else { - fa = fr = fg = fb = 0; - } - } - - // applying lighting conditions - fr = (fr * LightingAdjustment[lighting][0]) >>> LightingAdjustmentShift; - fg = (fg * LightingAdjustment[lighting][1]) >>> LightingAdjustmentShift; - fb = (fb * LightingAdjustment[lighting][2]) >>> LightingAdjustmentShift; - target[ofs] = (fa << 24) | (fr << 16) | (fg << 8) | fb; + if (blended) { + drawTileBlended(srcPri, srcSec, srcOvl, target, tile.isTisV1()); + } else { + drawTileMasked(srcPri, srcSec, srcOvl, target, isDoorTile, isDoorClosed); } srcOvl = null; srcPri = null; @@ -920,22 +969,7 @@ private synchronized void drawTile(Tile tile, boolean isDoorTile) } // drawing tile graphics - if (srcTile != null) { - for (int ofs = 0; ofs < 4096; ofs++) { - fr = (srcTile[ofs] >>> 16) & 0xff; - fg = (srcTile[ofs] >>> 8) & 0xff; - fb = srcTile[ofs] & 0xff; - fr = (fr * LightingAdjustment[lighting][0]) >>> LightingAdjustmentShift; - fg = (fg * LightingAdjustment[lighting][1]) >>> LightingAdjustmentShift; - fb = (fb * LightingAdjustment[lighting][2]) >>> LightingAdjustmentShift; - target[ofs] = 0xff000000 | (fr << 16) | (fg << 8) | fb; - } - } else { - // no tile = transparent pixel data (work-around for faulty tiles in BG1's WEDs) - for (int ofs = 0; ofs < 4096; ofs++) { - target[ofs] = 0; - } - } + drawTileSimple(srcTile, target); srcTile = null; } @@ -1046,6 +1080,7 @@ private static class Tileset public final List listOverlayTiles = new ArrayList(); public int tilesX, tilesY; // stores number of tiles per row/column + public boolean isTisPalette; // whether tileset is palette-based public Tileset(WedResource wed, Overlay ovl) { @@ -1070,12 +1105,12 @@ private void init(WedResource wed, Overlay ovl) { if (wed != null && ovl != null) { // storing tile data - boolean isTilesetV1 = true; + isTisPalette = !Profile.isEnhancedEdition(); // choose sane default ResourceEntry tisEntry = getTisResource(wed, ovl); if (tisEntry != null) { try { TisDecoder decoder = TisDecoder.loadTis(tisEntry); - isTilesetV1 = decoder.getType() == TisDecoder.Type.PALETTE; + isTisPalette = decoder.getType() == TisDecoder.Type.PALETTE; BufferedImage tileImage = new BufferedImage(64, 64, BufferedImage.TYPE_INT_ARGB); for (int i = 0, tCount = decoder.getTileCount(); i < tCount; i++) { decoder.getTile(i, tileImage); @@ -1133,7 +1168,7 @@ private void init(WedResource wed, Overlay ovl) Flag drawOverlays = (Flag)tile.getAttribute(Tilemap.WED_TILEMAP_DRAW_OVERLAYS); int flags = (int)drawOverlays.getValue() & 255; - listTiles.add(new Tile(x, y, count, tileIdx, tileIdx2, flags, isTilesetV1)); + listTiles.add(new Tile(x, y, count, tileIdx, tileIdx2, flags, isTisPalette)); curOfs += tile.getSize(); } else { listTiles.add(new Tile(x, y, 0, new int[]{}, -1, 0, true)); // needed as placeholder From edd779efac8ba72960757b7c50a3baba47fe8e2b Mon Sep 17 00:00:00 2001 From: Argent77 <4519923+Argent77@users.noreply.github.com> Date: Mon, 20 Apr 2020 19:13:20 +0200 Subject: [PATCH 03/25] Retrieve area search names in non-EE games from worldmaps --- src/org/infinity/resource/AbstractStruct.java | 15 ++++++ .../infinity/resource/are/AreResource.java | 51 +++++++++++++++++++ src/org/infinity/resource/wmp/AreaEntry.java | 2 +- src/org/infinity/resource/wmp/MapEntry.java | 2 +- 4 files changed, 68 insertions(+), 2 deletions(-) diff --git a/src/org/infinity/resource/AbstractStruct.java b/src/org/infinity/resource/AbstractStruct.java index 34a659e8b..b58166c2b 100644 --- a/src/org/infinity/resource/AbstractStruct.java +++ b/src/org/infinity/resource/AbstractStruct.java @@ -18,6 +18,7 @@ import java.util.List; import java.util.ListIterator; import java.util.Map; +import java.util.stream.Collectors; import javax.swing.JComponent; import javax.swing.JOptionPane; @@ -744,6 +745,20 @@ public List getFields() return fields; } + /** + * Returns an unmodifiable list of fields of this structure matching the specified structure type. + * @param type The class type to filter. Specify {@code null} to return all fields of this structure. + * @return Unmodifiable list of fields of {@code type}. + */ + public List getFields(Class type) + { + return Collections.unmodifiableList( + fields + .stream() + .filter(se -> type == null || type.isAssignableFrom(se.getClass())) + .collect(Collectors.toList())); + } + /** * Returns the StructEntry object at the specified index. * @param index The index of the desired StructEntry object. diff --git a/src/org/infinity/resource/are/AreResource.java b/src/org/infinity/resource/are/AreResource.java index f28a56397..bb34371e5 100644 --- a/src/org/infinity/resource/are/AreResource.java +++ b/src/org/infinity/resource/are/AreResource.java @@ -8,6 +8,7 @@ import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Set; @@ -20,6 +21,8 @@ import org.infinity.datatype.DecNumber; import org.infinity.datatype.Flag; import org.infinity.datatype.HexNumber; +import org.infinity.datatype.IsNumeric; +import org.infinity.datatype.IsReference; import org.infinity.datatype.ResourceRef; import org.infinity.datatype.SectionCount; import org.infinity.datatype.SectionOffset; @@ -40,6 +43,8 @@ import org.infinity.resource.are.viewer.AreaViewer; import org.infinity.resource.key.ResourceEntry; import org.infinity.resource.vertex.Vertex; +import org.infinity.resource.wmp.AreaEntry; +import org.infinity.resource.wmp.WmpResource; import org.infinity.search.SearchOptions; import org.infinity.util.IdsMapCache; import org.infinity.util.LuaEntry; @@ -198,6 +203,9 @@ private static void initMapNames(boolean force) { } else if (ResourceFactory.resourceExists("MAPNAME.2DA")) { // PST map names mapNames = createMapNamesFromTable(); + } else { + // try getting map names from worldmaps + mapNames = createMapNamesFromWorldmap(); } } } @@ -251,6 +259,49 @@ private static HashMap createMapNamesFromTable() return retVal; } + // Collect area names from worldmaps + private static HashMap createMapNamesFromWorldmap() + { + HashMap retVal = new HashMap<>(); + List wmpList = ResourceFactory.getResources("WMP", Collections.emptyList()); + if (wmpList != null) { + for (ResourceEntry wmpEntry : wmpList) { + try { + WmpResource wmp = (WmpResource)ResourceFactory.getResource(wmpEntry); + if (wmp != null) { + List mapList = wmp.getFields(org.infinity.resource.wmp.MapEntry.class); + for (StructEntry mapEntry : mapList) { + List areaList = ((AbstractStruct)mapEntry).getFields(AreaEntry.class); + for (StructEntry areaEntry : areaList) { + AreaEntry area = (AreaEntry)areaEntry; + + String resref = ((IsReference)area.getAttribute(AreaEntry.WMP_AREA_CURRENT)).getResourceName(); + if (resref == null || resref.isEmpty()) + continue; + int pos = resref.lastIndexOf('.'); + if (pos > 0) + resref = resref.substring(0, pos); + resref = resref.toUpperCase(); + + int strref = ((IsNumeric)area.getAttribute(AreaEntry.WMP_AREA_TOOLTIP)).getValue(); + if (!StringTable.isValidStringRef(strref)) + strref = ((IsNumeric)area.getAttribute(AreaEntry.WMP_AREA_NAME)).getValue(); + if (StringTable.isValidStringRef(strref)) { + String name = StringTable.getStringRef(strref); + if (name != null && !name.isEmpty()) + retVal.put(resref, name); + } + } + } + } + } catch (Exception e) { + // no need to report anything + } + } + } + return retVal; + } + public static void addScriptNames(Set scriptNames, ByteBuffer buffer) { int offset = 0; diff --git a/src/org/infinity/resource/wmp/AreaEntry.java b/src/org/infinity/resource/wmp/AreaEntry.java index 25f6874b2..5cdd2d189 100644 --- a/src/org/infinity/resource/wmp/AreaEntry.java +++ b/src/org/infinity/resource/wmp/AreaEntry.java @@ -19,7 +19,7 @@ import org.infinity.resource.AbstractStruct; import org.infinity.resource.HasViewerTabs; -final class AreaEntry extends AbstractStruct implements HasViewerTabs +final public class AreaEntry extends AbstractStruct implements HasViewerTabs { // WMP/AreaEntry-specific field labels public static final String WMP_AREA = "Area"; diff --git a/src/org/infinity/resource/wmp/MapEntry.java b/src/org/infinity/resource/wmp/MapEntry.java index 52f92a7dc..dc7db1dff 100644 --- a/src/org/infinity/resource/wmp/MapEntry.java +++ b/src/org/infinity/resource/wmp/MapEntry.java @@ -20,7 +20,7 @@ import org.infinity.resource.HasViewerTabs; import org.infinity.resource.Profile; -final class MapEntry extends AbstractStruct implements HasViewerTabs +final public class MapEntry extends AbstractStruct implements HasViewerTabs { // WMP/MapEntry-specific field labels public static final String WMP_MAP = "Map"; From e4ee685ff84c917bcfbf7022a7c8ae79e880a6eb Mon Sep 17 00:00:00 2001 From: Argent77 <4519923+Argent77@users.noreply.github.com> Date: Tue, 21 Apr 2020 09:56:29 +0200 Subject: [PATCH 04/25] BAM Converter: Fix alpha transparency issue with external palettes - Fixes alpha in BAM palette and Replace Colors filter - Added support for PNG palette in Replace Colors filter - Improved import of Microsoft PAL palettes --- .../gui/converter/BamFilterColorReplace.java | 6 +- .../gui/converter/BamPaletteDialog.java | 4 +- .../resource/graphics/ColorConvert.java | 72 +++++++++++-------- 3 files changed, 50 insertions(+), 32 deletions(-) diff --git a/src/org/infinity/gui/converter/BamFilterColorReplace.java b/src/org/infinity/gui/converter/BamFilterColorReplace.java index 61e93142b..c66b5ab75 100644 --- a/src/org/infinity/gui/converter/BamFilterColorReplace.java +++ b/src/org/infinity/gui/converter/BamFilterColorReplace.java @@ -23,6 +23,7 @@ import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Arrays; import javax.swing.AbstractAction; import javax.swing.BorderFactory; @@ -292,6 +293,9 @@ public void loadPalette(Path paletteFile) throws Exception int[] palette = null; if ("BM".equals(new String(signature, 0, 2))) { palette = ColorConvert.loadPaletteBMP(paletteFile); + } else if (Arrays.equals(Arrays.copyOfRange(signature, 0, 4), + new byte[]{(byte)0x89, 0x50, 0x4e, 0x47})) { + palette = ColorConvert.loadPalettePNG(paletteFile, ConvertToBam.getUseAlpha()); } else if ("RIFF".equals(new String(signature, 0, 4))) { palette = ColorConvert.loadPalettePAL(paletteFile); } else { @@ -299,7 +303,7 @@ public void loadPalette(Path paletteFile) throws Exception String ver = new String(signature, 4, 4); if ("BAM ".equals(sig) || "BAMC".equals(sig)) { if ("V1 ".equals(ver)) { - palette = ColorConvert.loadPaletteBAM(paletteFile); + palette = ColorConvert.loadPaletteBAM(paletteFile, ConvertToBam.getUseAlpha()); } else { throw new Exception(String.format("BAM file \"%s\" does not contain palette data.", paletteFile.getFileName())); diff --git a/src/org/infinity/gui/converter/BamPaletteDialog.java b/src/org/infinity/gui/converter/BamPaletteDialog.java index 3b1ce8f5f..37a54e5c4 100644 --- a/src/org/infinity/gui/converter/BamPaletteDialog.java +++ b/src/org/infinity/gui/converter/BamPaletteDialog.java @@ -264,13 +264,13 @@ public void loadExternalPalette(int type, Path paletteFile) throws Exception } else if (Arrays.equals(Arrays.copyOfRange(signature, 0, 4), new byte[]{(byte)0x89, 0x50, 0x4e, 0x47})) { // PNG supports palette with alpha channel - palette = ColorConvert.loadPalettePNG(paletteFile); + palette = ColorConvert.loadPalettePNG(paletteFile, ConvertToBam.getUseAlpha()); } else if ("RIFF".equals(new String(signature, 0, 4))) { palette = ColorConvert.loadPalettePAL(paletteFile); } else { String s = new String(signature); if ("BAM V1 ".equals(s) || "BAMCV1 ".equals(s)) { - palette = ColorConvert.loadPaletteBAM(paletteFile); + palette = ColorConvert.loadPaletteBAM(paletteFile, ConvertToBam.getUseAlpha()); } else { // Photoshop ACT files don't have a header palette = ColorConvert.loadPaletteACT(paletteFile); diff --git a/src/org/infinity/resource/graphics/ColorConvert.java b/src/org/infinity/resource/graphics/ColorConvert.java index ba2987244..777b2117f 100644 --- a/src/org/infinity/resource/graphics/ColorConvert.java +++ b/src/org/infinity/resource/graphics/ColorConvert.java @@ -458,7 +458,7 @@ public static int[] loadPaletteBMP(Path file) throws Exception is.read(palette); int[] retVal = new int[colorCount]; for (int i =0; i < colorCount; i++) { - retVal[i] = DynamicArray.getInt(palette, i << 2) & 0x00ffffff; + retVal[i] = 0xff000000 | (DynamicArray.getInt(palette, i << 2) & 0x00ffffff); } return retVal; } else { @@ -479,10 +479,11 @@ public static int[] loadPaletteBMP(Path file) throws Exception /** * Attempts to load a palette from the specified PNG file. * @param file The PNG file to extract the palette from. + * @param preserveAlpha Whether to preserve original alpha transparency of the palette. * @return The palette as ARGB integers. * @throws Exception on error. */ - public static int[] loadPalettePNG(Path file) throws Exception + public static int[] loadPalettePNG(Path file, boolean preserveAlpha) throws Exception { if (file != null && Files.isRegularFile(file)) { try (InputStream is = StreamUtils.getInputStream(file)) { @@ -491,9 +492,13 @@ public static int[] loadPalettePNG(Path file) throws Exception IndexColorModel cm = (IndexColorModel)img.getColorModel(); int[] retVal = new int[cm.getMapSize()]; cm.getRGBs(retVal); + if (!preserveAlpha) { + for (int i = 0; i < retVal.length; i++) + retVal[i] |= 0xff000000; + } return retVal; } else { - throw new Exception("Error loading palette from PNG fille " + file.getFileName()); + throw new Exception("Error loading palette from PNG file " + file.getFileName()); } } catch (IOException e) { e.printStackTrace(); @@ -505,7 +510,7 @@ public static int[] loadPalettePNG(Path file) throws Exception } /** - * Attempts to load a palette from the specified Windows PAL file. + * Attempts to load a palette from the specified Windows PAL file. Does not support alpha transparency. * @param file The Windows PAL file to load. * @return The palette as ARGB integers. * @throws Exception on error. @@ -514,30 +519,35 @@ public static int[] loadPalettePAL(Path file) throws Exception { if (file != null && Files.isRegularFile(file)) { try (InputStream is = StreamUtils.getInputStream(file)) { - byte[] signature = new byte[8]; - is.read(signature); - if ("RIFF".equals(new String(signature, 0, 4))) { - // extracting palette from Windows palette file + byte[] signature = new byte[12]; + boolean eof = is.read(signature) != signature.length; + if ("RIFF".equals(new String(signature, 0, 4)) && "PAL ".equals(new String(signature, 8, 4))) { byte[] signature2 = new byte[8]; - is.read(signature2); - if ("PAL data".equals(new String(signature2))) { - byte[] header = new byte[8]; - is.read(header); - int numColors = DynamicArray.getUnsignedShort(header, 6); - if (numColors >= 2 && numColors <= 256) { - byte[] palData = new byte[numColors << 2]; - is.read(palData); - int[] retVal = new int[numColors]; - for (int i = 0; i < numColors; i++) { - int col = DynamicArray.getInt(palData, i << 2); - retVal[i] = ((col << 16) & 0xff0000) | (col & 0x00ff00) | ((col >> 16) & 0x0000ff); - } - return retVal; - } else { - throw new Exception("Invalid number of color entries in Windows palette file " + file.getFileName()); + + // find palette data block + eof = is.read(signature2) != signature2.length; + while (!eof && !"data".equals(new String(signature2, 0, 4))) { + is.skip(DynamicArray.getInt(signature2, 4) - 4); + eof = is.read(signature2) != signature2.length; + } + if (eof) + throw new Exception(); + + // extracting palette from Windows palette file + byte[] header = new byte[4]; + is.read(header); + int numColors = DynamicArray.getUnsignedShort(header, 2); + if (numColors >= 2 && numColors <= 256) { + byte[] palData = new byte[numColors << 2]; + is.read(palData); + int[] retVal = new int[numColors]; + for (int i = 0; i < numColors; i++) { + int col = DynamicArray.getInt(palData, i << 2); + retVal[i] = 0xff000000 | ((col << 16) & 0xff0000) | (col & 0x00ff00) | ((col >> 16) & 0x0000ff); } + return retVal; } else { - throw new Exception("Error loading palette from Windows palette file " + file.getFileName()); + throw new Exception("Invalid number of color entries in Windows palette file " + file.getFileName()); } } else { throw new Exception("Invalid Windows palette file " + file.getFileName()); @@ -552,7 +562,7 @@ public static int[] loadPalettePAL(Path file) throws Exception } /** - * Attempts to load a palette from the specified Adobe Color Table file. + * Attempts to load a palette from the specified Adobe Color Table file. Does not support alpha transparency. * @param file The Adobe Color Table file to load. * @return The palette as ARGB integers. * @throws Exception on error. @@ -576,7 +586,7 @@ public static int[] loadPaletteACT(Path file) throws Exception if (i == transColor) { retVal[i] = 0x00ff00; } else { - retVal[i] = ((palData[ofs] & 0xff) << 16) | ((palData[ofs+1] & 0xff) << 8) | (palData[ofs+2] & 0xff); + retVal[i] = 0xff000000 | ((palData[ofs] & 0xff) << 16) | ((palData[ofs+1] & 0xff) << 8) | (palData[ofs+2] & 0xff); } } return retVal; @@ -595,10 +605,11 @@ public static int[] loadPaletteACT(Path file) throws Exception /** * Attempts to load a palette from the specified BAM file. * @param file The BAM file to extract the palette from. + * @param preserveAlpha Whether to preserve original alpha transparency of the BAM palette. * @return The palette as ARGB integers. * @throws Exception on error. */ - public static int[] loadPaletteBAM(Path file) throws Exception + public static int[] loadPaletteBAM(Path file, boolean preserveAlpha) throws Exception { if (file != null && Files.isRegularFile(file)) { try (InputStream is = StreamUtils.getInputStream(file)) { @@ -617,7 +628,10 @@ public static int[] loadPaletteBAM(Path file) throws Exception if (ofs >= 0x18 && ofs < bamData.length - 1024) { int[] retVal = new int[256]; for (int i = 0; i < 256; i++) { - retVal[i] = DynamicArray.getInt(bamData, ofs+(i << 2)) & 0x00ffffff; + retVal[i] = DynamicArray.getInt(bamData, ofs+(i << 2)); + // backwards compatibility with non-EE BAM files + if (!preserveAlpha || (retVal[i] & 0xff000000) == 0) + retVal[i] |= 0xff000000; } return retVal; } else { From 54aa962a444e6358c76ea3330df9e99097c90e73 Mon Sep 17 00:00:00 2001 From: Argent77 <4519923+Argent77@users.noreply.github.com> Date: Tue, 21 Apr 2020 11:17:47 +0200 Subject: [PATCH 05/25] Fix bug when assigning resource references containing lower-case extension - Added new method getResourceRef() to ResourceEntry class - Updated various instances to prefer getResourceRef() over getResourceName() --- src/org/infinity/datatype/ResourceRef.java | 14 ++++++-------- src/org/infinity/resource/dlg/DlgResource.java | 6 +----- .../infinity/resource/graphics/TisResource.java | 5 +---- .../infinity/resource/graphics/TisV2Decoder.java | 6 ++---- src/org/infinity/resource/itm/Viewer.java | 5 +---- src/org/infinity/resource/itm/ViewerAbility.java | 5 +---- .../infinity/resource/key/BIFFResourceEntry.java | 6 ++++++ .../infinity/resource/key/FileResourceEntry.java | 9 +++++++++ src/org/infinity/resource/key/ResourceEntry.java | 7 ++++++- src/org/infinity/resource/other/FntResource.java | 4 +--- .../infinity/resource/sav/SavResourceEntry.java | 10 ++++++++++ src/org/infinity/resource/spl/Viewer.java | 8 +++----- src/org/infinity/resource/video/MveResource.java | 7 +------ src/org/infinity/resource/video/WbmResource.java | 16 +++++----------- .../infinity/search/ScriptReferenceSearcher.java | 12 ++---------- .../infinity/search/WavReferenceSearcher.java | 14 +++++--------- 16 files changed, 60 insertions(+), 74 deletions(-) diff --git a/src/org/infinity/datatype/ResourceRef.java b/src/org/infinity/datatype/ResourceRef.java index 4a1b087d4..089283ab7 100644 --- a/src/org/infinity/datatype/ResourceRef.java +++ b/src/org/infinity/datatype/ResourceRef.java @@ -126,7 +126,7 @@ public JComponent edit(final ActionListener container) //FIXME: ResRefChecker check only that point is exist, so this must be // the same check or this check must be inside isLegalEntry(...) // There only 2 places where isLegalEntry is called: this and ResRefChecker - if (isLegalEntry(entry) && entry.getResourceName().lastIndexOf('.') <= 8) { + if (isLegalEntry(entry) && entry.getResourceRef().length() <= 8) { values.add(new ResourceRefEntry(entry)); } } @@ -226,19 +226,17 @@ public boolean updateValue(AbstractStruct struct) if (entry == null) { setValue(selected.name); } else { - int i = -1; + boolean found = false; for (final String type : types) { - //TODO: It seems that instead of toString getExtension must be used - i = entry.getResourceName().indexOf('.' + type.toUpperCase(Locale.ENGLISH)); - if (i != -1) { + found = entry.getExtension().equalsIgnoreCase(type); + if (found) { this.type = type; - setValue(entry.getResourceName().substring(0, i)); + setValue(entry.getResourceRef()); break; } } - if (i == -1) { + if (!found) return false; - } } // notifying listeners diff --git a/src/org/infinity/resource/dlg/DlgResource.java b/src/org/infinity/resource/dlg/DlgResource.java index f729b87fd..1b9762881 100644 --- a/src/org/infinity/resource/dlg/DlgResource.java +++ b/src/org/infinity/resource/dlg/DlgResource.java @@ -609,11 +609,7 @@ private boolean exportDlgAsText(PrintWriter writer) writer.println(); // *** start of WeiDU D script *** - String dlgResRef = getResourceEntry().getResourceName(); - int p = dlgResRef.lastIndexOf('.'); - if (p >= 0) { - dlgResRef = dlgResRef.substring(0, p); - } + String dlgResRef = getResourceEntry().getResourceRef(); writer.print("BEGIN ~" + dlgResRef + "~"); StructEntry entry = getAttribute(DLG_THREAT_RESPONSE); diff --git a/src/org/infinity/resource/graphics/TisResource.java b/src/org/infinity/resource/graphics/TisResource.java index 8cff95be9..3fecf273f 100644 --- a/src/org/infinity/resource/graphics/TisResource.java +++ b/src/org/infinity/resource/graphics/TisResource.java @@ -1232,10 +1232,7 @@ public static int calcTileWidth(ResourceEntry entry, int tileCount) // Try to fetch the correct width from an associated WED if available if (entry != null) { try { - String tisNameBase = entry.getResourceName(); - if (tisNameBase.lastIndexOf('.') > 0) { - tisNameBase = tisNameBase.substring(0, tisNameBase.lastIndexOf('.')); - } + String tisNameBase = entry.getResourceRef(); ResourceEntry wedEntry = null; while (tisNameBase.length() >= 6) { String wedFileName = tisNameBase + ".WED"; diff --git a/src/org/infinity/resource/graphics/TisV2Decoder.java b/src/org/infinity/resource/graphics/TisV2Decoder.java index 87219d2d9..b91ce2a1f 100644 --- a/src/org/infinity/resource/graphics/TisV2Decoder.java +++ b/src/org/infinity/resource/graphics/TisV2Decoder.java @@ -167,11 +167,9 @@ private void init() } tisBuffer = getResourceEntry().getResourceBuffer(); - String name = getResourceEntry().getResourceName(); - int idx = name.lastIndexOf('.'); - if (idx < 0) idx = name.length(); + String name = getResourceEntry().getResourceRef(); pvrzNameBase = getResourceEntry().getResourceName().substring(0, 1) + - getResourceEntry().getResourceName().substring(2, idx); + getResourceEntry().getResourceName().substring(2, name.length()); setType(Type.PVRZ); diff --git a/src/org/infinity/resource/itm/Viewer.java b/src/org/infinity/resource/itm/Viewer.java index 2a2b93e7b..d1d73bc2f 100644 --- a/src/org/infinity/resource/itm/Viewer.java +++ b/src/org/infinity/resource/itm/Viewer.java @@ -125,10 +125,7 @@ private static String getItemDialog(ItmResource itm) Table2da table = Table2daCache.get("itemdial.2da"); if (table != null && table.getColCount() > 2) { // getting item resref - String resref = itm.getResourceEntry().getResourceName(); - if (resref.lastIndexOf('.') > 0) { - resref = resref.substring(0, resref.lastIndexOf('.')); - } + String resref = itm.getResourceEntry().getResourceRef(); // fetching item dialog file, if available if (resref != null) { diff --git a/src/org/infinity/resource/itm/ViewerAbility.java b/src/org/infinity/resource/itm/ViewerAbility.java index d61280558..8b5f3b9c1 100644 --- a/src/org/infinity/resource/itm/ViewerAbility.java +++ b/src/org/infinity/resource/itm/ViewerAbility.java @@ -112,10 +112,7 @@ private static String getAbilityName(Ability ability) Table2da table = Table2daCache.get("tooltip.2da"); if (table != null) { // getting parent item resref - String resref = ability.getParent().getResourceEntry().getResourceName(); - if (resref.lastIndexOf('.') > 0) { - resref = resref.substring(0, resref.lastIndexOf('.')); - } + String resref = ability.getParent().getResourceEntry().getResourceRef(); // fetching tooltip label for ability, if available if (resref != null) { diff --git a/src/org/infinity/resource/key/BIFFResourceEntry.java b/src/org/infinity/resource/key/BIFFResourceEntry.java index dd322df2b..030710ff7 100644 --- a/src/org/infinity/resource/key/BIFFResourceEntry.java +++ b/src/org/infinity/resource/key/BIFFResourceEntry.java @@ -240,6 +240,12 @@ public String getResourceName() return resourceName + '.' + extension; } + @Override + public String getResourceRef() + { + return resourceName; + } + @Override public String getTreeFolderName() { diff --git a/src/org/infinity/resource/key/FileResourceEntry.java b/src/org/infinity/resource/key/FileResourceEntry.java index d43f73ec0..6e0ac03d5 100644 --- a/src/org/infinity/resource/key/FileResourceEntry.java +++ b/src/org/infinity/resource/key/FileResourceEntry.java @@ -103,6 +103,15 @@ public String getResourceName() return file.getFileName().toString(); } + public String getResourceRef() + { + String fileName = file.getFileName().toString(); + int pos = fileName.lastIndexOf('.'); + if (pos >= 0) + fileName = fileName.substring(0, pos); + return fileName; + } + @Override public String getTreeFolderName() { diff --git a/src/org/infinity/resource/key/ResourceEntry.java b/src/org/infinity/resource/key/ResourceEntry.java index 615a8ff01..6dc8f770c 100644 --- a/src/org/infinity/resource/key/ResourceEntry.java +++ b/src/org/infinity/resource/key/ResourceEntry.java @@ -254,7 +254,7 @@ public boolean isVisible() // 1. Options->Show Unknown Resource Types == true OR resource type is supported // 2. NOT Resource type part of skippedExtensions // 3. Filename length is valid - int resLen = getResourceName().lastIndexOf('.'); + int resLen = getResourceRef().length(); boolean bRet = (BrowserMenuBar.getInstance() != null && BrowserMenuBar.getInstance().showUnknownResourceTypes()) || Profile.isResourceTypeSupported(getExtension()) && !skippedExtensions.contains(getExtension().toUpperCase(Locale.ENGLISH)) && @@ -266,6 +266,7 @@ public boolean isVisible() public abstract long getResourceSize(boolean ignoreOverride); + /** Returns the type of the resource (extension without leading dot). */ public abstract String getExtension(); public abstract ByteBuffer getResourceBuffer(boolean ignoreOverride) throws Exception; @@ -274,8 +275,12 @@ public boolean isVisible() public abstract int[] getResourceInfo(boolean ignoreOverride) throws Exception; + /** Returns the full resource name (name dot extension) */ public abstract String getResourceName(); + /** Returns the resource name without extension. */ + public abstract String getResourceRef(); + /** Returns name of folder in the resource tree in which this entry appears. */ public abstract String getTreeFolderName(); diff --git a/src/org/infinity/resource/other/FntResource.java b/src/org/infinity/resource/other/FntResource.java index 226f3f250..df65d50e3 100644 --- a/src/org/infinity/resource/other/FntResource.java +++ b/src/org/infinity/resource/other/FntResource.java @@ -46,9 +46,7 @@ public void close() throws Exception @Override public int read(ByteBuffer buffer, int startoffset) throws Exception { - String resName = getResourceEntry().getResourceName(); - if (resName.lastIndexOf('.') > 0) - resName = resName.substring(0, resName.lastIndexOf('.')); + String resName = getResourceEntry().getResourceRef(); byte[] b = new byte[8]; System.arraycopy(resName.getBytes(), 0, b, 0, resName.length()); diff --git a/src/org/infinity/resource/sav/SavResourceEntry.java b/src/org/infinity/resource/sav/SavResourceEntry.java index ebd5b62db..bfee957a3 100644 --- a/src/org/infinity/resource/sav/SavResourceEntry.java +++ b/src/org/infinity/resource/sav/SavResourceEntry.java @@ -78,6 +78,16 @@ public String getResourceName() return fileName; } + @Override + public String getResourceRef() + { + int pos = fileName.lastIndexOf('.'); + if (pos >= 0) + return fileName.substring(0, pos); + else + return fileName; + } + @Override public String getExtension() { diff --git a/src/org/infinity/resource/spl/Viewer.java b/src/org/infinity/resource/spl/Viewer.java index 3c7d2ac83..a213f7ad2 100644 --- a/src/org/infinity/resource/spl/Viewer.java +++ b/src/org/infinity/resource/spl/Viewer.java @@ -47,14 +47,12 @@ public final class Viewer extends JPanel public static String getSymbolicName(ResourceEntry entry, boolean formatted) { if (entry != null) { - String resName = entry.getResourceName().toUpperCase(Locale.ENGLISH); - int idx = resName.lastIndexOf('.'); - String ext = (idx >= 0) ? resName.substring(idx+1) : ""; - String name = (idx >= 0) ? resName.substring(0, idx) : resName; + String ext = entry.getExtension().toUpperCase(Locale.ENGLISH); + String name = entry.getResourceRef().toUpperCase(Locale.ENGLISH); if ("SPL".equals(ext) && name.length() >= 7) { // fetching spell type - String s = resName.substring(0, 4); + String s = name.substring(0, 4); int type = 0; if (SpellType.containsKey(s)) { type = SpellType.get(s).intValue(); diff --git a/src/org/infinity/resource/video/MveResource.java b/src/org/infinity/resource/video/MveResource.java index c1ff46927..98365b064 100644 --- a/src/org/infinity/resource/video/MveResource.java +++ b/src/org/infinity/resource/video/MveResource.java @@ -322,12 +322,7 @@ private static void exportAsAvi(ResourceEntry inEntry, Window parent) if (inEntry != null) { JFileChooser fc = new JFileChooser(Profile.getGameRoot().toFile()); fc.setDialogTitle("Export MVE as AVI"); - String name = inEntry.getResourceName(); - if (name.lastIndexOf('.') > 0) { - name = name.substring(0, name.lastIndexOf('.')) + ".avi"; - } else { - name = name + ".avi"; - } + String name = inEntry.getResourceRef() + ".avi"; fc.setSelectedFile(new File(fc.getCurrentDirectory(), name)); fc.setDialogType(JFileChooser.SAVE_DIALOG); fc.setFileSelectionMode(JFileChooser.FILES_ONLY); diff --git a/src/org/infinity/resource/video/WbmResource.java b/src/org/infinity/resource/video/WbmResource.java index c94412414..5361c20cd 100644 --- a/src/org/infinity/resource/video/WbmResource.java +++ b/src/org/infinity/resource/video/WbmResource.java @@ -167,18 +167,12 @@ private Path getVideoFile() retVal = ((FileResourceEntry)entry).getActualPath(); isTempFile = false; } else { - String fileName = entry.getResourceName(); - String fileBase, fileExt; - int p = fileName.lastIndexOf('.'); - if (p > 0) { - fileBase = fileName.substring(0, p); - fileExt = fileName.substring(p); - } else { - fileBase = fileName; - fileExt = ".wbm"; - } + String fileBase = entry.getResourceRef(); + String fileExt = entry.getExtension(); + if (fileExt.isEmpty()) + fileExt = "wbm"; try { - Path outFile = Files.createTempFile(fileBase + "-", fileExt); + Path outFile = Files.createTempFile(fileBase + "-", "." + fileExt); if (Files.isRegularFile(outFile)) { try (InputStream is = entry.getResourceDataAsStream()) { try (OutputStream os = StreamUtils.getOutputStream(outFile, true)) { diff --git a/src/org/infinity/search/ScriptReferenceSearcher.java b/src/org/infinity/search/ScriptReferenceSearcher.java index f93210fbc..570e8366c 100644 --- a/src/org/infinity/search/ScriptReferenceSearcher.java +++ b/src/org/infinity/search/ScriptReferenceSearcher.java @@ -79,11 +79,7 @@ else if (o instanceof AbstractCode) { private void searchText(ResourceEntry entry, String text) { - String name = targetEntry.getResourceName(); - int idx = name.lastIndexOf('.');//TODO: add special method to get name without extension - if (idx > 0) { - name = name.substring(0, idx); - } + String name = targetEntry.getResourceRef(); final Pattern p = Pattern.compile("\\b" + name + "\\b", Pattern.CASE_INSENSITIVE); final Matcher m = p.matcher(text); if (m.find()) { @@ -93,11 +89,7 @@ private void searchText(ResourceEntry entry, String text) private void searchScript(ResourceEntry entry, String script, StructEntry ref) { - String name = targetEntry.getResourceName(); - int idx = name.lastIndexOf('.');//TODO: add special method to get name without extension - if (idx > 0) { - name = name.substring(0, idx); - } + String name = targetEntry.getResourceRef(); final Pattern p = Pattern.compile("\"" + name + "\"", Pattern.CASE_INSENSITIVE); final Matcher m = p.matcher(script); if (m.find()) { diff --git a/src/org/infinity/search/WavReferenceSearcher.java b/src/org/infinity/search/WavReferenceSearcher.java index e5cc7af0b..1d1b1f1d6 100644 --- a/src/org/infinity/search/WavReferenceSearcher.java +++ b/src/org/infinity/search/WavReferenceSearcher.java @@ -54,15 +54,11 @@ else if (o instanceof AbstractStruct) { private void searchText(ResourceEntry entry, PlainTextResource text) { - String name = getTargetEntry().getResourceName(); - int idx = name.lastIndexOf('.'); - if (idx > 0) { - String nameBase = name.substring(0, idx); - Pattern p = Pattern.compile("\\b" + nameBase + "\\b", Pattern.CASE_INSENSITIVE); - Matcher m = p.matcher(text.getText()); - if (m.find()) { - addHit(entry, null, null); - } + String nameBase = getTargetEntry().getResourceRef(); + Pattern p = Pattern.compile("\\b" + nameBase + "\\b", Pattern.CASE_INSENSITIVE); + Matcher m = p.matcher(text.getText()); + if (m.find()) { + addHit(entry, null, null); } } } From 9cff35def74916e4fcb66ec22631f9c50490db0f Mon Sep 17 00:00:00 2001 From: Argent77 <4519923+Argent77@users.noreply.github.com> Date: Tue, 21 Apr 2020 15:01:35 +0200 Subject: [PATCH 06/25] Experimental: Change method how transparent palette indices in BAM V1 resources are determined - Treat palette index 0 and any palette indices matching RGB(0,255,0) as transparent. - Restrictions: BAM Converter and BAM frame export support only one transparent palette index (non-zero index preferred). --- .../viewer/BackgroundAnimationProvider.java | 3 - .../resource/graphics/BamResource.java | 1 - .../resource/graphics/BamV1Decoder.java | 88 +++++-------------- 3 files changed, 21 insertions(+), 71 deletions(-) diff --git a/src/org/infinity/resource/are/viewer/BackgroundAnimationProvider.java b/src/org/infinity/resource/are/viewer/BackgroundAnimationProvider.java index c4b9e1856..baf4b5dc2 100644 --- a/src/org/infinity/resource/are/viewer/BackgroundAnimationProvider.java +++ b/src/org/infinity/resource/are/viewer/BackgroundAnimationProvider.java @@ -75,9 +75,6 @@ public void setAnimation(BamDecoder bam) this.control = this.bam.createControl(); this.control.setMode(BamDecoder.BamControl.Mode.INDIVIDUAL); this.control.setSharedPerCycle(!isMultiPart()); - if (this.control instanceof BamV1Decoder.BamV1Control) { - ((BamV1Decoder.BamV1Control)this.control).setTransparencyMode(BamV1Decoder.TransparencyMode.NORMAL); - } resetFrame(); updateCanvas(); diff --git a/src/org/infinity/resource/graphics/BamResource.java b/src/org/infinity/resource/graphics/BamResource.java index 0d61bf2c2..a971a10f2 100644 --- a/src/org/infinity/resource/graphics/BamResource.java +++ b/src/org/infinity/resource/graphics/BamResource.java @@ -154,7 +154,6 @@ public BamResource(ResourceEntry entry) bamControl.setMode(BamDecoder.BamControl.Mode.SHARED); if (bamControl instanceof BamV1Decoder.BamV1Control) { ((BamV1Decoder.BamV1Control)bamControl).setTransparencyEnabled(transparencyEnabled); - ((BamV1Decoder.BamV1Control)bamControl).setTransparencyMode(BamV1Decoder.TransparencyMode.NORMAL); } } catch (Throwable t) { t.printStackTrace(); diff --git a/src/org/infinity/resource/graphics/BamV1Decoder.java b/src/org/infinity/resource/graphics/BamV1Decoder.java index 4540232b1..b6ca62f73 100644 --- a/src/org/infinity/resource/graphics/BamV1Decoder.java +++ b/src/org/infinity/resource/graphics/BamV1Decoder.java @@ -25,15 +25,6 @@ */ public class BamV1Decoder extends BamDecoder { - /** - * Definitions on how to handle palette transparency:
- * {@code Normal} looks for the first entry containing RGB(0, 255, 0). It falls back to palette - * index 0 if no entry has been found.
- * {@code FirstIndexOnly} automatically uses palette index 0 without looking for entries - * containing RGB(0, 255, 0). - */ - public enum TransparencyMode { NORMAL, FIRST_INDEX_ONLY } - private final List listFrames = new ArrayList(); private final List listCycles = new ArrayList(); private final BamV1FrameEntry defaultFrameInfo = new BamV1FrameEntry(null, 0); @@ -383,7 +374,6 @@ public static class BamV1Control extends BamControl { private int[] currentPalette, externalPalette; private boolean transparencyEnabled; - private TransparencyMode transparencyMode; private int currentCycle, currentFrame; protected BamV1Control(BamV1Decoder decoder) @@ -411,37 +401,14 @@ public void setTransparencyEnabled(boolean enable) } } - /** - * Returns the currently used transparency mode for palettes. - */ - public TransparencyMode getTransparencyMode() - { - return transparencyMode; - } - - /** - * Sets the mode on how to handle transparency in palettes. - * @param transparencyMode The transparency mode to set. - */ - public void setTransparencyMode(TransparencyMode transparencyMode) - { - if (transparencyMode != null) { - if (this.transparencyMode != transparencyMode) { - this.transparencyMode = transparencyMode; - preparePalette(externalPalette); - } - } - } - /** Returns the transparency index of the current palette. */ public int getTransparencyIndex() { - for (int i = 0; i < currentPalette.length; i++) { - if ((currentPalette[i] & 0xff000000) == 0) { - return i; - } - } - return 0; + int idx = currentPalette.length - 1; + for (; idx > 0; idx--) + if ((currentPalette[idx] & 0xff000000) == 0) + break; + return idx; } /** Returns whether the palette makes use of alpha transparency. */ @@ -653,11 +620,6 @@ public int cycleGetFrameIndexAbsolute(int cycleIdx, int frameIdx) private void init() { transparencyEnabled = true; - if (Profile.getEngine() == Profile.Engine.BG1 || Profile.getEngine() == Profile.Engine.PST) { - transparencyMode = TransparencyMode.NORMAL; - } else { - transparencyMode = TransparencyMode.FIRST_INDEX_ONLY; - } currentPalette = null; externalPalette = null; currentCycle = currentFrame = 0; @@ -670,16 +632,15 @@ private void init() // Prepares the palette to be used for decoding BAM frames private void preparePalette(int[] externalPalette) { - if (currentPalette == null) { + if (currentPalette == null) currentPalette = new int[256]; - } // some optimizations: don't prepare if the palette hasn't changed - boolean isNormalMode = (getTransparencyMode() == TransparencyMode.NORMAL); int idx = 0; - int transIndex = -1; + List transIndices = new ArrayList<>(); // multiple transparent palette indices are supported int alphaMask = Profile.isEnhancedEdition() ? 0 : 0xff000000; boolean alphaUsed = false; // determines whether alpha is actually used + if (externalPalette != null) { // filling palette entries from external palette, as much as possible for (; idx < externalPalette.length && idx < 256; idx++) { @@ -688,44 +649,37 @@ private void preparePalette(int[] externalPalette) currentPalette[idx] |= alphaMask; } alphaUsed |= (currentPalette[idx] & 0xff000000) != 0; - if (isNormalMode && transIndex < 0 && (currentPalette[idx] & 0x00ffffff) == 0x0000ff00) { - transIndex = idx; - } + if (idx == 0 || (currentPalette[idx] & 0x00ffffff) == 0x0000ff00) + transIndices.add(idx); } } - // filling remaining entries with BAM palette + if (getDecoder().bamPalette != null) { + // filling remaining entries with BAM palette for (; idx < getDecoder().bamPalette.length; idx++) { currentPalette[idx] = getDecoder().bamPalette[idx]; if ((currentPalette[idx] & 0xff000000) == 0) { currentPalette[idx] |= alphaMask; } alphaUsed |= (currentPalette[idx] & 0xff000000) != 0; - if (isNormalMode && transIndex < 0 && (currentPalette[idx] & 0x00ffffff) == 0x0000ff00) { - transIndex = idx; - } + if (idx == 0 || (currentPalette[idx] & 0x00ffffff) == 0x0000ff00) + transIndices.add(idx); } } - // removing alpha support if needed if (!alphaUsed) { + // discarding alpha for (int i = 0; i < currentPalette.length; i++) { currentPalette[i] |= 0xff000000; } } - // applying transparent index - if (isNormalMode && transIndex >= 0) { - if (transparencyEnabled) { - currentPalette[transIndex] = 0; - } else { - currentPalette[transIndex] |= 0xff000000; - } - } - - // falling back to transparency at color index 0 - if (transparencyEnabled && transIndex < 0) { - currentPalette[0] = 0; + // applying transparent indices + for (int i : transIndices) { + if (transparencyEnabled) + currentPalette[i] = 0; + else + currentPalette[i] |= 0xff000000; } } } From 44a8fcdc01e0541e77dd2b0a62ce6d2f99d1bc67 Mon Sep 17 00:00:00 2001 From: Argent77 <4519923+Argent77@users.noreply.github.com> Date: Tue, 21 Apr 2020 15:24:16 +0200 Subject: [PATCH 07/25] BAM decoder: Fail gracefully on RLE decoding errors --- .../resource/graphics/BamV1Decoder.java | 44 +++++++++++-------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/src/org/infinity/resource/graphics/BamV1Decoder.java b/src/org/infinity/resource/graphics/BamV1Decoder.java index b6ca62f73..2b6ff9466 100644 --- a/src/org/infinity/resource/graphics/BamV1Decoder.java +++ b/src/org/infinity/resource/graphics/BamV1Decoder.java @@ -291,28 +291,34 @@ private void decodeFrame(BamControl control, int frameIdx, Image canvas) srcOfs = ofsData; dstOfs = 0; } - for (int y = 0; y < maxHeight; y++) { - for (int x = 0; x < srcWidth; x++, dstOfs++) { - if (count > 0) { - // writing remaining RLE compressed pixels - count--; - if (x < maxWidth) { - if (bufferB != null) bufferB[dstOfs] = pixel; - if (bufferI != null) bufferI[dstOfs] = color; - } - } else { - pixel = bamBuffer.get(srcOfs++); - color = palette[pixel & 0xff]; - if (isCompressed && (pixel & 0xff) == rleIndex) { - count = bamBuffer.get(srcOfs++) & 0xff; - } - if (x < maxWidth) { - if (bufferB != null) bufferB[dstOfs] = pixel; - if (bufferI != null) bufferI[dstOfs] = color; + try { + for (int y = 0; y < maxHeight; y++) { + for (int x = 0; x < srcWidth; x++, dstOfs++) { + if (count > 0) { + // writing remaining RLE compressed pixels + count--; + if (x < maxWidth) { + if (bufferB != null) bufferB[dstOfs] = pixel; + if (bufferI != null) bufferI[dstOfs] = color; + } + } else { + pixel = bamBuffer.get(srcOfs++); + color = palette[pixel & 0xff]; + if (isCompressed && (pixel & 0xff) == rleIndex) { + count = bamBuffer.get(srcOfs++) & 0xff; + } + if (x < maxWidth) { + if (bufferB != null) bufferB[dstOfs] = pixel; + if (bufferI != null) bufferI[dstOfs] = color; + } } } + dstOfs += dstWidth - srcWidth; } - dstOfs += dstWidth - srcWidth; + } catch (Exception e) { + System.err.printf("Error [%s]: input (offset=%d, size=%d), output (offset=%d, size=%d)\n", + e.getClass().getName(), srcOfs, bamBuffer.limit(), dstOfs, + bufferB != null ? bufferB.length : bufferI.length); } bufferB = null; bufferI = null; From ddaa431b7be2d90242eb94d1ec1d894ef6bf2edb Mon Sep 17 00:00:00 2001 From: Argent77 <4519923+Argent77@users.noreply.github.com> Date: Tue, 21 Apr 2020 16:31:42 +0200 Subject: [PATCH 08/25] Add description to ITM Ability flags bit 9 --- src/org/infinity/resource/itm/Ability.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/infinity/resource/itm/Ability.java b/src/org/infinity/resource/itm/Ability.java index 9ae9fd040..39addec09 100644 --- a/src/org/infinity/resource/itm/Ability.java +++ b/src/org/infinity/resource/itm/Ability.java @@ -52,7 +52,7 @@ public final class Ability extends AbstractAbility implements AddRemovable, HasA public static final String[] s_abilityuse = {"", "Weapon", "Spell", "Item", "Ability", "reserved"}; public static final String[] s_recharge = { "No flags set", "Add strength bonus", "Breakable", "EE: Damage strength bonus", - "EE: THAC0 strength bonus", null, null, null, null, null, null, + "EE: THAC0 strength bonus", null, null, null, null, null, "EE: Break Sanctuary;Ignored for Target: Caster", "Hostile", "Recharge after resting", null, null, null, null, "Bypass armor", "Keen edge", null, null, null, null, null, null, null, "Ex: Toggle backstab", "EE/Ex: Cannot target invisible"}; From ffaf80a36144f09e73e8098ca0f9fba013b78e65 Mon Sep 17 00:00:00 2001 From: Argent77 <4519923+Argent77@users.noreply.github.com> Date: Wed, 22 Apr 2020 11:49:38 +0200 Subject: [PATCH 09/25] Add option to colorize substructures and associated offset and count fields in structured resources Replaces option "Colorized offset fields". --- src/org/infinity/gui/BIFFEditor.java | 2 +- src/org/infinity/gui/BrowserMenuBar.java | 14 +-- src/org/infinity/gui/StructViewer.java | 98 ++++++++++++++----- src/org/infinity/gui/ViewerUtil.java | 20 ++++ src/org/infinity/resource/AbstractStruct.java | 17 +++- .../resource/dlg/DlgTreeCellRenderer.java | 20 +--- .../infinity/resource/text/QuestsPanel.java | 4 +- .../infinity/resource/wed/IndexNumber.java | 26 +++++ src/org/infinity/resource/wed/Overlay.java | 4 +- .../infinity/resource/wed/WedResource.java | 4 +- 10 files changed, 154 insertions(+), 55 deletions(-) create mode 100644 src/org/infinity/resource/wed/IndexNumber.java diff --git a/src/org/infinity/gui/BIFFEditor.java b/src/org/infinity/gui/BIFFEditor.java index f3f9371f6..27b4cf8e4 100644 --- a/src/org/infinity/gui/BIFFEditor.java +++ b/src/org/infinity/gui/BIFFEditor.java @@ -371,7 +371,7 @@ private void setProgress(int level, boolean ok) if (ok) boxes[level - 1].setSelected(true); else - boxes[level - 1].setForeground(Color.red); + boxes[level - 1].setForeground(Color.RED); bok.setEnabled(level == boxes.length || !ok); } diff --git a/src/org/infinity/gui/BrowserMenuBar.java b/src/org/infinity/gui/BrowserMenuBar.java index 96ffaa228..229bfbcdf 100644 --- a/src/org/infinity/gui/BrowserMenuBar.java +++ b/src/org/infinity/gui/BrowserMenuBar.java @@ -343,7 +343,7 @@ public boolean showDlgTechInfo() public boolean getColoredOffsetsEnabled() { - return optionsMenu.optionShowColoredOffsets.isSelected(); + return optionsMenu.optionShowColoredStructures.isSelected(); } public boolean getHexColorMapEnabled() @@ -1756,7 +1756,7 @@ private static final class OptionsMenu extends JMenu implements ActionListener, private static final String OPTION_CACHEOVERRIDE = "CacheOverride"; private static final String OPTION_MORECOMPILERWARNINGS = "MoreCompilerWarnings"; private static final String OPTION_SHOWSTRREFS = "ShowStrrefs"; - private static final String OPTION_SHOWCOLOREDOFFSETS = "ShowColoredOffsets"; + private static final String OPTION_SHOWCOLOREDSTRUCTURES = "ShowColoredStructures"; private static final String OPTION_SHOWHEXCOLORED = "ShowHexColored"; private static final String OPTION_KEEPVIEWONCOPY = "UpdateTreeOnCopy"; private static final String OPTION_SHOWTREESEARCHNAMES = "ShowTreeSearchNames"; @@ -1832,7 +1832,7 @@ private static final class OptionsMenu extends JMenu implements ActionListener, private JCheckBoxMenuItem optionBackupOnSave, optionShowOffset, optionIgnoreOverride, optionIgnoreReadErrors, optionCacheOverride, optionShowStrrefs, - optionShowColoredOffsets, optionShowHexColored, optionShowUnknownResources, + optionShowColoredStructures, optionShowHexColored, optionShowUnknownResources, optionKeepViewOnCopy, optionTreeSearchNames, optionHighlightOverridden; // optionMonitorFileChanges; @@ -1900,9 +1900,9 @@ private OptionsMenu() optionShowStrrefs = new JCheckBoxMenuItem("Show Strrefs in View tabs", getPrefs().getBoolean(OPTION_SHOWSTRREFS, false)); add(optionShowStrrefs); - optionShowColoredOffsets = - new JCheckBoxMenuItem("Show colored offset fields in Edit tabs", getPrefs().getBoolean(OPTION_SHOWCOLOREDOFFSETS, true)); - add(optionShowColoredOffsets); + optionShowColoredStructures = + new JCheckBoxMenuItem("Show colored structures in Edit tabs", getPrefs().getBoolean(OPTION_SHOWCOLOREDSTRUCTURES, true)); + add(optionShowColoredStructures); optionShowHexColored = new JCheckBoxMenuItem("Show colored blocks in Raw tabs", getPrefs().getBoolean(OPTION_SHOWHEXCOLORED, true)); add(optionShowHexColored); @@ -2523,7 +2523,7 @@ private void storePreferences() getPrefs().putBoolean(OPTION_MORECOMPILERWARNINGS, optionMoreCompileWarnings.isSelected()); getPrefs().putBoolean(OPTION_SHOWSTRREFS, optionShowStrrefs.isSelected()); dialogViewerMenu.storePreferences(getPrefs()); - getPrefs().putBoolean(OPTION_SHOWCOLOREDOFFSETS, optionShowColoredOffsets.isSelected()); + getPrefs().putBoolean(OPTION_SHOWCOLOREDSTRUCTURES, optionShowColoredStructures.isSelected()); getPrefs().putBoolean(OPTION_SHOWHEXCOLORED, optionShowHexColored.isSelected()); getPrefs().putBoolean(OPTION_KEEPVIEWONCOPY, optionKeepViewOnCopy.isSelected()); getPrefs().putBoolean(OPTION_SHOWTREESEARCHNAMES, optionTreeSearchNames.isSelected()); diff --git a/src/org/infinity/gui/StructViewer.java b/src/org/infinity/gui/StructViewer.java index 8a7720b22..0ecefa35a 100644 --- a/src/org/infinity/gui/StructViewer.java +++ b/src/org/infinity/gui/StructViewer.java @@ -29,6 +29,7 @@ import java.awt.print.PrinterJob; import java.nio.ByteBuffer; import java.util.HashMap; +import java.util.Map; import javax.swing.BorderFactory; import javax.swing.Icon; @@ -134,10 +135,12 @@ public final class StructViewer extends JPanel implements ListSelectionListener, private static final String CARD_EDIT = "Edit"; private static final String CARD_TEXT = "Text"; + private static Class lastNameStruct, lastIndexStruct; private static String lastName; private static int lastIndex; private final AbstractStruct struct; + private final Map, Color> fieldColors = new HashMap<>(); private final CardLayout cards = new CardLayout(); private final JMenuItem miCopyValue = createMenuItem(CMD_COPYVALUE, "Copy value", Icons.getIcon(Icons.ICON_COPY_16), this); private final JMenuItem miPasteValue = createMenuItem(CMD_PASTEVALUE, "Replace value", Icons.getIcon(Icons.ICON_PASTE_16), this); @@ -224,9 +227,12 @@ public void mouseClicked(MouseEvent e) Object selected = table.getModel().getValueAt(table.getSelectedRow(), 1); if (selected instanceof Viewable) { createViewFrame(table.getTopLevelAncestor(), (Viewable)selected); - } else - if (selected instanceof SectionOffset) { - selectOffset((SectionOffset)selected); + } + else if (selected instanceof SectionOffset) { + selectFirstEntryOfType(((SectionOffset)selected).getSection()); + } + else if (selected instanceof SectionCount) { + selectFirstEntryOfType(((SectionCount)selected).getSection()); } } } @@ -239,9 +245,18 @@ public Component getTableCellRendererComponent(JTable table, Object value, int column) { final StructEntry field = (StructEntry)table.getModel().getValueAt(row, 1); - final boolean isColored = BrowserMenuBar.getInstance().getColoredOffsetsEnabled() && - field instanceof SectionOffset; - setBackground(isColored ? Color.cyan : null); + Class cls = null; + if (BrowserMenuBar.getInstance().getColoredOffsetsEnabled()) { + if (field instanceof SectionOffset) + cls = ((SectionOffset)field).getSection(); + else if (field instanceof SectionCount) + cls = ((SectionCount)field).getSection(); + else if (field instanceof AbstractStruct) + cls = field.getClass(); + else if (fieldColors.containsKey(field.getClass())) // consider only referenced simple field types + cls = field.getClass(); + } + setBackground(getClassColor(cls)); super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); if (column == 2) @@ -565,7 +580,14 @@ public void actionPerformed(ActionEvent event) } else if (CMD_RESET.equals(cmd)) { convertAttribute(min, miReset); } else if (CMD_GOTO_OFFSET.equals(cmd)) { - selectOffset((SectionOffset)table.getValueAt(min, 1)); + final StructEntry se = (StructEntry)table.getValueAt(min, 1); + Class cls = null; + if (se instanceof SectionOffset) + cls = ((SectionOffset)se).getSection(); + else if (se instanceof SectionCount) + cls = ((SectionCount)se).getSection(); + if (cls != null) + selectFirstEntryOfType(cls); } else if (CMD_SHOW_IN_TREE.equals(cmd)) { // this should only be available for DlgResources final DlgResource dlgRes = (DlgResource) struct; @@ -765,7 +787,7 @@ public void valueChanged(ListSelectionEvent event) miReset.setEnabled(isDataType && isReadable && getCachedStructEntry(((Datatype)selected).getOffset()) instanceof Readable && !(selected instanceof AbstractCode)); - miGotoOffset.setEnabled(selected instanceof SectionOffset); + miGotoOffset.setEnabled(selected instanceof SectionOffset|| selected instanceof SectionCount); final boolean isSpecialDlgTreeItem = (selected instanceof State || selected instanceof Transition); final boolean isSpecialDlgStruct = isSpecialDlgTreeItem @@ -1361,26 +1383,58 @@ private void select(StructEntry field) } } +// /** +// * Selects in the table field that corresponds to the specified offset entry +// * or opens new child structure viewer, if corresponding field not in this table +// * +// * @param entry Offset to show +// */ +// private void selectOffset(SectionOffset entry) +// { +// // Select entry at offset +// final int offset = entry.getValue(); +// final StructEntry field = struct.getAttribute(offset, entry.getSection()); +// if (field != null) { +// final AbstractStruct parent = field.getParent(); +// if (parent != struct) { +// new ViewFrame(this, parent); +// } +// parent.getViewer().select(field); +// } +// } + /** - * Selects in the table field that corresponds to the specified offset entry - * or opens new child structure viewer, if corresponding field not in this table - * - * @param entry Offset to show + * Selects the first structure of the specified class type. + * @param cls Class of the structure to search. */ - private void selectOffset(SectionOffset entry) + private void selectFirstEntryOfType(Class cls) { - // Select entry at offset - final int offset = entry.getValue(); - final StructEntry field = struct.getAttribute(offset, entry.getSection()); - if (field != null) { - final AbstractStruct parent = field.getParent(); - if (parent != struct) { - new ViewFrame(this, parent); + if (cls != null) { + final StructEntry field = struct.getField(cls, 0); + if (field != null) { + final AbstractStruct parent = field.getParent(); + if (parent != struct ) { + new ViewFrame(this, parent); + } + parent.getViewer().select(field); } - parent.getViewer().select(field); } } + /** + * Returns the color associated with the specified class type. Returns Color.WHITE if no class type specified. + * @param cls The class associated with the field value. + * @return Color corresponding to the specified field class type. {@code Color.WHITE} by default. + */ + private Color getClassColor(Class cls) + { + if (cls != null) { + return fieldColors.computeIfAbsent(cls, + c -> ViewerUtil.BACKGROUND_COLORS[fieldColors.size() % ViewerUtil.BACKGROUND_COLORS.length]); + } + return Color.WHITE; + } + // -------------------------- INNER CLASSES -------------------------- private final class PopupListener extends MouseAdapter @@ -1414,7 +1468,7 @@ private StructTable() public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException { Graphics2D g2 = (Graphics2D)graphics; - g2.setColor(Color.black); + g2.setColor(Color.BLACK); int fontHeight = g2.getFontMetrics().getHeight(); int fontDesent = g2.getFontMetrics().getDescent(); diff --git a/src/org/infinity/gui/ViewerUtil.java b/src/org/infinity/gui/ViewerUtil.java index af0a2b91e..c00af3f6e 100644 --- a/src/org/infinity/gui/ViewerUtil.java +++ b/src/org/infinity/gui/ViewerUtil.java @@ -5,6 +5,7 @@ package org.infinity.gui; import java.awt.BorderLayout; +import java.awt.Color; import java.awt.Component; import java.awt.Cursor; import java.awt.Dimension; @@ -65,6 +66,25 @@ public final class ViewerUtil { + /** + * A collection of pastel shaded colors that can be used to colorize the background of + * list, table or tree items. + */ + public static final Color[] BACKGROUND_COLORS = { + new Color(0xd8d8ff), + new Color(0xb8ffb8), + new Color(0xffd0d0), + new Color(0xffc8ff), + new Color(0xffffa0), + new Color(0x85ffc2), + new Color(0xffd3a6), + new Color(0x99ccff), + new Color(0xffa3d1), + new Color(0xa3fff8), + new Color(0xb5a3ff), + new Color(0xe0e0e0), + }; + public static void addLabelFieldPair(JPanel panel, StructEntry entry, GridBagLayout gbl, GridBagConstraints gbc, boolean endline) { diff --git a/src/org/infinity/resource/AbstractStruct.java b/src/org/infinity/resource/AbstractStruct.java index b58166c2b..9747ada70 100644 --- a/src/org/infinity/resource/AbstractStruct.java +++ b/src/org/infinity/resource/AbstractStruct.java @@ -411,7 +411,7 @@ public void write(OutputStream os) throws IOException public String toString() { // limit text length to speed things up - int capacity = 160; + int capacity = 256; final StringBuilder sb = new StringBuilder(capacity); for (int i = 0, count = fields.size(); i < count; i++) { final StructEntry field = fields.get(i); @@ -759,6 +759,21 @@ public List getFields(Class type) .collect(Collectors.toList())); } + /** + * Returns the first {@code StructEntry} object of the specified class type. + * @param type Class of the {@code StructEntry} object to return. + * @param offset Start offset to search {@code StructEntry} instances. + * @return First available {@code StructEntry} instance, {@code null} otherwise. + */ + public StructEntry getField(Class type, int offset) + { + return fields + .stream() + .filter(se -> se.getOffset() >= offset && (type == null || type.isAssignableFrom(se.getClass()))) + .findFirst() + .orElse(null); + } + /** * Returns the StructEntry object at the specified index. * @param index The index of the desired StructEntry object. diff --git a/src/org/infinity/resource/dlg/DlgTreeCellRenderer.java b/src/org/infinity/resource/dlg/DlgTreeCellRenderer.java index e74eb45db..74aba78c5 100644 --- a/src/org/infinity/resource/dlg/DlgTreeCellRenderer.java +++ b/src/org/infinity/resource/dlg/DlgTreeCellRenderer.java @@ -12,6 +12,7 @@ import javax.swing.tree.DefaultTreeCellRenderer; import org.infinity.gui.BrowserMenuBar; +import org.infinity.gui.ViewerUtil; /** * Renderer for dialogue tree, drawing elements of each dialog with its own color @@ -25,23 +26,6 @@ */ final class DlgTreeCellRenderer extends DefaultTreeCellRenderer { - /** - * This array contains background colors for other dialogs to which viewed dialog - * refers. Colors are assigned to other resources from this array on rotation basis. - */ - private static final Color[] OTHER_DIALOG_COLORS = { - new Color(0xd8d8ff), - new Color(0xb8ffb8), - new Color(0xffd0d0), - new Color(0xffc8ff), - new Color(0xffffa0), - new Color(0xe0e0e0), - new Color(0x85ffc2), - new Color(0xffd3a6), - new Color(0x99ccff), - new Color(0xffa3d1), - }; - /** Background colors for text in dialogs to that can refer main dialog. */ private final HashMap dialogColors = new HashMap<>(); /** Main dialogue that shown in the tree. */ @@ -91,7 +75,7 @@ private Color getColor(DlgResource dialog) return null; } return dialogColors.computeIfAbsent(dialog, - d -> OTHER_DIALOG_COLORS[dialogColors.size() % OTHER_DIALOG_COLORS.length] + d -> ViewerUtil.BACKGROUND_COLORS[dialogColors.size() % ViewerUtil.BACKGROUND_COLORS.length] ); } } diff --git a/src/org/infinity/resource/text/QuestsPanel.java b/src/org/infinity/resource/text/QuestsPanel.java index 17f7a9f32..469789180 100644 --- a/src/org/infinity/resource/text/QuestsPanel.java +++ b/src/org/infinity/resource/text/QuestsPanel.java @@ -157,7 +157,7 @@ private class CompletedCellRenderer extends DefaultTableCellRenderer protected void setupCompleted(boolean isSelected) { if (!isSelected) { - setBackground(Color.cyan); + setBackground(Color.CYAN); } final Font font = getFont(); // Because of capture (symbol ?) in type signature raw type usage is required... @@ -189,7 +189,7 @@ public Component getTableCellRendererComponent(JTable table, Object value, boole final QuestsModel model = (QuestsModel)table.getModel(); final Quest quest = model.quests.get(row); switch (quest.evaluate(vars)) { - case Unassigned: setForeground(Color.gray); break; + case Unassigned: setForeground(Color.GRAY); break; case Completed: setupCompleted(isSelected); break; default: break; diff --git a/src/org/infinity/resource/wed/IndexNumber.java b/src/org/infinity/resource/wed/IndexNumber.java new file mode 100644 index 000000000..3e71a81bd --- /dev/null +++ b/src/org/infinity/resource/wed/IndexNumber.java @@ -0,0 +1,26 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2020 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.wed; + +import java.nio.ByteBuffer; + +import org.infinity.datatype.DecNumber; + +/** + * Subclassed from {@code DecNumber} to make field type identifiable by reflection. + */ +public class IndexNumber extends DecNumber +{ + public IndexNumber(ByteBuffer buffer, int offset, int length, String name) + { + super(buffer, offset, length, name); + } + + public IndexNumber(ByteBuffer buffer, int offset, int length, String name, boolean signed) + { + super(buffer, offset, length, name, signed); + } + +} diff --git a/src/org/infinity/resource/wed/Overlay.java b/src/org/infinity/resource/wed/Overlay.java index dbdc2b3fd..4da9f974e 100644 --- a/src/org/infinity/resource/wed/Overlay.java +++ b/src/org/infinity/resource/wed/Overlay.java @@ -63,7 +63,7 @@ public int read(ByteBuffer buffer, int offset) throws Exception } final SectionOffset offset_tilemap = new SectionOffset(buffer, offset + 16, WED_OVERLAY_OFFSET_TILEMAP, Tilemap.class); addField(offset_tilemap); - final SectionOffset offset_tilelookup = new SectionOffset(buffer, offset + 20, WED_OVERLAY_OFFSET_TILEMAP_LOOKUP, DecNumber.class); + final SectionOffset offset_tilelookup = new SectionOffset(buffer, offset + 20, WED_OVERLAY_OFFSET_TILEMAP_LOOKUP, IndexNumber.class); addField(offset_tilelookup); int retoff = offset + 24; @@ -82,7 +82,7 @@ public int read(ByteBuffer buffer, int offset) throws Exception // readLookuptable offset = offset_tilelookup.getValue(); for (int i = 0; i < lookuptablesize; i++) { - addField(new DecNumber(buffer, offset + i * 2, 2, WED_OVERLAY_TILEMAP_INDEX + " " + i)); + addField(new IndexNumber(buffer, offset + i * 2, 2, WED_OVERLAY_TILEMAP_INDEX + " " + i)); } return retoff; } diff --git a/src/org/infinity/resource/wed/WedResource.java b/src/org/infinity/resource/wed/WedResource.java index 915395fa3..5d10ef271 100644 --- a/src/org/infinity/resource/wed/WedResource.java +++ b/src/org/infinity/resource/wed/WedResource.java @@ -259,7 +259,7 @@ public int read(ByteBuffer buffer, int offset) throws Exception Wallgroup.class); addField(offsetWallgroups); SectionOffset offsetPolytable = new SectionOffset(buffer, offset + 16, WED_OFFSET_WALL_POLYGON_LOOKUP, - RemovableDecNumber.class); + IndexNumber.class); addField(offsetPolytable); HexNumber offsets[] = new HexNumber[]{offsetOverlays, offsetHeader2, offsetDoors, offsetDoortile, @@ -306,7 +306,7 @@ public int compare(HexNumber s1, HexNumber s2) offset = offsetPolytable.getValue(); for (int i = 0; i < countPolytable; i++) { - addField(new DecNumber(buffer, offset + i * 2, 2, WED_WALL_POLYGON_INDEX + " " + i)); + addField(new IndexNumber(buffer, offset + i * 2, 2, WED_WALL_POLYGON_INDEX + " " + i)); } int endoffset = offset; From 2c4dbb116fba41705ae9da3b699a98ccf4938cc7 Mon Sep 17 00:00:00 2001 From: Argent77 <4519923+Argent77@users.noreply.github.com> Date: Wed, 22 Apr 2020 12:34:03 +0200 Subject: [PATCH 10/25] Repaint viewer when toggling "Colorize structures" option --- src/org/infinity/NearInfinity.java | 11 +++++++++++ src/org/infinity/gui/BrowserMenuBar.java | 2 ++ 2 files changed, 13 insertions(+) diff --git a/src/org/infinity/NearInfinity.java b/src/org/infinity/NearInfinity.java index 44425266d..330bfc4a8 100644 --- a/src/org/infinity/NearInfinity.java +++ b/src/org/infinity/NearInfinity.java @@ -68,9 +68,11 @@ import org.infinity.gui.QuickSearch; import org.infinity.gui.ResourceTree; import org.infinity.gui.StatusBar; +import org.infinity.gui.StructViewer; import org.infinity.gui.ViewFrame; import org.infinity.gui.WindowBlocker; import org.infinity.icon.Icons; +import org.infinity.resource.AbstractStruct; import org.infinity.resource.Closeable; import org.infinity.resource.EffectFactory; import org.infinity.resource.Profile; @@ -550,6 +552,15 @@ public void actionPerformed(ActionEvent event) } finally { WindowBlocker.blockWindow(this, false); } + } else if (event.getActionCommand().equals("RefreshView")) { + // repaint UI controls of current view + if (getViewable() instanceof AbstractStruct) { + StructViewer sv = ((AbstractStruct)getViewable()).getViewer(); + if (sv != null) + SwingUtilities.updateComponentTreeUI(sv); + } + // repaint UI controls of child windows + ChildFrame.updateWindowGUIs(); } else if (event.getActionCommand().equals("ChangeLook")) { try { LookAndFeelInfo info = BrowserMenuBar.getInstance().getLookAndFeel(); diff --git a/src/org/infinity/gui/BrowserMenuBar.java b/src/org/infinity/gui/BrowserMenuBar.java index 229bfbcdf..406ffcb1f 100644 --- a/src/org/infinity/gui/BrowserMenuBar.java +++ b/src/org/infinity/gui/BrowserMenuBar.java @@ -1902,6 +1902,8 @@ private OptionsMenu() add(optionShowStrrefs); optionShowColoredStructures = new JCheckBoxMenuItem("Show colored structures in Edit tabs", getPrefs().getBoolean(OPTION_SHOWCOLOREDSTRUCTURES, true)); + optionShowColoredStructures.setActionCommand("RefreshView"); + optionShowColoredStructures.addActionListener(NearInfinity.getInstance()); add(optionShowColoredStructures); optionShowHexColored = new JCheckBoxMenuItem("Show colored blocks in Raw tabs", getPrefs().getBoolean(OPTION_SHOWHEXCOLORED, true)); From c6832ff6743b08946552ebb27165df002a95eb3b Mon Sep 17 00:00:00 2001 From: Argent77 <4519923+Argent77@users.noreply.github.com> Date: Wed, 22 Apr 2020 12:48:39 +0200 Subject: [PATCH 11/25] Use specialized data type for ARE explored bitmap field Fixes false positives if "Colorize structures" option is enabled. --- .../infinity/resource/are/AreResource.java | 6 ++--- src/org/infinity/resource/are/Explored.java | 27 +++++++++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 src/org/infinity/resource/are/Explored.java diff --git a/src/org/infinity/resource/are/AreResource.java b/src/org/infinity/resource/are/AreResource.java index bb34371e5..970551cba 100644 --- a/src/org/infinity/resource/are/AreResource.java +++ b/src/org/infinity/resource/are/AreResource.java @@ -705,10 +705,10 @@ public int read(ByteBuffer buffer, int offset) throws Exception addField(new HexNumber(buffer, offset + 144, 4, ARE_OFFSET_OBJECT_FLAGS)); addField(new ResourceRef(buffer, offset + 148, ARE_AREA_SCRIPT, "BCS")); SectionCount size_exploredbitmap = new SectionCount(buffer, offset + 156, 4, ARE_SIZE_EXPLORED_BITMAP, - Unknown.class); + Explored.class); addField(size_exploredbitmap); SectionOffset offset_exploredbitmap = new SectionOffset(buffer, offset + 160, ARE_OFFSET_EXPLORED_BITMAP, - Unknown.class); + Explored.class); addField(offset_exploredbitmap); SectionCount count_doors = new SectionCount(buffer, offset + 164, 4, ARE_NUM_DOORS, Door.class); @@ -829,7 +829,7 @@ else if (Profile.getEngine() == Profile.Engine.IWD2) { offset = offset_exploredbitmap.getValue(); if (size_exploredbitmap.getValue() > 0) { - addField(new Unknown(buffer, offset, size_exploredbitmap.getValue(), ARE_EXPLORED_BITMAP)); + addField(new Explored(buffer, offset, size_exploredbitmap.getValue(), ARE_EXPLORED_BITMAP)); } offset = offset_doors.getValue(); diff --git a/src/org/infinity/resource/are/Explored.java b/src/org/infinity/resource/are/Explored.java new file mode 100644 index 000000000..086a1afd7 --- /dev/null +++ b/src/org/infinity/resource/are/Explored.java @@ -0,0 +1,27 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2020 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.are; + +import java.nio.ByteBuffer; + +import org.infinity.datatype.Unknown; + +/** + * Subclassed from {@code Unknown} to make field type identifiable by reflection. + */ +public class Explored extends Unknown +{ + + public Explored(ByteBuffer buffer, int offset, int length) + { + super(buffer, offset, length); + } + + public Explored(ByteBuffer buffer, int offset, int length, String name) + { + super(buffer, offset, length, name); + } + +} From 102918f6a21861713046558449a273ca72cda39b Mon Sep 17 00:00:00 2001 From: Argent77 <4519923+Argent77@users.noreply.github.com> Date: Wed, 22 Apr 2020 14:47:59 +0200 Subject: [PATCH 12/25] Advanced Search: Fix missing import of "Match case" options (field and value) --- src/org/infinity/search/advanced/FilterInput.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/org/infinity/search/advanced/FilterInput.java b/src/org/infinity/search/advanced/FilterInput.java index 821d21a89..c107fdb17 100644 --- a/src/org/infinity/search/advanced/FilterInput.java +++ b/src/org/infinity/search/advanced/FilterInput.java @@ -559,6 +559,7 @@ private void importOptions(SearchOptions so) cbFieldType.setSelectedIndex(idx); tfFieldName.setText(so.getSearchName()); + cbFieldNameCase.setSelected(so.isSearchNameCaseSensitive()); cbFieldNameRegex.setSelected(so.isSearchNameRegex()); ftfFieldOffsetInput.setValue(Long.toString(so.getSearchOffset(), 16) + "h"); } @@ -577,6 +578,7 @@ private void importOptions(SearchOptions so) cbValueType.setSelectedIndex(idx); tfValueStringInput.setText(so.getValueText()); + cbValueStringCase.setSelected(so.isValueTextCaseSensitive()); cbValueStringRegex.setSelected(so.isValueTextRegex()); ftfValueInputMin.setValue(so.getValueNumberMin()); ftfValueInputMax.setValue(so.getValueNumberMax()); From d7ea2c8cbf8a11b97a37c79a8bdfc275a22755d4 Mon Sep 17 00:00:00 2001 From: Argent77 <4519923+Argent77@users.noreply.github.com> Date: Wed, 22 Apr 2020 15:08:29 +0200 Subject: [PATCH 13/25] New option: Add selected resource field as filter definition to Advanced Search dialog Right-click on field entry > Add to Advanced Search --- src/org/infinity/gui/StructViewer.java | 73 +++++++++++++++ .../search/advanced/AdvancedSearch.java | 89 +++++++++++++------ 2 files changed, 137 insertions(+), 25 deletions(-) diff --git a/src/org/infinity/gui/StructViewer.java b/src/org/infinity/gui/StructViewer.java index 0ecefa35a..eab984ea0 100644 --- a/src/org/infinity/gui/StructViewer.java +++ b/src/org/infinity/gui/StructViewer.java @@ -29,7 +29,10 @@ import java.awt.print.PrinterJob; import java.nio.ByteBuffer; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.swing.BorderFactory; import javax.swing.Icon; @@ -63,6 +66,8 @@ import org.infinity.datatype.Flag; import org.infinity.datatype.HexNumber; import org.infinity.datatype.InlineEditable; +import org.infinity.datatype.IsNumeric; +import org.infinity.datatype.IsReference; import org.infinity.datatype.IsTextual; import org.infinity.datatype.Readable; import org.infinity.datatype.ResourceRef; @@ -93,6 +98,8 @@ import org.infinity.search.AttributeSearcher; import org.infinity.search.DialogItemRefSearcher; import org.infinity.search.DialogStateReferenceSearcher; +import org.infinity.search.advanced.AdvancedSearch; +import org.infinity.search.advanced.SearchOptions; import org.infinity.util.Misc; import org.infinity.util.Pair; import org.infinity.util.StructClipboard; @@ -124,6 +131,7 @@ public final class StructViewer extends JPanel implements ListSelectionListener, public static final String CMD_TORESLIST = "ToResList"; public static final String CMD_RESET = "ResetType"; public static final String CMD_GOTO_OFFSET = "GotoOffset"; + public static final String CMD_ADD_ADV_SEARCH = "AddAdvSearch"; public static final String CMD_SHOW_IN_TREE = "ShowInTree"; public static final String CMD_SHOWVIEWER = "ShowView"; public static final String CMD_SHOWNEWVIEWER = "ShowNewView"; @@ -155,6 +163,7 @@ public final class StructViewer extends JPanel implements ListSelectionListener, private final JMenuItem miToHexInt = createMenuItem(CMD_TOHEXINT, "Edit as hex number", Icons.getIcon(Icons.ICON_REFRESH_16), this); private final JMenuItem miToFlags = createMenuItem(CMD_TOFLAGS, "Edit as bit field", Icons.getIcon(Icons.ICON_REFRESH_16), this); private final JMenuItem miReset = createMenuItem(CMD_RESET, "Reset field type", Icons.getIcon(Icons.ICON_REFRESH_16), this); + private final JMenuItem miAddToAdvSearch = createMenuItem(CMD_ADD_ADV_SEARCH, "Add to Advanced Search", Icons.getIcon(Icons.ICON_FIND_16), this); private final JMenuItem miGotoOffset = createMenuItem(CMD_GOTO_OFFSET, "Go to offset", null, this); private final JMenuItem miShowInTree = createMenuItem(CMD_SHOW_IN_TREE, "Show in tree", Icons.getIcon(Icons.ICON_SELECT_IN_TREE_16), this); private final JMenuItem miShowViewer = createMenuItem(CMD_SHOWVIEWER, "Show in viewer", Icons.getIcon(Icons.ICON_ROW_INSERT_AFTER_16), this); @@ -272,6 +281,7 @@ else if (fieldColors.containsKey(field.getClass())) // consider only referenced popupmenu.add(miCut); popupmenu.add(miCopy); popupmenu.add(miPaste); + popupmenu.addSeparator(); popupmenu.add(miToHex); popupmenu.add(miToBin); popupmenu.add(miToDec); @@ -281,6 +291,8 @@ else if (fieldColors.containsKey(field.getClass())) // consider only referenced popupmenu.add(miToResref); popupmenu.add(miToString); popupmenu.add(miReset); + popupmenu.addSeparator(); + popupmenu.add(miAddToAdvSearch); popupmenu.add(miGotoOffset); if (struct instanceof DlgResource) { popupmenu.add(miShowInTree); @@ -303,6 +315,7 @@ else if (fieldColors.containsKey(field.getClass())) // consider only referenced miToResref.setEnabled(false); miToString.setEnabled(false); miReset.setEnabled(false); + miAddToAdvSearch.setEnabled(false); miGotoOffset.setEnabled(false); miShowInTree.setEnabled(false); miShowViewer.setEnabled(false); @@ -588,6 +601,8 @@ else if (se instanceof SectionCount) cls = ((SectionCount)se).getSection(); if (cls != null) selectFirstEntryOfType(cls); + } else if (CMD_ADD_ADV_SEARCH.equals(cmd)) { + addToAdvancedSearch((StructEntry)table.getValueAt(min, 1)); } else if (CMD_SHOW_IN_TREE.equals(cmd)) { // this should only be available for DlgResources final DlgResource dlgRes = (DlgResource) struct; @@ -706,6 +721,7 @@ public void valueChanged(ListSelectionEvent event) miToResref.setEnabled(false); miToString.setEnabled(false); miReset.setEnabled(false); + miAddToAdvSearch.setEnabled(false); miGotoOffset.setEnabled(false); miShowInTree.setEnabled(false); miShowViewer.setEnabled(false); @@ -787,6 +803,7 @@ public void valueChanged(ListSelectionEvent event) miReset.setEnabled(isDataType && isReadable && getCachedStructEntry(((Datatype)selected).getOffset()) instanceof Readable && !(selected instanceof AbstractCode)); + miAddToAdvSearch.setEnabled(!(selected instanceof AbstractStruct || selected instanceof Unknown)); miGotoOffset.setEnabled(selected instanceof SectionOffset|| selected instanceof SectionCount); final boolean isSpecialDlgTreeItem = (selected instanceof State || selected instanceof Transition); @@ -1435,6 +1452,62 @@ private Color getClassColor(Class cls) return Color.WHITE; } + /** + * Creates an Advanced Search filter out of the specified {@code StructEntry} instance + * and adds it to the Advanced Search dialog. + */ + private void addToAdvancedSearch(StructEntry entry) + { + if (entry == null || entry instanceof AbstractStruct) + return; + + // setting search value + SearchOptions so = null; + if (entry instanceof Flag) { + so = new SearchOptions(); + so.setValueBitfield(((Flag)entry).getValue(), SearchOptions.BitFieldMode.Exact); + } else if (entry instanceof IsReference) { + so = new SearchOptions(); + so.setValueResource(((IsReference)entry).getResourceName()); + } else if (entry instanceof IsNumeric) { + so = new SearchOptions(); + so.setValueNumber(((IsNumeric)entry).getValue()); + } else if (!(entry instanceof Unknown)) { + so = new SearchOptions(); + so.setValueText(entry.toString(), false, false); + } else { + return; + } + + // setting structure level and field name + List structure = so.getStructure(); + for (AbstractStruct struct = entry.getParent(); struct != null && struct.getParent() != null; struct = struct.getParent()) + structure.add(0, getStrippedFieldName(struct.getName())); + so.setSearchName(entry.getName(), true, false); + + // root structure of resource needed for resource name + AbstractStruct root = struct; + while (root.getParent() != null) + root = root.getParent(); + if (root == null || root.getResourceEntry() == null) + return; + + AdvancedSearch dlg = ChildFrame.show(AdvancedSearch.class, () -> new AdvancedSearch()); + dlg.setResourceType(root.getResourceEntry().getExtension()); + dlg.addFilter(so); + } + + /** Strips numeric indices from the specified field name. */ + private String getStrippedFieldName(String name) + { + Pattern p = Pattern.compile("(.+)\\s+\\d+"); + Matcher m = p.matcher(name); + if (m.find()) + return m.group(1); + + return name; + } + // -------------------------- INNER CLASSES -------------------------- private final class PopupListener extends MouseAdapter diff --git a/src/org/infinity/search/advanced/AdvancedSearch.java b/src/org/infinity/search/advanced/AdvancedSearch.java index d8bdce23f..b8472ecc5 100644 --- a/src/org/infinity/search/advanced/AdvancedSearch.java +++ b/src/org/infinity/search/advanced/AdvancedSearch.java @@ -170,6 +170,28 @@ public Void doInBackground() chooser.setFileFilter(new FileNameExtensionFilter("XML Configuration (*.xml)", "xml")); } + /** Returns the currently selected resource type. */ + public String getResourceType() + { + return cbResourceTypes.getSelectedItem().toString(); + } + + /** Sets the specified resource type if available. Returns success state. */ + public boolean setResourceType(String type) + { + if (type == null) + return false; + + type = type.toUpperCase(); + for (int i = 0, cnt = cbResourceTypes.getModel().getSize(); i < cnt; i++) { + if (cbResourceTypes.getModel().getElementAt(i).equals(type)) { + cbResourceTypes.setSelectedIndex(i); + return true; + } + } + return false; + } + /** * Adds the specified SearchOptions instance to the filter list. * If the specified filter already exists it will be updated instead. @@ -189,6 +211,48 @@ public void addFilter(SearchOptions filter) } } + /** Returns the number of defined filters. */ + public int getFilterCount() + { + return filterList.getModel().getSize(); + } + + /** Returns the filter instance at the specified index. */ + public SearchOptions getFilter(int index) + { + if (index >= 0 && index < filterList.getModel().getSize()) + return filterList.getModel().getElementAt(index); + return null; + } + + /** + * Removes the filter at the specified index from the filter list. + * Optionally selects the next available entry in the list. + */ + public boolean removeFilter(int idx, boolean autoSelect) + { + SimpleListModel model = (SimpleListModel)filterList.getModel(); + if (idx >= 0 && idx < model.getSize()) { + model.remove(idx); + if (autoSelect) { + if (idx >= model.getSize()) + idx--; + if (idx >= 0) + filterList.setSelectedIndex(idx); + } + return true; + } + + return false; + } + + /** Removes all filters from the filter list. */ + private void removeAllFilters() + { + SimpleListModel model = (SimpleListModel)filterList.getModel(); + model.clear(); + } + private void init() throws Exception { setIconImage(Icons.getIcon(Icons.ICON_FIND_16).getImage()); @@ -557,31 +621,6 @@ private void setFilterMode(FilterMode mode) } } - /** Removes the filter at the specified index from the filter list. Optionally selects the next available entry in the list. */ - private boolean removeFilter(int idx, boolean autoSelect) - { - SimpleListModel model = (SimpleListModel)filterList.getModel(); - if (idx >= 0 && idx < model.getSize()) { - model.remove(idx); - if (autoSelect) { - if (idx >= model.getSize()) - idx--; - if (idx >= 0) - filterList.setSelectedIndex(idx); - } - return true; - } - - return false; - } - - /** Removes all filters from the filter list. */ - private void removeAllFilters() - { - SimpleListModel model = (SimpleListModel)filterList.getModel(); - model.clear(); - } - /** Imports a new search configuration from the specified xml file. */ private boolean importConfig(File xmlFile) { From 9fc37a93fff51f619bbf4506f2ed1830a199fe18 Mon Sep 17 00:00:00 2001 From: Argent77 <4519923+Argent77@users.noreply.github.com> Date: Thu, 23 Apr 2020 09:04:57 +0200 Subject: [PATCH 14/25] Use specialized data type for CRE item slot indices Fixes false positives if "Colorize structures" option is enabled. --- .../infinity/resource/cre/CreResource.java | 148 +++++++++--------- .../infinity/resource/cre/IndexNumber.java | 26 +++ 2 files changed, 100 insertions(+), 74 deletions(-) create mode 100644 src/org/infinity/resource/cre/IndexNumber.java diff --git a/src/org/infinity/resource/cre/CreResource.java b/src/org/infinity/resource/cre/CreResource.java index 331b20e89..75a224d28 100644 --- a/src/org/infinity/resource/cre/CreResource.java +++ b/src/org/infinity/resource/cre/CreResource.java @@ -1342,7 +1342,7 @@ private int readIWD2(ByteBuffer buffer, int offset) throws Exception shape_num, CRE_SHAPES, Iwd2Struct.TYPE_SHAPE); addField(shape_str); - SectionOffset itemslots_offset = new SectionOffset(buffer, offset + 1546, CRE_OFFSET_ITEM_SLOTS, DecNumber.class); + SectionOffset itemslots_offset = new SectionOffset(buffer, offset + 1546, CRE_OFFSET_ITEM_SLOTS, IndexNumber.class); addField(itemslots_offset); SectionOffset items_offset = new SectionOffset(buffer, offset + 1550, CRE_OFFSET_ITEMS, Item.class); @@ -1387,33 +1387,33 @@ private int readIWD2(ByteBuffer buffer, int offset) throws Exception } offset = getExtraOffset() + itemslots_offset.getValue(); - addField(new DecNumber(buffer, offset, 2, CRE_ITEM_SLOT_HELMET)); - addField(new DecNumber(buffer, offset + 2, 2, CRE_ITEM_SLOT_ARMOR)); - addField(new DecNumber(buffer, offset + 4, 2, CRE_ITEM_SLOT_SHIELD)); - addField(new DecNumber(buffer, offset + 6, 2, CRE_ITEM_SLOT_GAUNTLETS)); - addField(new DecNumber(buffer, offset + 8, 2, CRE_ITEM_SLOT_LEFT_RING)); - addField(new DecNumber(buffer, offset + 10, 2, CRE_ITEM_SLOT_RIGHT_RING)); - addField(new DecNumber(buffer, offset + 12, 2, CRE_ITEM_SLOT_AMULET)); - addField(new DecNumber(buffer, offset + 14, 2, CRE_ITEM_SLOT_BELT)); - addField(new DecNumber(buffer, offset + 16, 2, CRE_ITEM_SLOT_BOOTS)); + addField(new IndexNumber(buffer, offset, 2, CRE_ITEM_SLOT_HELMET)); + addField(new IndexNumber(buffer, offset + 2, 2, CRE_ITEM_SLOT_ARMOR)); + addField(new IndexNumber(buffer, offset + 4, 2, CRE_ITEM_SLOT_SHIELD)); + addField(new IndexNumber(buffer, offset + 6, 2, CRE_ITEM_SLOT_GAUNTLETS)); + addField(new IndexNumber(buffer, offset + 8, 2, CRE_ITEM_SLOT_LEFT_RING)); + addField(new IndexNumber(buffer, offset + 10, 2, CRE_ITEM_SLOT_RIGHT_RING)); + addField(new IndexNumber(buffer, offset + 12, 2, CRE_ITEM_SLOT_AMULET)); + addField(new IndexNumber(buffer, offset + 14, 2, CRE_ITEM_SLOT_BELT)); + addField(new IndexNumber(buffer, offset + 16, 2, CRE_ITEM_SLOT_BOOTS)); for (int i = 0; i < 4; i++) { - addField(new DecNumber(buffer, offset + 18 + (i * 4), 2, String.format(CRE_ITEM_SLOT_WEAPON_FMT, i+1))); - addField(new DecNumber(buffer, offset + 20 + (i * 4), 2, String.format(CRE_ITEM_SLOT_SHIELD_FMT, i+1))); + addField(new IndexNumber(buffer, offset + 18 + (i * 4), 2, String.format(CRE_ITEM_SLOT_WEAPON_FMT, i+1))); + addField(new IndexNumber(buffer, offset + 20 + (i * 4), 2, String.format(CRE_ITEM_SLOT_SHIELD_FMT, i+1))); } for (int i = 0; i < 4; i++) { - addField(new DecNumber(buffer, offset + 34 + (i * 2), 2, String.format(CRE_ITEM_SLOT_QUIVER_FMT, i+1))); + addField(new IndexNumber(buffer, offset + 34 + (i * 2), 2, String.format(CRE_ITEM_SLOT_QUIVER_FMT, i+1))); } - addField(new DecNumber(buffer, offset + 42, 2, CRE_ITEM_SLOT_CLOAK)); + addField(new IndexNumber(buffer, offset + 42, 2, CRE_ITEM_SLOT_CLOAK)); for (int i = 0; i < 3; i++) { - addField(new DecNumber(buffer, offset + 44 + (i * 2), 2, String.format(CRE_ITEM_SLOT_QUICK_FMT, i+1))); + addField(new IndexNumber(buffer, offset + 44 + (i * 2), 2, String.format(CRE_ITEM_SLOT_QUICK_FMT, i+1))); } for (int i = 0; i < 24; i++) { - addField(new DecNumber(buffer, offset + 50 + (i * 2), 2, - String.format(CRE_ITEM_SLOT_INVENTORY_FMT, i+1))); + addField(new IndexNumber(buffer, offset + 50 + (i * 2), 2, + String.format(CRE_ITEM_SLOT_INVENTORY_FMT, i+1))); } - addField(new DecNumber(buffer, offset + 98, 2, CRE_ITEM_SLOT_MAGIC_WEAPON)); - addField(new DecNumber(buffer, offset + 100, 2, CRE_SELECTED_WEAPON_SLOT)); - addField(new DecNumber(buffer, offset + 102, 2, CRE_SELECTED_WEAPON_ABILITY)); + addField(new IndexNumber(buffer, offset + 98, 2, CRE_ITEM_SLOT_MAGIC_WEAPON)); + addField(new IndexNumber(buffer, offset + 100, 2, CRE_SELECTED_WEAPON_SLOT)); + addField(new IndexNumber(buffer, offset + 102, 2, CRE_SELECTED_WEAPON_ABILITY)); int endoffset = offset; for (final StructEntry entry : getFields()) { @@ -1736,7 +1736,7 @@ else if (version.equalsIgnoreCase("V9.0") || version.equalsIgnoreCase("V9.1")) { SectionCount countMemSpells = new SectionCount(buffer, offset + 684, 4, CRE_NUM_MEMORIZED_SPELLS, MemorizedSpells.class); addField(countMemSpells); - SectionOffset offsetItemslots = new SectionOffset(buffer, offset + 688, CRE_OFFSET_ITEM_SLOTS, DecNumber.class); + SectionOffset offsetItemslots = new SectionOffset(buffer, offset + 688, CRE_OFFSET_ITEM_SLOTS, IndexNumber.class); addField(offsetItemslots); SectionOffset offsetItems = new SectionOffset(buffer, offset + 692, CRE_OFFSET_ITEMS, Item.class); addField(offsetItems); @@ -1796,88 +1796,88 @@ else if (version.equalsIgnoreCase("V9.0") || version.equalsIgnoreCase("V9.1")) { offset = getExtraOffset() + offsetItemslots.getValue(); int slotCount = 0; if (version.equalsIgnoreCase("V1.2")) { - addField(new DecNumber(buffer, offset, 2, CRE_ITEM_SLOT_RIGHT_EARRING)); - addField(new DecNumber(buffer, offset + 2, 2, CRE_ITEM_SLOT_CHEST)); - addField(new DecNumber(buffer, offset + 4, 2, CRE_ITEM_SLOT_LEFT_TATTOO)); - addField(new DecNumber(buffer, offset + 6, 2, CRE_ITEM_SLOT_HAND)); - addField(new DecNumber(buffer, offset + 8, 2, CRE_ITEM_SLOT_LEFT_RING)); - addField(new DecNumber(buffer, offset + 10, 2, CRE_ITEM_SLOT_RIGHT_RING)); - addField(new DecNumber(buffer, offset + 12, 2, CRE_ITEM_SLOT_LEFT_EARRING)); - addField(new DecNumber(buffer, offset + 14, 2, CRE_ITEM_SLOT_RIGHT_TATTOO_LOWER)); - addField(new DecNumber(buffer, offset + 16, 2, CRE_ITEM_SLOT_WRIST)); + addField(new IndexNumber(buffer, offset, 2, CRE_ITEM_SLOT_RIGHT_EARRING)); + addField(new IndexNumber(buffer, offset + 2, 2, CRE_ITEM_SLOT_CHEST)); + addField(new IndexNumber(buffer, offset + 4, 2, CRE_ITEM_SLOT_LEFT_TATTOO)); + addField(new IndexNumber(buffer, offset + 6, 2, CRE_ITEM_SLOT_HAND)); + addField(new IndexNumber(buffer, offset + 8, 2, CRE_ITEM_SLOT_LEFT_RING)); + addField(new IndexNumber(buffer, offset + 10, 2, CRE_ITEM_SLOT_RIGHT_RING)); + addField(new IndexNumber(buffer, offset + 12, 2, CRE_ITEM_SLOT_LEFT_EARRING)); + addField(new IndexNumber(buffer, offset + 14, 2, CRE_ITEM_SLOT_RIGHT_TATTOO_LOWER)); + addField(new IndexNumber(buffer, offset + 16, 2, CRE_ITEM_SLOT_WRIST)); slotCount += 9; for (int i = 0; i < 4; i++) { - addField(new DecNumber(buffer, offset + 18 + (i * 2), 2, - String.format(CRE_ITEM_SLOT_WEAPON_FMT, i+1))); + addField(new IndexNumber(buffer, offset + 18 + (i * 2), 2, + String.format(CRE_ITEM_SLOT_WEAPON_FMT, i+1))); } slotCount += 4; for (int i = 0; i < 6; i++) { - addField(new DecNumber(buffer, offset + 26 + (i * 2), 2, String.format(CRE_ITEM_SLOT_QUIVER_FMT, i+1))); + addField(new IndexNumber(buffer, offset + 26 + (i * 2), 2, String.format(CRE_ITEM_SLOT_QUIVER_FMT, i+1))); } - addField(new DecNumber(buffer, offset + 38, 2, CRE_ITEM_SLOT_RIGHT_TATTOO_UPPER)); + addField(new IndexNumber(buffer, offset + 38, 2, CRE_ITEM_SLOT_RIGHT_TATTOO_UPPER)); slotCount += 7; for (int i = 0; i < 5; i++) { - addField(new DecNumber(buffer, offset + 40 + (i * 2), 2, String.format(CRE_ITEM_SLOT_QUICK_FMT, i+1))); + addField(new IndexNumber(buffer, offset + 40 + (i * 2), 2, String.format(CRE_ITEM_SLOT_QUICK_FMT, i+1))); } slotCount += 5; for (int i = 0; i < 20; i++) { - addField(new DecNumber(buffer, offset + 50 + (i * 2), 2, String.format(CRE_ITEM_SLOT_INVENTORY_FMT, i+1))); + addField(new IndexNumber(buffer, offset + 50 + (i * 2), 2, String.format(CRE_ITEM_SLOT_INVENTORY_FMT, i+1))); } slotCount += 20; - addField(new DecNumber(buffer, offset + 90, 2, CRE_ITEM_SLOT_MAGIC_WEAPON)); - addField(new DecNumber(buffer, offset + 92, 2, CRE_SELECTED_WEAPON_SLOT)); - addField(new DecNumber(buffer, offset + 94, 2, CRE_SELECTED_WEAPON_ABILITY)); + addField(new IndexNumber(buffer, offset + 90, 2, CRE_ITEM_SLOT_MAGIC_WEAPON)); + addField(new IndexNumber(buffer, offset + 92, 2, CRE_SELECTED_WEAPON_SLOT)); + addField(new IndexNumber(buffer, offset + 94, 2, CRE_SELECTED_WEAPON_ABILITY)); slotCount += 3; } else { if (Profile.getGame() == Profile.Game.PSTEE) { // REMEMBER: ITMSLOTS.2DA can be used as reference for item slot layout - addField(new DecNumber(buffer, offset, 2, CRE_ITEM_SLOT_LEFT_EARRING)); - addField(new DecNumber(buffer, offset + 2, 2, CRE_ITEM_SLOT_CHEST)); - addField(new DecNumber(buffer, offset + 4, 2, CRE_ITEM_SLOT_RIGHT_TATTOO_LOWER)); - addField(new DecNumber(buffer, offset + 6, 2, CRE_ITEM_SLOT_HAND)); - addField(new DecNumber(buffer, offset + 8, 2, CRE_ITEM_SLOT_RIGHT_RING)); - addField(new DecNumber(buffer, offset + 10, 2, CRE_ITEM_SLOT_LEFT_RING)); - addField(new DecNumber(buffer, offset + 12, 2, CRE_ITEM_SLOT_RIGHT_EARRING)); - addField(new DecNumber(buffer, offset + 14, 2, CRE_ITEM_SLOT_LEFT_TATTOO)); - addField(new DecNumber(buffer, offset + 16, 2, CRE_ITEM_SLOT_WRIST)); + addField(new IndexNumber(buffer, offset, 2, CRE_ITEM_SLOT_LEFT_EARRING)); + addField(new IndexNumber(buffer, offset + 2, 2, CRE_ITEM_SLOT_CHEST)); + addField(new IndexNumber(buffer, offset + 4, 2, CRE_ITEM_SLOT_RIGHT_TATTOO_LOWER)); + addField(new IndexNumber(buffer, offset + 6, 2, CRE_ITEM_SLOT_HAND)); + addField(new IndexNumber(buffer, offset + 8, 2, CRE_ITEM_SLOT_RIGHT_RING)); + addField(new IndexNumber(buffer, offset + 10, 2, CRE_ITEM_SLOT_LEFT_RING)); + addField(new IndexNumber(buffer, offset + 12, 2, CRE_ITEM_SLOT_RIGHT_EARRING)); + addField(new IndexNumber(buffer, offset + 14, 2, CRE_ITEM_SLOT_LEFT_TATTOO)); + addField(new IndexNumber(buffer, offset + 16, 2, CRE_ITEM_SLOT_WRIST)); slotCount += 9; } else { - addField(new DecNumber(buffer, offset, 2, CRE_ITEM_SLOT_HELMET)); - addField(new DecNumber(buffer, offset + 2, 2, CRE_ITEM_SLOT_ARMOR)); - addField(new DecNumber(buffer, offset + 4, 2, CRE_ITEM_SLOT_SHIELD)); - addField(new DecNumber(buffer, offset + 6, 2, CRE_ITEM_SLOT_GLOVES)); - addField(new DecNumber(buffer, offset + 8, 2, CRE_ITEM_SLOT_LEFT_RING)); - addField(new DecNumber(buffer, offset + 10, 2, CRE_ITEM_SLOT_RIGHT_RING)); - addField(new DecNumber(buffer, offset + 12, 2, CRE_ITEM_SLOT_AMULET)); - addField(new DecNumber(buffer, offset + 14, 2, CRE_ITEM_SLOT_BELT)); - addField(new DecNumber(buffer, offset + 16, 2, CRE_ITEM_SLOT_BOOTS)); + addField(new IndexNumber(buffer, offset, 2, CRE_ITEM_SLOT_HELMET)); + addField(new IndexNumber(buffer, offset + 2, 2, CRE_ITEM_SLOT_ARMOR)); + addField(new IndexNumber(buffer, offset + 4, 2, CRE_ITEM_SLOT_SHIELD)); + addField(new IndexNumber(buffer, offset + 6, 2, CRE_ITEM_SLOT_GLOVES)); + addField(new IndexNumber(buffer, offset + 8, 2, CRE_ITEM_SLOT_LEFT_RING)); + addField(new IndexNumber(buffer, offset + 10, 2, CRE_ITEM_SLOT_RIGHT_RING)); + addField(new IndexNumber(buffer, offset + 12, 2, CRE_ITEM_SLOT_AMULET)); + addField(new IndexNumber(buffer, offset + 14, 2, CRE_ITEM_SLOT_BELT)); + addField(new IndexNumber(buffer, offset + 16, 2, CRE_ITEM_SLOT_BOOTS)); slotCount += 9; } for (int i = 0; i < 4; i++) { - addField(new DecNumber(buffer, offset + 18 + (i * 2), 2, String.format(CRE_ITEM_SLOT_WEAPON_FMT, i+1))); + addField(new IndexNumber(buffer, offset + 18 + (i * 2), 2, String.format(CRE_ITEM_SLOT_WEAPON_FMT, i+1))); slotCount++; } for (int i = 0; i < 4; i++) { - addField(new DecNumber(buffer, offset + 26 + (i * 2), 2, String.format(CRE_ITEM_SLOT_QUIVER_FMT, i+1))); + addField(new IndexNumber(buffer, offset + 26 + (i * 2), 2, String.format(CRE_ITEM_SLOT_QUIVER_FMT, i+1))); slotCount++; } if (Profile.getGame() == Profile.Game.PSTEE) { - addField(new DecNumber(buffer, offset + 34, 2, CRE_ITEM_SLOT_RIGHT_TATTOO_UPPER)); + addField(new IndexNumber(buffer, offset + 34, 2, CRE_ITEM_SLOT_RIGHT_TATTOO_UPPER)); slotCount++; } else { - addField(new DecNumber(buffer, offset + 34, 2, CRE_ITEM_SLOT_CLOAK)); + addField(new IndexNumber(buffer, offset + 34, 2, CRE_ITEM_SLOT_CLOAK)); slotCount++; } for (int i = 0; i < 3; i++) { - addField(new DecNumber(buffer, offset + 36 + (i * 2), 2, String.format(CRE_ITEM_SLOT_QUICK_FMT, i+1))); + addField(new IndexNumber(buffer, offset + 36 + (i * 2), 2, String.format(CRE_ITEM_SLOT_QUICK_FMT, i+1))); slotCount++; } for (int i = 0; i < 16; i++) { - addField(new DecNumber(buffer, offset + 42 + (i * 2), 2, String.format(CRE_ITEM_SLOT_INVENTORY_FMT, i+1))); + addField(new IndexNumber(buffer, offset + 42 + (i * 2), 2, String.format(CRE_ITEM_SLOT_INVENTORY_FMT, i+1))); slotCount++; } - addField(new DecNumber(buffer, offset + 74, 2, CRE_ITEM_SLOT_MAGIC_WEAPON)); + addField(new IndexNumber(buffer, offset + 74, 2, CRE_ITEM_SLOT_MAGIC_WEAPON)); slotCount++; StructEntry se = getAttribute(CRE_NUM_ITEM_SLOTS); int maxSlotCount = 0; @@ -1887,30 +1887,30 @@ else if (version.equalsIgnoreCase("V9.0") || version.equalsIgnoreCase("V9.1")) { if (Profile.getGame() == Profile.Game.PSTEE && slotCount + 8 < maxSlotCount) { // registered characters gain additional item slots int idxUnused = 1; - addField(new DecNumber(buffer, offset + 76, 2, String.format(CRE_ITEM_SLOT_QUIVER_FMT, 5))); - addField(new DecNumber(buffer, offset + 78, 2, String.format(CRE_ITEM_SLOT_UNUSED_FMT, idxUnused++))); - addField(new DecNumber(buffer, offset + 80, 2, String.format(CRE_ITEM_SLOT_QUICK_FMT, 4))); - addField(new DecNumber(buffer, offset + 82, 2, String.format(CRE_ITEM_SLOT_QUICK_FMT, 5))); + addField(new IndexNumber(buffer, offset + 76, 2, String.format(CRE_ITEM_SLOT_QUIVER_FMT, 5))); + addField(new IndexNumber(buffer, offset + 78, 2, String.format(CRE_ITEM_SLOT_UNUSED_FMT, idxUnused++))); + addField(new IndexNumber(buffer, offset + 80, 2, String.format(CRE_ITEM_SLOT_QUICK_FMT, 4))); + addField(new IndexNumber(buffer, offset + 82, 2, String.format(CRE_ITEM_SLOT_QUICK_FMT, 5))); slotCount += 4; offset += 84; for (int i = 0; i < 4; i++) { - addField(new DecNumber(buffer, offset, 2, String.format(CRE_ITEM_SLOT_INVENTORY_FMT, i+17))); + addField(new IndexNumber(buffer, offset, 2, String.format(CRE_ITEM_SLOT_INVENTORY_FMT, i+17))); slotCount++; offset += 2; } // filling the gap with placeholder slots for (int i = 0, imax = maxSlotCount - slotCount - 1; i < imax; i++) { - addField(new DecNumber(buffer, offset, 2, String.format(CRE_ITEM_SLOT_UNUSED_FMT, idxUnused++))); + addField(new IndexNumber(buffer, offset, 2, String.format(CRE_ITEM_SLOT_UNUSED_FMT, idxUnused++))); slotCount++; offset += 2; } - addField(new DecNumber(buffer, offset, 2, CRE_SELECTED_WEAPON_SLOT)); + addField(new IndexNumber(buffer, offset, 2, CRE_SELECTED_WEAPON_SLOT)); offset += 2; - addField(new DecNumber(buffer, offset, 2, CRE_SELECTED_WEAPON_ABILITY)); + addField(new IndexNumber(buffer, offset, 2, CRE_SELECTED_WEAPON_ABILITY)); offset += 2; } else { - addField(new DecNumber(buffer, offset + 76, 2, CRE_SELECTED_WEAPON_SLOT)); - addField(new DecNumber(buffer, offset + 78, 2, CRE_SELECTED_WEAPON_ABILITY)); + addField(new IndexNumber(buffer, offset + 76, 2, CRE_SELECTED_WEAPON_SLOT)); + addField(new IndexNumber(buffer, offset + 78, 2, CRE_SELECTED_WEAPON_ABILITY)); offset += 80; } } diff --git a/src/org/infinity/resource/cre/IndexNumber.java b/src/org/infinity/resource/cre/IndexNumber.java new file mode 100644 index 000000000..1c6c3c422 --- /dev/null +++ b/src/org/infinity/resource/cre/IndexNumber.java @@ -0,0 +1,26 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2020 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre; + +import java.nio.ByteBuffer; + +import org.infinity.datatype.DecNumber; + +/** + * Subclassed from {@code DecNumber} to make field type identifiable by reflection. + */ +public class IndexNumber extends DecNumber +{ + public IndexNumber(ByteBuffer buffer, int offset, int length, String name) + { + super(buffer, offset, length, name); + } + + public IndexNumber(ByteBuffer buffer, int offset, int length, String name, boolean signed) + { + super(buffer, offset, length, name, signed); + } + +} From 700dd431f26b348e221ae9ac68bbff0d5ad02df5 Mon Sep 17 00:00:00 2001 From: Argent77 <4519923+Argent77@users.noreply.github.com> Date: Thu, 23 Apr 2020 09:05:47 +0200 Subject: [PATCH 15/25] Improve color choices for colorized structure backgrounds --- src/org/infinity/gui/ViewerUtil.java | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/org/infinity/gui/ViewerUtil.java b/src/org/infinity/gui/ViewerUtil.java index c00af3f6e..1cb61024d 100644 --- a/src/org/infinity/gui/ViewerUtil.java +++ b/src/org/infinity/gui/ViewerUtil.java @@ -71,18 +71,22 @@ public final class ViewerUtil * list, table or tree items. */ public static final Color[] BACKGROUND_COLORS = { - new Color(0xd8d8ff), - new Color(0xb8ffb8), - new Color(0xffd0d0), - new Color(0xffc8ff), - new Color(0xffffa0), - new Color(0x85ffc2), - new Color(0xffd3a6), - new Color(0x99ccff), - new Color(0xffa3d1), - new Color(0xa3fff8), - new Color(0xb5a3ff), - new Color(0xe0e0e0), + new Color(0xceccff), + new Color(0xffcce6), + new Color(0xccffe9), + new Color(0xfaffcc), + new Color(0xccddff), + new Color(0xffccf9), + new Color(0xccffd7), + new Color(0xfff2cc), + new Color(0xccf0ff), + new Color(0xf4ccff), + new Color(0xd5ffcc), + new Color(0xffdfcc), + new Color(0xccfffc), + new Color(0xe1ccff), + new Color(0xe8ffcc), + new Color(0xffcccc), }; public static void addLabelFieldPair(JPanel panel, StructEntry entry, GridBagLayout gbl, From b22c6df70f455a791c97fee0ea51b25723ee6475 Mon Sep 17 00:00:00 2001 From: Argent77 <4519923+Argent77@users.noreply.github.com> Date: Thu, 23 Apr 2020 10:14:52 +0200 Subject: [PATCH 16/25] Prevent out of bounds tab selection in StructViewer Fixes GAM > NPC viewer in PST. --- src/org/infinity/gui/StructViewer.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/org/infinity/gui/StructViewer.java b/src/org/infinity/gui/StructViewer.java index eab984ea0..264c6423b 100644 --- a/src/org/infinity/gui/StructViewer.java +++ b/src/org/infinity/gui/StructViewer.java @@ -444,7 +444,9 @@ else if (fieldColors.containsKey(field.getClass())) // consider only referenced sViewer = struct.getParent().getParent().getViewer(); } if (sViewer != null && sViewer.tabbedPane != null) { - tabbedPane.setSelectedIndex(sViewer.tabbedPane.getSelectedIndex()); + // make sure tab index is within bounds + int idx = Math.max(Math.min(sViewer.tabbedPane.getSelectedIndex(), tabbedPane.getTabCount() - 1), 0); + tabbedPane.setSelectedIndex(idx); } } else if (lastIndexStruct == struct.getClass()) { tabbedPane.setSelectedIndex(lastIndex); From 9c00e515e9994054123554b7590fa8fbca519cbd Mon Sep 17 00:00:00 2001 From: Argent77 <4519923+Argent77@users.noreply.github.com> Date: Fri, 24 Apr 2020 09:09:33 +0200 Subject: [PATCH 17/25] Advanced Search: Fix grouping of filters --- .../search/advanced/AdvancedSearchWorker.java | 131 +++++++++++++----- 1 file changed, 96 insertions(+), 35 deletions(-) diff --git a/src/org/infinity/search/advanced/AdvancedSearchWorker.java b/src/org/infinity/search/advanced/AdvancedSearchWorker.java index e876aa4e2..b51e104a7 100644 --- a/src/org/infinity/search/advanced/AdvancedSearchWorker.java +++ b/src/org/infinity/search/advanced/AdvancedSearchWorker.java @@ -6,8 +6,11 @@ import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.regex.Pattern; import javax.swing.JProgressBar; @@ -58,28 +61,49 @@ public void run() Resource res = ResourceFactory.getResource(entry); if (res instanceof AbstractStruct) { AbstractStruct structRoot = (AbstractStruct)res; + // storage for evaluated matches List entryMatches = new ArrayList<>(); - Map>> entryStructures = new HashMap<>(); + // stores number of grouped filters applied per structure level + Map, Integer> groupFilters = new HashMap<>(); + // storage for potential grouped matches + Map, Set> groupCache = new HashMap<>(); int matches = 0; for (int filterIdx = 0; filterIdx < searchOptions.size(); filterIdx++) { SearchOptions so = searchOptions.get(filterIdx); + // keep track of grouped filter count per structure + if (so.isStructureGroup()) { + Integer count = groupFilters.get(so.getStructure()); + if (count == null) + count = Integer.valueOf(0); + groupFilters.put(so.getStructure(), Integer.valueOf(count.intValue() + 1)); + } + // list of structures to search + boolean isMatch = false; List structs = collectStructures(structRoot, so, 0); for (AbstractStruct struct : structs) { - if (findMatches(entryMatches, struct, so) && - isGroupedMatch(entryStructures, struct, so)) { - matches++; - break; - } + isMatch |= findMatches(entryMatches, groupCache, struct, so); + } + if (isMatch) + matches++; + } + + // evaluating grouped matches + collapseGroupFilters(groupCache, groupFilters); + for (Set set : groupCache.values()) { + // all StructEntry instances found in the map are considered valid matches + for (StructEntry ref : set) { + entryMatches.add(new ReferenceHitFrame.ReferenceHit(entry, entry.getSearchString(), ref)); + matches++; } } // evaluating filter mode switch (filterOp) { case MatchAll: - if (matches == searchOptions.size()) + if (matches >= searchOptions.size()) matched.addAll(entryMatches); break; case MatchAny: @@ -150,37 +174,65 @@ private List collectStructures(AbstractStruct struct, SearchOpti return list; } - // Return if a group match exists - private boolean isGroupedMatch(Map>> mapCache, AbstractStruct struct, SearchOptions so) + // Remove all incomplete group matches from the map + private void collapseGroupFilters(Map, Set> groupCache, Map, Integer> groupFilters) { - boolean retVal = true; - if (so.isStructureGroup()) { - // getting current struct chain - List> curPath = new ArrayList<>(); - for (AbstractStruct as = struct; as != null; as = as.getParent()) { - curPath.add(0, (Class)as.getClass()); - } + Iterator, Set>> iter = groupCache.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry, Set> entry = iter.next(); + Integer count = groupFilters.get(entry.getKey()); + if (count != null) { + // grouping entries of the current structure level in a temporary map + Set groupSet = entry.getValue(); + Map> structureMap = new HashMap<>(); + for (StructEntry se : groupSet) { + Set structureSet = structureMap.computeIfAbsent(se.getParent(), e -> new HashSet()); + structureSet.add(se); + } - // getting mapped struct chain - for (AbstractStruct as : mapCache.keySet()) { - if (as.getClass().equals(struct.getClass())) { - List> existingPath = mapCache.get(as); - // checking group - if (curPath.equals(existingPath) && !struct.equals(as)) { - retVal = false; - break; + // removing entries of incomplete group matches + for (AbstractStruct as : structureMap.keySet()) { + Set structureSet = structureMap.get(as); + if (structureSet != null) { + switch (filterOp) { + case MatchAll: + if (structureSet.size() < count) + groupSet.removeAll(structureSet); + break; + case MatchAny: + if (structureSet.size() == 0) + groupSet.removeAll(structureSet); + break; + case MatchOne: + if (structureSet.size() != 1) + groupSet.removeAll(structureSet); + break; + } } } - } - // adding current struct chain to map - mapCache.put(struct, curPath); + // structure level entry can be removed if it contains no matches + if (groupSet.size() == 0) { + iter.remove(); + } + } else { + System.err.println("Skipping unidentified group match"); + iter.remove(); + } } - return retVal; + } + + // Collect filters grouped by structure + private void addGroupFilter(Map, Set> groupCache, StructEntry se, SearchOptions so) + { + Set set = groupCache.computeIfAbsent(so.getStructure(), s -> new HashSet()); + set.add(se); } // Search for matching fields in specified structure - private boolean findMatches(List matchList, AbstractStruct struct, SearchOptions so) + private boolean findMatches(List matchList, + Map, Set> groupCache, + AbstractStruct struct, SearchOptions so) { if (struct != null && so != null) { if (so.getSearchType() == SearchOptions.FieldMode.ByName) { @@ -195,7 +247,7 @@ private boolean findMatches(List matchList, Abst boolean result = false; for (final StructEntry se : struct.getFields()) { if (pattern.matcher(se.getName()).find()) { - result |= isMatch(matchList, se, so); + result |= isMatch(matchList, groupCache, se, so); } } return result; @@ -205,14 +257,15 @@ private boolean findMatches(List matchList, Abst if (so.getSearchType() == SearchOptions.FieldMode.ByRelativeOffset) { offset += struct.getOffset(); } - return isMatch(matchList, struct.getAttribute(offset), so); + return isMatch(matchList, groupCache, struct.getAttribute(offset), so); } } return false; } // Match value against search options - private boolean isMatch(List matchList, StructEntry se, SearchOptions so) + private boolean isMatch(List matchList, Map, Set> groupCache, + StructEntry se, SearchOptions so) { boolean retVal = false; if (se != null && so != null) { @@ -232,10 +285,18 @@ private boolean isMatch(List matchList, StructEn } if (so.isInvertMatch()) - retVal = !retVal; + retVal = !retVal; - if (retVal) - matchList.add(new ReferenceHitFrame.ReferenceHit(entry, entry.getSearchString(), se)); + if (retVal) { + if (so.isStructureGroup()) { + // grouped matches are evaluated later + addGroupFilter(groupCache, se, so); + retVal = false; + } else { + // add ungrouped matches directly to the results list + matchList.add(new ReferenceHitFrame.ReferenceHit(entry, entry.getSearchString(), se)); + } + } } return retVal; } From a1514baf455ea928a37edcef4e9bd9cdf92376a7 Mon Sep 17 00:00:00 2001 From: Argent77 <4519923+Argent77@users.noreply.github.com> Date: Fri, 24 Apr 2020 09:32:47 +0200 Subject: [PATCH 18/25] Area Viewer: Fix erroneous detection of extended night in PSTEE --- src/org/infinity/resource/are/viewer/AreaViewer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/infinity/resource/are/viewer/AreaViewer.java b/src/org/infinity/resource/are/viewer/AreaViewer.java index 79b3af4a5..c0a760f89 100644 --- a/src/org/infinity/resource/are/viewer/AreaViewer.java +++ b/src/org/infinity/resource/are/viewer/AreaViewer.java @@ -2752,7 +2752,7 @@ private void init() // fetching important options final Flag flags = (Flag)are.getAttribute(AreResource.ARE_LOCATION); if (flags != null) { - if (Profile.getEngine() == Profile.Engine.PST) { + if (Profile.getEngine() == Profile.Engine.PST || Profile.getGame() == Profile.Game.PSTEE) { hasDayNight = flags.isFlagSet(10); hasExtendedNight = false; } else { From 2d977bb7d4076c52b1811c70813ee240f7797c62 Mon Sep 17 00:00:00 2001 From: Argent77 <4519923+Argent77@users.noreply.github.com> Date: Fri, 24 Apr 2020 11:07:52 +0200 Subject: [PATCH 19/25] Cosmetic --- src/org/infinity/gui/BrowserMenuBar.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/org/infinity/gui/BrowserMenuBar.java b/src/org/infinity/gui/BrowserMenuBar.java index 406ffcb1f..595d53249 100644 --- a/src/org/infinity/gui/BrowserMenuBar.java +++ b/src/org/infinity/gui/BrowserMenuBar.java @@ -1861,22 +1861,24 @@ private OptionsMenu() new JCheckBoxMenuItem("Ignore Overrides", getPrefs().getBoolean(OPTION_IGNOREOVERRIDE, false)); add(optionIgnoreOverride); optionIgnoreReadErrors = - new JCheckBoxMenuItem("Ignore Read Errors", getPrefs().getBoolean(OPTION_IGNOREREADERRORS, false)); + new JCheckBoxMenuItem("Ignore read errors", getPrefs().getBoolean(OPTION_IGNOREREADERRORS, false)); add(optionIgnoreReadErrors); optionShowUnknownResources = - new JCheckBoxMenuItem("Show Unknown Resource Types", getPrefs().getBoolean(OPTION_SHOWUNKNOWNRESOURCES, true)); + new JCheckBoxMenuItem("Show unknown resource types", getPrefs().getBoolean(OPTION_SHOWUNKNOWNRESOURCES, true)); optionShowUnknownResources.setActionCommand("Refresh"); optionShowUnknownResources.addActionListener(NearInfinity.getInstance()); optionShowUnknownResources.setToolTipText("Uncheck this option to hide unknown or unsupported resource types and invalid filenames."); add(optionShowUnknownResources); optionShowOffset = - new JCheckBoxMenuItem("Show Hex Offsets", getPrefs().getBoolean(OPTION_SHOWOFFSETS, false)); + new JCheckBoxMenuItem("Show hex offsets", getPrefs().getBoolean(OPTION_SHOWOFFSETS, false)); add(optionShowOffset); - optionTreeSearchNames = new JCheckBoxMenuItem("Show Search Names in Resource Tree", getPrefs().getBoolean(OPTION_SHOWTREESEARCHNAMES, true)); + optionTreeSearchNames = + new JCheckBoxMenuItem("Show search names in resource tree", getPrefs().getBoolean(OPTION_SHOWTREESEARCHNAMES, true)); optionTreeSearchNames.setActionCommand("RefreshTree"); optionTreeSearchNames.addActionListener(NearInfinity.getInstance()); add(optionTreeSearchNames); - optionHighlightOverridden = new JCheckBoxMenuItem("Show Overridden Files in Bold in Resource Tree", getPrefs().getBoolean(OPTION_HIGHLIGHT_OVERRIDDEN, true)); + optionHighlightOverridden = + new JCheckBoxMenuItem("Show overridden files in bold in resource tree", getPrefs().getBoolean(OPTION_HIGHLIGHT_OVERRIDDEN, true)); optionHighlightOverridden.setActionCommand("RefreshTree"); optionHighlightOverridden.addActionListener(NearInfinity.getInstance()); optionHighlightOverridden.setToolTipText("If checked, files, that contains in game index (.key file) and located " From e577bb099fc4d54308398b5e33e48a72fa5f3347 Mon Sep 17 00:00:00 2001 From: Argent77 <4519923+Argent77@users.noreply.github.com> Date: Fri, 24 Apr 2020 11:15:55 +0200 Subject: [PATCH 20/25] Improve tooltip of option "Show overridden files in bold in resource tree" --- src/org/infinity/gui/BrowserMenuBar.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/org/infinity/gui/BrowserMenuBar.java b/src/org/infinity/gui/BrowserMenuBar.java index 595d53249..dee599ea2 100644 --- a/src/org/infinity/gui/BrowserMenuBar.java +++ b/src/org/infinity/gui/BrowserMenuBar.java @@ -1881,9 +1881,8 @@ private OptionsMenu() new JCheckBoxMenuItem("Show overridden files in bold in resource tree", getPrefs().getBoolean(OPTION_HIGHLIGHT_OVERRIDDEN, true)); optionHighlightOverridden.setActionCommand("RefreshTree"); optionHighlightOverridden.addActionListener(NearInfinity.getInstance()); - optionHighlightOverridden.setToolTipText("If checked, files, that contains in game index (.key file) and located " - + "in the Override folder, will be shown in bold in the Resource Tree. " - + "This setting has no effect if override files are shown only in the Override folder"); + optionHighlightOverridden.setToolTipText("If checked, files that are listed in the chitin.key and are located in the Override folder, will be shown
" + + "in bold in the Resource Tree. This setting has no effect if override files are shown only in the Override folder."); add(optionHighlightOverridden); // optionMonitorFileChanges = // new JCheckBoxMenuItem("Autoupdate resource tree", getPrefs().getBoolean(OPTION_MONITORFILECHANGES, true)); From e02136ddf88532483317784b8aa2457b04a6e3dd Mon Sep 17 00:00:00 2001 From: Argent77 <4519923+Argent77@users.noreply.github.com> Date: Sat, 25 Apr 2020 09:47:37 +0200 Subject: [PATCH 21/25] Reference search: Improve results for textual resources (BCS, 2DA, ...) - Results list shows matching line and line number - Added support for multiple hits per resource - Jump to line number in text resource --- .../search/AbstractReferenceSearcher.java | 18 ++- .../infinity/search/ReferenceHitFrame.java | 87 +++++++++++--- .../infinity/search/ReferenceSearcher.java | 112 ++++++++++-------- 3 files changed, 149 insertions(+), 68 deletions(-) diff --git a/src/org/infinity/search/AbstractReferenceSearcher.java b/src/org/infinity/search/AbstractReferenceSearcher.java index edfc1a20c..9d4bed6ff 100644 --- a/src/org/infinity/search/AbstractReferenceSearcher.java +++ b/src/org/infinity/search/AbstractReferenceSearcher.java @@ -139,15 +139,27 @@ protected Runnable newWorker(ResourceEntry entry) /** * Registers match hit. * - * @param entry Pointer to resource in which match found - * @param name Localized name of the matched resource - * @param ref Field in the matched resource that contains founded object + * @param entry Resource in which match is found. + * @param name Localized name of the matched resource. + * @param ref Field in the matched resource that contains found object. */ synchronized void addHit(ResourceEntry entry, String name, StructEntry ref) { hitFrame.addHit(entry, name, ref); } + /** + * Registers textual match hit. + * + * @param entry Resource in which match is found. + * @param line Text content of line where match is found. + * @param lineNr Line number of the match. + */ + synchronized void addHit(ResourceEntry entry, String line, int lineNr) + { + hitFrame.addHit(entry, line, lineNr); + } + /** * Performs actual search necessary information in the resource. Search procedure * must register their results by calling method {@link #addHit}. diff --git a/src/org/infinity/search/ReferenceHitFrame.java b/src/org/infinity/search/ReferenceHitFrame.java index 92c73a99c..e8957e501 100644 --- a/src/org/infinity/search/ReferenceHitFrame.java +++ b/src/org/infinity/search/ReferenceHitFrame.java @@ -35,6 +35,7 @@ import org.infinity.resource.Resource; import org.infinity.resource.ResourceFactory; import org.infinity.resource.StructEntry; +import org.infinity.resource.TextResource; import org.infinity.resource.Viewable; import org.infinity.resource.dlg.DlgResource; import org.infinity.resource.key.FileResourceEntry; @@ -64,7 +65,7 @@ public ReferenceHitFrame(Object query, Component parent) this.parent = parent; setIconImage(Icons.getIcon(Icons.ICON_HISTORY_16).getImage()); - table = new SortableTable(new String[]{"File", "Name", "Attribute"}, + table = new SortableTable(new String[]{"File", "Name/Text", "Attribute/Line"}, new Class[]{ResourceEntry.class, String.class, String.class}, new Integer[]{100, 100, 300}); @@ -154,19 +155,20 @@ else if (event.getSource() == bsave) { } private void showEntryInViewer(int row, Viewable viewable) { + ReferenceHit hit = (ReferenceHit)table.getTableItemAt(row); if (viewable instanceof DlgResource) { DlgResource dlgRes = (DlgResource) viewable; JComponent detailViewer = dlgRes.getViewerTab(0); JTabbedPane parent = (JTabbedPane) detailViewer.getParent(); - dlgRes.selectInEdit( - ((ReferenceHit)table.getTableItemAt(row)).getStructEntry()); + dlgRes.selectInEdit(hit.getStructEntry()); // make sure we see the detail viewer parent.getModel().setSelectedIndex(parent.indexOfComponent(detailViewer)); - } else if (viewable instanceof AbstractStruct) { - ((AbstractStruct)viewable).getViewer().selectEntry( - ((ReferenceHit)table.getTableItemAt(row)).getStructEntry().getOffset()); + ((AbstractStruct)viewable).getViewer().selectEntry(hit.getStructEntry().getOffset()); + } + else if (viewable instanceof TextResource) { + ((TextResource)viewable).highlightText(hit.getLineNr(), hit.getLine()); } } @@ -200,20 +202,46 @@ public void addHit(ResourceEntry entry, String name, StructEntry ref) table.addTableItem(new ReferenceHit(entry, name, ref)); } + public void addHit(ResourceEntry entry, String line, int lineNr) + { + table.addTableItem(new ReferenceHit(entry, line, lineNr)); + } + // -------------------------- INNER CLASSES -------------------------- /** Stores a reference to a specific resource field. */ public static final class ReferenceHit implements TableItem, Comparable { + public enum Mode { + Struct, + Text, + } + + private final Mode mode; private final ResourceEntry entry; private final String name; private final StructEntry ref; + private final String line; + private final int lineNr; public ReferenceHit(ResourceEntry entry, String name, StructEntry ref) { + this.mode = Mode.Struct; this.entry = entry; this.name = name; this.ref = ref; + this.line = ""; + this.lineNr = 0; + } + + public ReferenceHit(ResourceEntry entry, String line, int lineNr) + { + this.mode = Mode.Text; + this.entry = entry; + this.name = ""; + this.ref = null; + this.line = line; + this.lineNr = lineNr; } @Override @@ -223,24 +251,33 @@ public Object getObjectAt(int columnIndex) case 0: return entry; case 1: - if (name != null) { - return name; + if (mode == Mode.Text) { + return line; } else { - if (entry instanceof FileResourceEntry) { + if (name != null) { + return name; + } else if (entry instanceof FileResourceEntry) { return entry.getActualPath().getParent().toString(); } else { return ""; } } default: - if (ref != null) { - return ref.getName() + '=' + ref; + if (mode == Mode.Text) { + return lineNr; } else { + if (ref != null) + return ref.getName() + '=' + ref; return null; } } } + public Mode getMode() + { + return mode; + } + public ResourceEntry getResource() { return entry; @@ -256,16 +293,30 @@ public StructEntry getStructEntry() return ref; } + public String getLine() + { + return line; + } + + public int getLineNr() + { + return lineNr; + } + @Override public String toString() { - final StringBuilder buf = new StringBuilder("File: "); - buf.append(entry.getResourceName()); - if (name != null) - buf.append(", Name: ").append(name); - if (ref != null) - buf.append(", Attribute: ").append(ref.getName()).append('=').append(ref); - return buf.toString(); + if (mode == Mode.Text) { + return String.format("File: %s, Line: %d, Text: %s", entry.getResourceName(), lineNr, line); + } else { + final StringBuilder buf = new StringBuilder("File: "); + buf.append(entry.getResourceName()); + if (name != null) + buf.append(", Name: ").append(name); + if (ref != null) + buf.append(", Attribute: ").append(ref.getName()).append('=').append(ref); + return buf.toString(); + } } @Override diff --git a/src/org/infinity/search/ReferenceSearcher.java b/src/org/infinity/search/ReferenceSearcher.java index 391fbaa79..bcfc3026b 100644 --- a/src/org/infinity/search/ReferenceSearcher.java +++ b/src/org/infinity/search/ReferenceSearcher.java @@ -5,6 +5,8 @@ package org.infinity.search; import java.awt.Component; +import java.io.BufferedReader; +import java.io.StringReader; import java.util.List; import java.util.Locale; import java.util.regex.Matcher; @@ -98,34 +100,34 @@ else if (o instanceof AbstractCode) { try { final ScriptType type = sourceCode instanceof Action ? ScriptType.ACTION : ScriptType.TRIGGER; final Compiler compiler = new Compiler(sourceCode.getText(), type); - final String code = compiler.getCode(); + String code = compiler.getCode(); if (compiler.getErrors().isEmpty()) { final Decompiler decompiler = new Decompiler(code, type, true); decompiler.setGenerateComments(false); decompiler.setGenerateResourcesUsed(true); - decompiler.decompile(); + code = decompiler.decompile(); - boolean hit = false; - for (final ResourceEntry resourceUsed : decompiler.getResourcesUsed()) { - final String resName = resourceUsed.getResourceName(); - if (targetName.equalsIgnoreCase(resName)) { - hit = true; - addHit(entry, entry.getSearchString(), sourceCode); - } else if (targetEntry == resourceUsed) { - // searching for symbolic spell names - String s = org.infinity.resource.spl.Viewer.getSymbolicName(targetEntry, false); - if (s != null && s.equalsIgnoreCase(resName)) { - addHit(entry, s, sourceCode); - } - } - } - if (!hit && creDeathVar != null) { - Pattern p = Pattern.compile("\\b" + creDeathVar + "\\b", Pattern.CASE_INSENSITIVE); - Matcher m = p.matcher(code); - if (m.find()) { - addHit(entry, creDeathVar, sourceCode); - } + // resref match + Pattern regName = Pattern.compile("\"" + Pattern.quote(targetEntry.getResourceRef()) + "\"", Pattern.CASE_INSENSITIVE); + // script name match + Pattern regVar = null; + if (creDeathVar != null && !creDeathVar.equalsIgnoreCase(targetEntry.getResourceRef())) + regVar = Pattern.compile("\"" + Pattern.quote(creDeathVar) + "\"", Pattern.CASE_INSENSITIVE); + // symbolic spell name match + String symbol = null; + Pattern regSymbol = null; + if (targetEntry.getExtension().equalsIgnoreCase("SPL")) { + symbol = org.infinity.resource.spl.Viewer.getSymbolicName(targetEntry, false); + if (symbol != null && !symbol.isEmpty()) + regSymbol = Pattern.compile("\\b" + Pattern.quote(symbol) + "\\b"); } + + if (decompiler.getResourcesUsed().contains(targetEntry) && regName.matcher(code).find()) + addHit(entry, entry.getSearchString(), sourceCode); + else if (regVar != null && regVar.matcher(code).find()) + addHit(entry, creDeathVar, sourceCode); + else if (regSymbol != null && regSymbol.matcher(code).find()) + addHit(entry, symbol, sourceCode); } } catch (Exception e) { System.out.println("Exception in " + dialog.getName() + " - " + sourceCode.getName()); @@ -160,21 +162,35 @@ private void searchSave(ResourceEntry entry, SavResource savfile) private void searchScript(ResourceEntry entry, BcsResource bcsfile) { - boolean hit = false; Decompiler decompiler = new Decompiler(bcsfile.getCode(), true); decompiler.setGenerateComments(false); decompiler.setGenerateResourcesUsed(true); try { String code = decompiler.decompile(); - if (decompiler.getResourcesUsed().contains(targetEntry)) { - hit = true; - addHit(entry, entry.getSearchString(), null); - } - if (!hit && creDeathVar != null) { - Pattern p = Pattern.compile("\\b" + creDeathVar + "\\b", Pattern.CASE_INSENSITIVE); - Matcher m = p.matcher(code); - if (m.find()) { - addHit(entry, creDeathVar, null); + boolean resourceExists = decompiler.getResourcesUsed().contains(targetEntry); + try (final BufferedReader br = new BufferedReader(new StringReader(code))) { + // resref match + Pattern regName = Pattern.compile("\"" + Pattern.quote(targetEntry.getResourceRef()) + "\"", Pattern.CASE_INSENSITIVE); + // script name match + Pattern regVar = null; + if (creDeathVar != null && !creDeathVar.equalsIgnoreCase(targetEntry.getResourceRef())) + regVar = Pattern.compile("\"" + Pattern.quote(creDeathVar) + "\"", Pattern.CASE_INSENSITIVE); + // symbolic spell name match + Pattern regSymbol = null; + if (targetEntry.getExtension().equalsIgnoreCase("SPL")) { + String symbol = org.infinity.resource.spl.Viewer.getSymbolicName(targetEntry, false); + if (symbol != null && !symbol.isEmpty()) + regSymbol = Pattern.compile("\\b" + Pattern.quote(symbol) + "\\b"); + } + + String line; + int lineNr = 0; + while ((line = br.readLine()) != null) { + lineNr++; + if (resourceExists && regName.matcher(line).find() || + regVar != null && regVar.matcher(line).find() || + regSymbol != null && regSymbol.matcher(line).find()) + addHit(entry, line.trim(), lineNr); } } } catch (Exception e) { @@ -288,22 +304,24 @@ private void searchTis(ResourceEntry entry, TisResource tis) private void searchText(ResourceEntry entry, PlainTextResource text) { - String name = getTargetEntry().getResourceName(); - int idx = name.lastIndexOf('.'); - if (idx > 0) { - name = name.substring(0, idx); - } - Pattern p = Pattern.compile("\\b(AP_|GA_)?" + name + "\\b", Pattern.CASE_INSENSITIVE); - Matcher m = p.matcher(text.getText()); - if (m.find()) { - addHit(entry, entry.getSearchString(), null); - } - if (creDeathVar != null && !creDeathVar.equalsIgnoreCase(name)) { - p = Pattern.compile("\\b" + creDeathVar + "\\b", Pattern.CASE_INSENSITIVE); - m = p.matcher(text.getText()); - if (m.find()) { - addHit(entry, creDeathVar, null); + String name = getTargetEntry().getResourceRef(); + Pattern regName = Pattern.compile("\\b(AP_|GA_)?" + name + "\\b", Pattern.CASE_INSENSITIVE); + Pattern regVar = null; + if (creDeathVar != null && !creDeathVar.equalsIgnoreCase(name)) + regVar = Pattern.compile("\\b" + creDeathVar + "\\b", Pattern.CASE_INSENSITIVE); + + try (final BufferedReader br = new BufferedReader(new StringReader(text.getText()))) { + String line; + int lineNr = 0; + while ((line = br.readLine()) != null) { + lineNr++; + if (regName.matcher(line).find() || + regVar != null && regVar.matcher(line).find()) { + addHit(entry, line.trim(), lineNr); + } } + } catch (Exception e) { + e.printStackTrace(); } } } From ad7ee098a1edc053a4672a6db73cc55925ef119b Mon Sep 17 00:00:00 2001 From: Argent77 <4519923+Argent77@users.noreply.github.com> Date: Sat, 25 Apr 2020 10:14:30 +0200 Subject: [PATCH 22/25] Clean up common selection fields in structured resources --- src/org/infinity/resource/AbstractStruct.java | 14 +++++ src/org/infinity/resource/EffectFactory.java | 57 +++++++++---------- src/org/infinity/resource/are/Actor.java | 16 +----- src/org/infinity/resource/are/Ambient.java | 2 +- src/org/infinity/resource/are/Animation.java | 2 +- .../infinity/resource/are/AutomapNotePST.java | 4 +- src/org/infinity/resource/are/Container.java | 5 +- src/org/infinity/resource/are/Door.java | 5 +- src/org/infinity/resource/are/Entrance.java | 2 +- src/org/infinity/resource/are/ITEPoint.java | 5 +- src/org/infinity/resource/are/RestSpawn.java | 2 +- src/org/infinity/resource/are/SpawnPoint.java | 5 +- .../are/viewer/LayerObjectEntrance.java | 6 +- src/org/infinity/resource/chu/Window.java | 3 +- .../infinity/resource/cre/CreResource.java | 15 +++-- .../infinity/resource/cre/KnownSpells.java | 2 +- .../resource/cre/SpellMemorization.java | 4 +- .../infinity/resource/gam/GamResource.java | 3 +- src/org/infinity/resource/gam/ModronMaze.java | 8 +-- .../resource/gam/ModronMazeEntry.java | 11 ++-- src/org/infinity/resource/gam/PartyNPC.java | 6 +- src/org/infinity/resource/itm/Ability.java | 7 +-- src/org/infinity/resource/maze/MazeEntry.java | 11 ++-- .../infinity/resource/maze/MazeResource.java | 8 +-- .../infinity/resource/other/VvcResource.java | 5 +- src/org/infinity/resource/sto/ItemSale.java | 3 +- src/org/infinity/resource/sto/ItemSale11.java | 3 +- .../resource/vef/AbstractComponent.java | 4 +- src/org/infinity/resource/wed/Door.java | 4 +- 29 files changed, 98 insertions(+), 124 deletions(-) diff --git a/src/org/infinity/resource/AbstractStruct.java b/src/org/infinity/resource/AbstractStruct.java index 9747ada70..3086f51fb 100644 --- a/src/org/infinity/resource/AbstractStruct.java +++ b/src/org/infinity/resource/AbstractStruct.java @@ -51,6 +51,20 @@ public abstract class AbstractStruct extends AbstractTableModel implements Struc public static final String COMMON_UNUSED = "Unused"; public static final String COMMON_UNUSED_BYTES = "Unused bytes?"; + // Commonly used string arrays + public static final String[] OPTION_NOYES = {"No", "Yes"}; + public static final String[] OPTION_YESNO = {"Yes", "No"}; + public static final String[] OPTION_SCHEDULE = {"Not active", + "00:30-01:29", "01:30-02:29", "02:30-03:29", "03:30-04:29", + "04:30-05:29", "05:30-06:29", "06:30-07:29", "07:30-08:29", + "08:30-09:29", "09:30-10:29", "10:30-11:29", "11:30-12:29", + "12:30-13:29", "13:30-14:29", "14:30-15:29", "15:30-16:29", + "16:30-17:29", "17:30-18:29", "18:30-19:29", "19:30-20:29", + "20:30-21:29", "21:30-22:29", "22:30-23:29", "23:30-00:29"}; + public static final String[] OPTION_ORIENTATION = { "South", "SSW", "SW", "WSW", "West", "WNW", "NW", "NNW", + "North", "NNE", "NE", "ENE", "East", "ESE", "SE", "SSE" }; + + /** Identifies the intention to removal of rows or columns. */ public static final int WILL_BE_DELETE = -2; diff --git a/src/org/infinity/resource/EffectFactory.java b/src/org/infinity/resource/EffectFactory.java index 01951c2bf..5966c884a 100644 --- a/src/org/infinity/resource/EffectFactory.java +++ b/src/org/infinity/resource/EffectFactory.java @@ -34,7 +34,6 @@ import org.infinity.datatype.Unknown; import org.infinity.datatype.UnsignDecNumber; import org.infinity.datatype.UpdateListener; -import org.infinity.resource.are.Actor; import org.infinity.resource.itm.ItmResource; import org.infinity.util.IdsMapEntry; import org.infinity.util.LongIntegerHashMap; @@ -129,8 +128,6 @@ public static enum EffectEntry { public static final LongIntegerHashMap m_colorloc = new LongIntegerHashMap(); public static final LongIntegerHashMap m_proj_iwd = new LongIntegerHashMap(); public static final String[] s_inctype = {"Increment", "Set", "Set % of"}; - public static final String[] s_yesno = {"Yes", "No"}; - public static final String[] s_noyes = {"No", "Yes"}; public static final String[] s_visuals = { // 0..9 @@ -940,7 +937,7 @@ private static boolean updateOpcode342(AbstractStruct struct) throws Exception StructEntry newEntry = null; switch (param2) { case 1: - newEntry = new Bitmap(getEntryData(struct, EffectEntry.IDX_PARAM1), 0, 4, "Enabled?", s_noyes); + newEntry = new Bitmap(getEntryData(struct, EffectEntry.IDX_PARAM1), 0, 4, "Enabled?", AbstractStruct.OPTION_NOYES); break; case 2: newEntry = new ColorValue(getEntryData(struct, EffectEntry.IDX_PARAM1), 0, 4, "Color"); @@ -2029,7 +2026,7 @@ private String makeEffectParamsGeneric(Datatype parent, ByteBuffer buffer, int o if (Profile.getEngine() == Profile.Engine.PST) { s.add(new DecNumber(buffer, offset, 4, AbstractStruct.COMMON_UNUSED)); } else { - s.add(new Bitmap(buffer, offset, 4, "Display text?", s_yesno)); + s.add(new Bitmap(buffer, offset, 4, "Display text?", AbstractStruct.OPTION_YESNO)); } final String[] s_type; if (Profile.getEngine() == Profile.Engine.BG1) { @@ -2209,7 +2206,7 @@ private String makeEffectParamsGeneric(Datatype parent, ByteBuffer buffer, int o case 32: // Raise dead s.add(new DecNumber(buffer, offset, 4, AbstractStruct.COMMON_UNUSED)); if (Profile.isEnhancedEdition()) { - s.add(new Bitmap(buffer, offset + 4, 4, "Restore creature animation?", s_noyes)); + s.add(new Bitmap(buffer, offset + 4, 4, "Restore creature animation?", AbstractStruct.OPTION_NOYES)); } else { s.add(new DecNumber(buffer, offset + 4, 4, AbstractStruct.COMMON_UNUSED)); } @@ -2246,7 +2243,7 @@ private String makeEffectParamsGeneric(Datatype parent, ByteBuffer buffer, int o s.add(new DecNumber(buffer, offset, 4, AbstractStruct.COMMON_UNUSED)); if (Profile.getEngine() == Profile.Engine.IWD || Profile.getEngine() == Profile.Engine.IWD2 || Profile.isEnhancedEdition()) { - s.add(new Bitmap(buffer, offset + 4, 4, "Wake on damage?", s_yesno)); + s.add(new Bitmap(buffer, offset + 4, 4, "Wake on damage?", AbstractStruct.OPTION_YESNO)); } else { s.add(new DecNumber(buffer, offset + 4, 4, AbstractStruct.COMMON_UNUSED)); } @@ -2410,7 +2407,7 @@ private String makeEffectParamsGeneric(Datatype parent, ByteBuffer buffer, int o break; case 68: // Unsummon creature - s.add(new Bitmap(buffer, offset, 4, "Display text?", s_noyes)); + s.add(new Bitmap(buffer, offset, 4, "Display text?", AbstractStruct.OPTION_NOYES)); s.add(new DecNumber(buffer, offset + 4, 4, AbstractStruct.COMMON_UNUSED)); if (Profile.isEnhancedEdition()) { restype = "VEF:VVC:BAM"; @@ -2851,7 +2848,7 @@ private String makeEffectParamsGeneric(Datatype parent, ByteBuffer buffer, int o new String[]{"Cast normally", "Cast instantly (ignore level)", "Cast instantly (at level)"})); } else { - s.add(new Bitmap(buffer, offset + 4, 4, "Cast instantly?", s_noyes)); + s.add(new Bitmap(buffer, offset + 4, 4, "Cast instantly?", AbstractStruct.OPTION_NOYES)); } restype = "SPL"; break; @@ -3051,7 +3048,7 @@ private String makeEffectParamsGeneric(Datatype parent, ByteBuffer buffer, int o makeEffectParamsDefault(buffer, offset, s); } else { s.add(new DecNumber(buffer, offset, 4, AbstractStruct.COMMON_UNUSED)); - s.add(new Bitmap(buffer, offset + 4, 4, "Pass walls?", s_yesno)); + s.add(new Bitmap(buffer, offset + 4, 4, "Pass walls?", AbstractStruct.OPTION_YESNO)); } break; @@ -3141,14 +3138,14 @@ private String makeEffectParamsBG2(Datatype parent, ByteBuffer buffer, int offse if (Profile.isEnhancedEdition() && ResourceFactory.resourceExists("DIR.IDS")) { s.add(new IdsBitmap(buffer, offset + 4, 4, "Orientation", "DIR.IDS")); } else { - s.add(new Bitmap(buffer, offset + 4, 4, "Orientation", Actor.s_orientation)); + s.add(new Bitmap(buffer, offset + 4, 4, "Orientation", AbstractStruct.OPTION_ORIENTATION)); } restype = "ARE"; break; case 188: // Increase spells cast per round s.add(new DecNumber(buffer, offset, 4, AbstractStruct.COMMON_UNUSED)); - s.add(new Bitmap(buffer, offset + 4, 4, "Cleanse aura?", s_noyes)); + s.add(new Bitmap(buffer, offset + 4, 4, "Cleanse aura?", AbstractStruct.OPTION_NOYES)); break; case 189: // Increase casting speed factor @@ -3313,7 +3310,7 @@ private String makeEffectParamsBG2(Datatype parent, ByteBuffer buffer, int offse case 218: // Stoneskin effect s.add(new DecNumber(buffer, offset, 4, "# skins")); if (Profile.isEnhancedEdition()) { - s.add(new Bitmap(buffer, offset + 4, 4, "Use dice?", s_noyes)); + s.add(new Bitmap(buffer, offset + 4, 4, "Use dice?", AbstractStruct.OPTION_NOYES)); } else { s.add(new DecNumber(buffer, offset + 4, 4, AbstractStruct.COMMON_UNUSED)); } @@ -3471,7 +3468,7 @@ private String makeEffectParamsBG2(Datatype parent, ByteBuffer buffer, int offse case 239: // Farsight s.add(new DecNumber(buffer, offset, 4, AbstractStruct.COMMON_UNUSED)); - s.add(new Bitmap(buffer, offset + 4, 4, "Can view unexplored?", s_noyes)); + s.add(new Bitmap(buffer, offset + 4, 4, "Can view unexplored?", AbstractStruct.OPTION_NOYES)); break; case 240: // Remove portrait icon @@ -3500,7 +3497,7 @@ private String makeEffectParamsBG2(Datatype parent, ByteBuffer buffer, int offse } case 243: // Drain item charges - s.add(new Bitmap(buffer, offset, 4, "Include weapons?", s_noyes)); + s.add(new Bitmap(buffer, offset, 4, "Include weapons?", AbstractStruct.OPTION_NOYES)); if (Profile.isEnhancedEdition()) { s.add(new DecNumber(buffer, offset + 4, 4, "# to drain")); } else { @@ -3578,7 +3575,7 @@ private String makeEffectParamsBG2(Datatype parent, ByteBuffer buffer, int offse case 264: // Drop item s.add(new DecNumber(buffer, offset, 4, AbstractStruct.COMMON_UNUSED)); - s.add(new Bitmap(buffer, offset + 4, 4, "Only quick weapons?", s_noyes)); + s.add(new Bitmap(buffer, offset + 4, 4, "Only quick weapons?", AbstractStruct.OPTION_NOYES)); break; case 265: // Set global variable @@ -3704,7 +3701,7 @@ private String makeEffectParamsBG2(Datatype parent, ByteBuffer buffer, int offse s.add(new Bitmap(buffer, offset, 4, "Store party location", new String[]{ "Use pocket plane field", "Use party location field", "Do not store" })); - s.add(new Bitmap(buffer, offset + 4, 4, "Use custom script?", s_noyes)); + s.add(new Bitmap(buffer, offset + 4, 4, "Use custom script?", AbstractStruct.OPTION_NOYES)); restype = "BCS"; } else { s.add(new DecNumber(buffer, offset, 4, AbstractStruct.COMMON_UNUSED)); @@ -4021,7 +4018,7 @@ private String makeEffectParamsBG2(Datatype parent, ByteBuffer buffer, int offse new String[]{"Unknown", "Body heat", "Blood color", "Unknown", "Personal space"}); switch (bmp.getValue()) { - case 1: s.add(new Bitmap(buffer, offset, 4, "Enabled?", s_noyes)); break; + case 1: s.add(new Bitmap(buffer, offset, 4, "Enabled?", AbstractStruct.OPTION_NOYES)); break; case 2: s.add(new ColorValue(buffer, offset, 4, "Color")); break; default: s.add(new DecNumber(buffer, offset, 4, "Value")); } @@ -4179,7 +4176,7 @@ private String makeEffectParamsBG2(Datatype parent, ByteBuffer buffer, int offse case 361: // Cast spell on critical miss if (Profile.isEnhancedEdition()) { s.add(new DecNumber(buffer, offset, 4, AbstractStruct.COMMON_UNUSED)); - s.add(new Bitmap(buffer, offset + 4, 4, "Current weapon only?", s_noyes)); + s.add(new Bitmap(buffer, offset + 4, 4, "Current weapon only?", AbstractStruct.OPTION_NOYES)); restype = "SPL"; } else { makeEffectParamsDefault(buffer, offset, s); @@ -4189,7 +4186,7 @@ private String makeEffectParamsBG2(Datatype parent, ByteBuffer buffer, int offse case 362: // Critical miss bonus if (Profile.isEnhancedEdition()) { s.add(new DecNumber(buffer, offset, 4, "Value")); - s.add(new Bitmap(buffer, offset + 4, 4, "Current weapon only?", s_noyes)); + s.add(new Bitmap(buffer, offset + 4, 4, "Current weapon only?", AbstractStruct.OPTION_NOYES)); } else { makeEffectParamsDefault(buffer, offset, s); } @@ -4206,8 +4203,8 @@ private String makeEffectParamsBG2(Datatype parent, ByteBuffer buffer, int offse case 365: // Make unselectable if (Profile.isEnhancedEdition()) { - s.add(new Bitmap(buffer, offset, 4, "Disable dialogue?", s_yesno)); - s.add(new Bitmap(buffer, offset + 4, 4, "Disable AI?", s_yesno)); + s.add(new Bitmap(buffer, offset, 4, "Disable dialogue?", AbstractStruct.OPTION_YESNO)); + s.add(new Bitmap(buffer, offset + 4, 4, "Disable AI?", AbstractStruct.OPTION_YESNO)); } else { makeEffectParamsDefault(buffer, offset, s); } @@ -4226,7 +4223,7 @@ private String makeEffectParamsBG2(Datatype parent, ByteBuffer buffer, int offse case 367: // Minimum base stats if (Profile.isEnhancedEdition()) { s.add(new DecNumber(buffer, offset, 4, AbstractStruct.COMMON_UNUSED)); - s.add(new Bitmap(buffer, offset + 4, 4, "Enabled?", s_noyes)); + s.add(new Bitmap(buffer, offset + 4, 4, "Enabled?", AbstractStruct.OPTION_NOYES)); } else { makeEffectParamsDefault(buffer, offset, s); } @@ -4482,7 +4479,7 @@ private String makeEffectParamsIWD(Datatype parent, ByteBuffer buffer, int offse case 188: // Increase spells cast per round s.add(new DecNumber(buffer, offset, 4, AbstractStruct.COMMON_UNUSED)); - s.add(new Bitmap(buffer, offset + 4, 4, "Cleanse aura?", s_noyes)); + s.add(new Bitmap(buffer, offset + 4, 4, "Cleanse aura?", AbstractStruct.OPTION_NOYES)); break; case 189: // Increase casting speed factor @@ -4717,7 +4714,7 @@ private String makeEffectParamsIWD(Datatype parent, ByteBuffer buffer, int offse case 285: // Force sleep s.add(new DecNumber(buffer, offset, 4, AbstractStruct.COMMON_UNUSED)); - s.add(new Bitmap(buffer, offset + 4, 4, "Wake on damage?", s_yesno)); + s.add(new Bitmap(buffer, offset + 4, 4, "Wake on damage?", AbstractStruct.OPTION_YESNO)); break; case 288: // Set spell state @@ -4757,7 +4754,7 @@ private String makeEffectParamsIWD2(Datatype parent, ByteBuffer buffer, int offs case 188: // Increase spells cast per round s.add(new DecNumber(buffer, offset, 4, AbstractStruct.COMMON_UNUSED)); - s.add(new Bitmap(buffer, offset + 4, 4, "Cleanse aura?", s_noyes)); + s.add(new Bitmap(buffer, offset + 4, 4, "Cleanse aura?", AbstractStruct.OPTION_NOYES)); break; case 189: // Increase casting speed factor @@ -4833,7 +4830,7 @@ private String makeEffectParamsIWD2(Datatype parent, ByteBuffer buffer, int offs case 193: // Invisibility detection s.add(new DecNumber(buffer, offset, 4, AbstractStruct.COMMON_UNUSED)); - s.add(new Bitmap(buffer, offset + 4, 4, "Ignore visibility?", s_noyes)); + s.add(new Bitmap(buffer, offset + 4, 4, "Ignore visibility?", AbstractStruct.OPTION_NOYES)); break; case 206: // Protection from spell @@ -4975,7 +4972,7 @@ private String makeEffectParamsIWD2(Datatype parent, ByteBuffer buffer, int offs case 285: // Force sleep case 419: // Unconsciousness s.add(new DecNumber(buffer, offset, 4, AbstractStruct.COMMON_UNUSED)); - s.add(new Bitmap(buffer, offset + 4, 4, "Wake on damage?", s_yesno)); + s.add(new Bitmap(buffer, offset + 4, 4, "Wake on damage?", AbstractStruct.OPTION_YESNO)); break; case 288: // Set spell state @@ -5260,7 +5257,7 @@ private int makeEffectParam25(Datatype parent, ByteBuffer buffer, int offset, Li break; case 145: // Disable spellcasting - s.add(new Bitmap(buffer, offset, 4, "Display message?", s_yesno)); + s.add(new Bitmap(buffer, offset, 4, "Display message?", AbstractStruct.OPTION_YESNO)); break; case 178: // THAC0 vs. type bonus @@ -5349,7 +5346,7 @@ private int makeEffectParam25(Datatype parent, ByteBuffer buffer, int offset, Li break; case 365: // Make unselectable - s.add(new Bitmap(buffer, offset, 4, "Use purple selection color?", s_yesno)); + s.add(new Bitmap(buffer, offset, 4, "Use purple selection color?", AbstractStruct.OPTION_YESNO)); break; case 366: // Apply spell on movement diff --git a/src/org/infinity/resource/are/Actor.java b/src/org/infinity/resource/are/Actor.java index f8d41d155..29d04443f 100644 --- a/src/org/infinity/resource/are/Actor.java +++ b/src/org/infinity/resource/are/Actor.java @@ -65,20 +65,10 @@ public final class Actor extends AbstractStruct implements AddRemovable, HasView public static final String ARE_ACTOR_CRE_FILE = "CRE file"; public static final String ARE_ACTOR_NAME_ALT = "Alternate actor name"; - public static final String[] s_orientation = { "South", "SSW", "SW", "WSW", "West", "WNW", "NW", "NNW", - "North", "NNE", "NE", "ENE", "East", "ESE", "SE", "SSE" }; - public static final String[] s_noyes = {"No", "Yes"}; public static final String[] s_flags = {"CRE attached", "CRE not attached", "Has seen party", "Toggle invulnerability", "Override script name"}; public static final String[] s_flags_iwd = {"CRE attached", "CRE not attached", "Has seen party", "Toggle invulnerability"}; - public static final String[] s_schedule = {"Not active", "00:30-01:29", "01:30-02:29", "02:30-03:29", - "03:30-04:29", "04:30-05:29", "05:30-06:29", "06:30-07:29", - "07:30-08:29", "08:30-09:29", "09:30-10:29", "10:30-11:29", - "11:30-12:29", "12:30-13:29", "13:30-14:29", "14:30-15:29", - "15:30-16:29", "16:30-17:29", "17:30-18:29", "18:30-19:29", - "19:30-20:29", "20:30-21:29", "21:30-22:29", "22:30-23:29", - "23:30-00:29"}; public static final String[] s_diff = {"None", "Level 1", "Level 2", "Level 3"}; public Actor() throws Exception @@ -199,7 +189,7 @@ public int read(ByteBuffer buffer, int offset) throws Exception } else { addField(new Flag(buffer, offset + 40, 4, ARE_ACTOR_FLAGS, s_flags)); } - addField(new Bitmap(buffer, offset + 44, 2, ARE_ACTOR_IS_SPAWNED, s_noyes)); + addField(new Bitmap(buffer, offset + 44, 2, ARE_ACTOR_IS_SPAWNED, OPTION_NOYES)); addField(new TextString(buffer, offset + 46, 1, ARE_ACTOR_RESREF_LETTER)); if (Profile.getEngine() == Profile.Engine.IWD2) { addField(new Flag(buffer, offset + 47, 1, ARE_ACTOR_DIFFICULTY, s_diff)); @@ -207,12 +197,12 @@ public int read(ByteBuffer buffer, int offset) throws Exception addField(new Unknown(buffer, offset + 47, 1)); } addField(new AnimateBitmap(buffer, offset + 48, 4, ARE_ACTOR_ANIMATION)); - addField(new Bitmap(buffer, offset + 52, 2, ARE_ACTOR_ORIENTATION, s_orientation)); + addField(new Bitmap(buffer, offset + 52, 2, ARE_ACTOR_ORIENTATION, OPTION_ORIENTATION)); addField(new Unknown(buffer, offset + 54, 2)); addField(new DecNumber(buffer, offset + 56, 4, ARE_ACTOR_EXPIRY_TIME)); addField(new DecNumber(buffer, offset + 60, 2, ARE_ACTOR_WANDER_DISTANCE)); addField(new DecNumber(buffer, offset + 62, 2, ARE_ACTOR_FOLLOW_DISTANCE)); - addField(new Flag(buffer, offset + 64, 4, ARE_ACTOR_PRESENT_AT, s_schedule)); + addField(new Flag(buffer, offset + 64, 4, ARE_ACTOR_PRESENT_AT, OPTION_SCHEDULE)); addField(new DecNumber(buffer, offset + 68, 4, ARE_ACTOR_NUM_TIMES_TALKED_TO)); addField(new ResourceRef(buffer, offset + 72, ARE_ACTOR_DIALOG, "DLG")); addField(new ResourceRef(buffer, offset + 80, ARE_ACTOR_SCRIPT_OVERRIDE, "BCS")); diff --git a/src/org/infinity/resource/are/Ambient.java b/src/org/infinity/resource/are/Ambient.java index f1f24ac24..86193ce59 100644 --- a/src/org/infinity/resource/are/Ambient.java +++ b/src/org/infinity/resource/are/Ambient.java @@ -75,7 +75,7 @@ public int read(ByteBuffer buffer, int offset) throws Exception addField(new Unknown(buffer, offset + 130, 2)); addField(new DecNumber(buffer, offset + 132, 4, ARE_AMBIENT_INTERVAL_BASE)); addField(new DecNumber(buffer, offset + 136, 4, ARE_AMBIENT_INTERVAL_VARIATION)); - addField(new Flag(buffer, offset + 140, 4, ARE_AMBIENT_ACTIVE_AT, Actor.s_schedule)); + addField(new Flag(buffer, offset + 140, 4, ARE_AMBIENT_ACTIVE_AT, OPTION_SCHEDULE)); addField(new Flag(buffer, offset + 144, 4, ARE_AMBIENT_FLAGS, s_flag)); addField(new Unknown(buffer, offset + 148, 64)); return offset + 212; diff --git a/src/org/infinity/resource/are/Animation.java b/src/org/infinity/resource/are/Animation.java index 3539efae2..e4ae8eb67 100644 --- a/src/org/infinity/resource/are/Animation.java +++ b/src/org/infinity/resource/are/Animation.java @@ -68,7 +68,7 @@ public int read(ByteBuffer buffer, int offset) throws Exception addField(new TextString(buffer, offset, 32, ARE_ANIMATION_NAME)); addField(new DecNumber(buffer, offset + 32, 2, ARE_ANIMATION_LOCATION_X)); addField(new DecNumber(buffer, offset + 34, 2, ARE_ANIMATION_LOCATION_Y)); - addField(new Flag(buffer, offset + 36, 4, ARE_ANIMATION_ACTIVE_AT, Actor.s_schedule)); + addField(new Flag(buffer, offset + 36, 4, ARE_ANIMATION_ACTIVE_AT, OPTION_SCHEDULE)); if (Profile.isEnhancedEdition()) { addField(new ResourceRef(buffer, offset + 40, ARE_ANIMATION_RESREF, "BAM", "WBM", "PVRZ")); } else { diff --git a/src/org/infinity/resource/are/AutomapNotePST.java b/src/org/infinity/resource/are/AutomapNotePST.java index 96f9f59ab..c7d503c08 100644 --- a/src/org/infinity/resource/are/AutomapNotePST.java +++ b/src/org/infinity/resource/are/AutomapNotePST.java @@ -24,8 +24,6 @@ public final class AutomapNotePST extends AbstractStruct implements AddRemovable public static final String ARE_AUTOMAP_TEXT = "Text"; public static final String ARE_AUTOMAP_READ_ONLY = "Is read only?"; - public static final String[] s_noyes = { "No", "Yes" }; - AutomapNotePST() throws Exception { super(null, ARE_AUTOMAP, StreamUtils.getByteBuffer(532), 0); @@ -52,7 +50,7 @@ public int read(ByteBuffer buffer, int offset) throws Exception addField(new DecNumber(buffer, offset, 4, ARE_AUTOMAP_LOCATION_X)); addField(new DecNumber(buffer, offset + 4, 4, ARE_AUTOMAP_LOCATION_Y)); addField(new TextString(buffer, offset + 8, 500, ARE_AUTOMAP_TEXT)); - addField(new Bitmap(buffer, offset + 508, 4, ARE_AUTOMAP_READ_ONLY, s_noyes)); + addField(new Bitmap(buffer, offset + 508, 4, ARE_AUTOMAP_READ_ONLY, OPTION_NOYES)); addField(new Unknown(buffer, offset + 512, 20)); return offset + 532; } diff --git a/src/org/infinity/resource/are/Container.java b/src/org/infinity/resource/are/Container.java index e19f1bd28..228ffe5e2 100644 --- a/src/org/infinity/resource/are/Container.java +++ b/src/org/infinity/resource/are/Container.java @@ -59,7 +59,6 @@ public final class Container extends AbstractStruct implements AddRemovable, Has public static final String[] s_type = { "", "Bag", "Chest", "Drawer", "Pile", "Table", "Shelf", "Altar", "Non-visible", "Spellbook", "Body", "Barrel", "Crate"}; - public static final String[] s_noyes = {"No", "Yes"}; public static final String[] s_flag = { "No flags set", "Locked", "Disable if no owner", "Magical lock", "Trap resets", "Remove only", "Disabled", "EE: Don't clear" }; @@ -225,8 +224,8 @@ public int read(ByteBuffer buffer, int offset) throws Exception addField(new Flag(buffer, offset + 40, 4, ARE_CONTAINER_FLAGS, s_flag)); addField(new DecNumber(buffer, offset + 44, 2, ARE_CONTAINER_TRAP_DETECTION_DIFFICULTY)); addField(new DecNumber(buffer, offset + 46, 2, ARE_CONTAINER_TRAP_REMOVAL_DIFFICULTY)); - addField(new Bitmap(buffer, offset + 48, 2, ARE_CONTAINER_TRAPPED, s_noyes)); - addField(new Bitmap(buffer, offset + 50, 2, ARE_CONTAINER_TRAP_DETECTED, s_noyes)); + addField(new Bitmap(buffer, offset + 48, 2, ARE_CONTAINER_TRAPPED, OPTION_NOYES)); + addField(new Bitmap(buffer, offset + 50, 2, ARE_CONTAINER_TRAP_DETECTED, OPTION_NOYES)); addField(new DecNumber(buffer, offset + 52, 2, ARE_CONTAINER_LAUNCH_POINT_X)); addField(new DecNumber(buffer, offset + 54, 2, ARE_CONTAINER_LAUNCH_POINT_Y)); addField(new DecNumber(buffer, offset + 56, 2, ARE_CONTAINER_BOUNDING_BOX_LEFT)); diff --git a/src/org/infinity/resource/are/Door.java b/src/org/infinity/resource/are/Door.java index e2f0fe6e5..b323e92dd 100644 --- a/src/org/infinity/resource/are/Door.java +++ b/src/org/infinity/resource/are/Door.java @@ -74,7 +74,6 @@ public final class Door extends AbstractStruct implements AddRemovable, HasVerti public static final String ARE_DOOR_SPEAKER_NAME = "Speaker name"; public static final String ARE_DOOR_DIALOG = "Dialogue"; - public static final String[] s_noyes = {"No", "Yes"}; public static final String[] s_flag = {"No flags set", "Door open", "Door locked", "Trap resets", "Detectable trap", "Door forced", "Cannot close", "Door located", "Door secret", "Secret door detected", "Can be looked through", @@ -246,8 +245,8 @@ public int read(ByteBuffer buffer, int offset) throws Exception addField(new DecNumber(buffer, offset + 104, 4, ARE_DOOR_CURSOR_INDEX)); addField(new DecNumber(buffer, offset + 108, 2, ARE_DOOR_TRAP_DETECTION_DIFFICULTY)); addField(new DecNumber(buffer, offset + 110, 2, ARE_DOOR_TRAP_REMOVAL_DIFFICULTY)); - addField(new Bitmap(buffer, offset + 112, 2, ARE_DOOR_TRAPPED, s_noyes)); - addField(new Bitmap(buffer, offset + 114, 2, ARE_DOOR_TRAP_DETECTED, s_noyes)); + addField(new Bitmap(buffer, offset + 112, 2, ARE_DOOR_TRAPPED, OPTION_NOYES)); + addField(new Bitmap(buffer, offset + 114, 2, ARE_DOOR_TRAP_DETECTED, OPTION_NOYES)); addField(new DecNumber(buffer, offset + 116, 2, ARE_DOOR_LAUNCH_POINT_X)); addField(new DecNumber(buffer, offset + 118, 2, ARE_DOOR_LAUNCH_POINT_Y)); addField(new ResourceRef(buffer, offset + 120, ARE_DOOR_KEY, "ITM")); diff --git a/src/org/infinity/resource/are/Entrance.java b/src/org/infinity/resource/are/Entrance.java index 67bc0ca92..59bfc8ad5 100644 --- a/src/org/infinity/resource/are/Entrance.java +++ b/src/org/infinity/resource/are/Entrance.java @@ -49,7 +49,7 @@ public int read(ByteBuffer buffer, int offset) throws Exception addField(new TextString(buffer, offset, 32, ARE_ENTRANCE_NAME)); addField(new DecNumber(buffer, offset + 32, 2, ARE_ENTRANCE_LOCATION_X)); addField(new DecNumber(buffer, offset + 34, 2, ARE_ENTRANCE_LOCATION_Y)); - addField(new Bitmap(buffer, offset + 36, 4, ARE_ENTRANCE_ORIENTATION, Actor.s_orientation)); + addField(new Bitmap(buffer, offset + 36, 4, ARE_ENTRANCE_ORIENTATION, OPTION_ORIENTATION)); addField(new Unknown(buffer, offset + 40, 64)); return offset + 104; } diff --git a/src/org/infinity/resource/are/ITEPoint.java b/src/org/infinity/resource/are/ITEPoint.java index 50fc1e733..2c4445e8f 100644 --- a/src/org/infinity/resource/are/ITEPoint.java +++ b/src/org/infinity/resource/are/ITEPoint.java @@ -65,7 +65,6 @@ public final class ITEPoint extends AbstractStruct implements AddRemovable, HasV "Trap set off by enemy", "Tutorial trigger", "Trap set off by NPC", "Trigger silent", "Trigger deactivated", "Cannot be passed by NPC", "Use activation point", "Connected to door"}; - public static final String[] s_noyes = {"No", "Yes"}; public ITEPoint() throws Exception { @@ -174,8 +173,8 @@ public int read(ByteBuffer buffer, int offset) throws Exception addField(new StringRef(buffer, offset + 100, ARE_TRIGGER_INFO_POINT_TEXT)); addField(new DecNumber(buffer, offset + 104, 2, ARE_TRIGGER_TRAP_DETECTION_DIFFICULTY)); addField(new DecNumber(buffer, offset + 106, 2, ARE_TRIGGER_TRAP_REMOVAL_DIFFICULTY)); - addField(new Bitmap(buffer, offset + 108, 2, ARE_TRIGGER_TRAPPED, s_noyes)); - addField(new Bitmap(buffer, offset + 110, 2, ARE_TRIGGER_TRAP_DETECTED, s_noyes)); + addField(new Bitmap(buffer, offset + 108, 2, ARE_TRIGGER_TRAPPED, OPTION_NOYES)); + addField(new Bitmap(buffer, offset + 110, 2, ARE_TRIGGER_TRAP_DETECTED, OPTION_NOYES)); addField(new DecNumber(buffer, offset + 112, 2, ARE_TRIGGER_LAUNCH_POINT_X)); addField(new DecNumber(buffer, offset + 114, 2, ARE_TRIGGER_LAUNCH_POINT_Y)); addField(new ResourceRef(buffer, offset + 116, ARE_TRIGGER_KEY, "ITM")); diff --git a/src/org/infinity/resource/are/RestSpawn.java b/src/org/infinity/resource/are/RestSpawn.java index eed2368ec..ceed24f60 100644 --- a/src/org/infinity/resource/are/RestSpawn.java +++ b/src/org/infinity/resource/are/RestSpawn.java @@ -54,7 +54,7 @@ public int read(ByteBuffer buffer, int offset) throws Exception addField(new DecNumber(buffer, offset + 160, 2, ARE_RESTSPAWN_WANDER_DISTANCE)); addField(new DecNumber(buffer, offset + 162, 2, ARE_RESTSPAWN_FOLLOW_DISTANCE)); addField(new DecNumber(buffer, offset + 164, 2, ARE_RESTSPAWN_MAX_CREATURES)); - addField(new Bitmap(buffer, offset + 166, 2, ARE_RESTSPAWN_ACTIVE, new String[]{"No", "Yes"})); + addField(new Bitmap(buffer, offset + 166, 2, ARE_RESTSPAWN_ACTIVE, OPTION_NOYES)); addField(new DecNumber(buffer, offset + 168, 2, ARE_RESTSPAWN_PROBABILITY_DAY)); addField(new DecNumber(buffer, offset + 170, 2, ARE_RESTSPAWN_PROBABILITY_NIGHT)); addField(new Unknown(buffer, offset + 172, 56)); diff --git a/src/org/infinity/resource/are/SpawnPoint.java b/src/org/infinity/resource/are/SpawnPoint.java index 00cd7811b..404c64103 100644 --- a/src/org/infinity/resource/are/SpawnPoint.java +++ b/src/org/infinity/resource/are/SpawnPoint.java @@ -41,7 +41,6 @@ public final class SpawnPoint extends AbstractStruct implements AddRemovable public static final String ARE_SPAWN_COUNTDOWN = "Countdown"; public static final String ARE_SPAWN_WEIGHT_FMT = "Spawn weight %d"; - public static final String[] s_noyes = { "No", "Yes" }; public static final String[] s_method = {"No flags set", "Spawn until paused", "Disable after spawn", "Spawn paused"}; @@ -83,8 +82,8 @@ public int read(ByteBuffer buffer, int offset) throws Exception addField(new DecNumber(buffer, offset + 128, 2, ARE_SPAWN_WANDER_DISTANCE)); addField(new DecNumber(buffer, offset + 130, 2, ARE_SPAWN_FOLLOW_DISTANCE)); addField(new DecNumber(buffer, offset + 132, 2, ARE_SPAWN_MAX_CREATURES)); - addField(new Bitmap(buffer, offset + 134, 2, ARE_SPAWN_ACTIVE, s_noyes)); - addField(new Flag(buffer, offset + 136, 4, ARE_SPAWN_ACTIVE_AT, Actor.s_schedule)); + addField(new Bitmap(buffer, offset + 134, 2, ARE_SPAWN_ACTIVE, OPTION_NOYES)); + addField(new Flag(buffer, offset + 136, 4, ARE_SPAWN_ACTIVE_AT, OPTION_SCHEDULE)); addField(new DecNumber(buffer, offset + 140, 2, ARE_SPAWN_PROBABILITY_DAY)); addField(new DecNumber(buffer, offset + 142, 2, ARE_SPAWN_PROBABILITY_NIGHT)); if (Profile.isEnhancedEdition()) { diff --git a/src/org/infinity/resource/are/viewer/LayerObjectEntrance.java b/src/org/infinity/resource/are/viewer/LayerObjectEntrance.java index 0ba295907..92f4b614c 100644 --- a/src/org/infinity/resource/are/viewer/LayerObjectEntrance.java +++ b/src/org/infinity/resource/are/viewer/LayerObjectEntrance.java @@ -11,8 +11,8 @@ import org.infinity.gui.layeritem.AbstractLayerItem; import org.infinity.gui.layeritem.IconLayerItem; import org.infinity.icon.Icons; +import org.infinity.resource.AbstractStruct; import org.infinity.resource.Viewable; -import org.infinity.resource.are.Actor; import org.infinity.resource.are.AreResource; import org.infinity.resource.are.Entrance; import org.infinity.resource.are.viewer.icon.ViewerIcons; @@ -40,9 +40,9 @@ public LayerObjectEntrance(AreResource parent, Entrance entrance) location.x = ((IsNumeric)entrance.getAttribute(Entrance.ARE_ENTRANCE_LOCATION_X)).getValue(); location.y = ((IsNumeric)entrance.getAttribute(Entrance.ARE_ENTRANCE_LOCATION_Y)).getValue(); int o = ((IsNumeric)entrance.getAttribute(Entrance.ARE_ENTRANCE_ORIENTATION)).getValue(); - if (o < 0) o = 0; else if (o >= Actor.s_orientation.length) o = Actor.s_orientation.length - 1; + if (o < 0) o = 0; else if (o >= AbstractStruct.OPTION_ORIENTATION.length) o = AbstractStruct.OPTION_ORIENTATION.length - 1; final String name = entrance.getAttribute(Entrance.ARE_ENTRANCE_NAME).toString(); - msg = String.format("%s (%s)", name, Actor.s_orientation[o]); + msg = String.format("%s (%s)", name, AbstractStruct.OPTION_ORIENTATION[o]); } catch (Exception e) { e.printStackTrace(); } diff --git a/src/org/infinity/resource/chu/Window.java b/src/org/infinity/resource/chu/Window.java index 1bb195f52..0e2cfc048 100644 --- a/src/org/infinity/resource/chu/Window.java +++ b/src/org/infinity/resource/chu/Window.java @@ -38,7 +38,6 @@ final class Window extends AbstractStruct // implements AddRemovable public static final String CHU_WINDOW_FIRST_CONTROL_INDEX = "First control index"; public static final String CHU_WINDOW_FLAGS = "Flags"; - private static final String hasb[] = {"No", "Yes"}; private static final String s_flag[] = {"No flags set", "Don't dim background"}; Window() throws Exception @@ -168,7 +167,7 @@ public int read(ByteBuffer buffer, int offset) throws Exception addField(new DecNumber(buffer, offset + 6, 2, CHU_WINDOW_POSITION_Y)); addField(new DecNumber(buffer, offset + 8, 2, CHU_WINDOW_WIDTH)); addField(new DecNumber(buffer, offset + 10, 2, CHU_WINDOW_HEIGHT)); - addField(new Bitmap(buffer, offset + 12, 2, CHU_WINDOW_HAS_BACKGROUND, hasb)); + addField(new Bitmap(buffer, offset + 12, 2, CHU_WINDOW_HAS_BACKGROUND, OPTION_NOYES)); addField(new UnsignDecNumber(buffer, offset + 14, 2, CHU_WINDOW_NUM_CONTROLS)); addField(new ResourceRef(buffer, offset + 16, CHU_WINDOW_BACKGROUND, "MOS")); addField(new UnsignDecNumber(buffer, offset + 24, 2, CHU_WINDOW_FIRST_CONTROL_INDEX)); diff --git a/src/org/infinity/resource/cre/CreResource.java b/src/org/infinity/resource/cre/CreResource.java index 75a224d28..c474444e5 100644 --- a/src/org/infinity/resource/cre/CreResource.java +++ b/src/org/infinity/resource/cre/CreResource.java @@ -465,7 +465,6 @@ public final class CreResource extends AbstractStruct public static final String[] s_attributes_iwd2 = {"No flags set", "Mental fortitude", "Critical hit immunity", "Cannot be paladin", "Cannot be monk"}; public static final String[] s_attacks = {"0", "1", "2", "3", "4", "5", "1/2", "3/2", "5/2", "7/2", "9/2"}; - public static final String[] s_noyes = {"No", "Yes"}; public static final String[] s_visible = {"Shown", "Hidden"}; public static final String[] s_profLabels = {"Active class", "Original class"}; public static final String[] s_effversion = {"Version 1", "Version 2"}; @@ -1175,15 +1174,15 @@ private int readIWD2(ByteBuffer buffer, int offset) throws Exception addField(new ResourceRef(buffer, offset + 644, CRE_SCRIPT_SPECIAL_3, "BCS")); addField(new ResourceRef(buffer, offset + 652, CRE_SCRIPT_MOVEMENT, "BCS")); addField(new Bitmap(buffer, offset + 660, 1, CRE_DEFAULT_VISIBILITY, s_visible)); - addField(new Bitmap(buffer, offset + 661, 1, CRE_SET_EXTRA_DEATH_VAR, s_noyes)); - addField(new Bitmap(buffer, offset + 662, 1, CRE_INCREMENT_KILL_COUNT, s_noyes)); + addField(new Bitmap(buffer, offset + 661, 1, CRE_SET_EXTRA_DEATH_VAR, OPTION_NOYES)); + addField(new Bitmap(buffer, offset + 662, 1, CRE_INCREMENT_KILL_COUNT, OPTION_NOYES)); addField(new Unknown(buffer, offset + 663, 1)); for (int i = 0; i < 5; i++) { addField(new DecNumber(buffer, offset + 664 + (i * 2), 2, String.format(CRE_INTERNAL_FMT, i+1))); } addField(new TextString(buffer, offset + 674, 32, CRE_DEATH_VAR_SET)); addField(new TextString(buffer, offset + 706, 32, CRE_DEATH_VAR_INC)); - addField(new Bitmap(buffer, offset + 738, 2, CRE_LOCATION_SAVED, s_noyes)); + addField(new Bitmap(buffer, offset + 738, 2, CRE_LOCATION_SAVED, OPTION_NOYES)); addField(new DecNumber(buffer, offset + 740, 2, CRE_SAVED_LOCATION_X)); addField(new DecNumber(buffer, offset + 742, 2, CRE_SAVED_LOCATION_Y)); addField(new DecNumber(buffer, offset + 744, 2, CRE_SAVED_ORIENTATION)); @@ -1522,7 +1521,7 @@ private int readOther(String version, ByteBuffer buffer, int offset) throws Exce } else { addField(new Unknown(buffer, offset + 110, 7)); } - addField(new Bitmap(buffer, offset + 117, 1, CRE_NIGHTMARE_MODE, s_noyes)); + addField(new Bitmap(buffer, offset + 117, 1, CRE_NIGHTMARE_MODE, OPTION_NOYES)); addField(new UnsignDecNumber(buffer, offset + 118, 1, CRE_TRANSLUCENCY)); if (Profile.getGame() == Profile.Game.PSTEE) { addField(new DecNumber(buffer, offset + 119, 1, CRE_MURDER_INC)); @@ -1689,15 +1688,15 @@ else if (version.equalsIgnoreCase("V9.0") || version.equalsIgnoreCase("V9.1")) { } else if (version.equalsIgnoreCase("V9.0") || version.equalsIgnoreCase("V9.1")) { addField(new Bitmap(buffer, offset + 616, 1, CRE_DEFAULT_VISIBILITY, s_visible)); - addField(new Bitmap(buffer, offset + 617, 1, CRE_SET_EXTRA_DEATH_VAR, s_noyes)); - addField(new Bitmap(buffer, offset + 618, 1, CRE_INCREMENT_KILL_COUNT, s_noyes)); + addField(new Bitmap(buffer, offset + 617, 1, CRE_SET_EXTRA_DEATH_VAR, OPTION_NOYES)); + addField(new Bitmap(buffer, offset + 618, 1, CRE_INCREMENT_KILL_COUNT, OPTION_NOYES)); addField(new Unknown(buffer, offset + 619, 1)); for (int i = 0; i < 5; i++) { addField(new DecNumber(buffer, offset + 620 + (i * 2), 2, String.format(CRE_INTERNAL_FMT, i+1))); } addField(new TextString(buffer, offset + 630, 32, CRE_DEATH_VAR_SET)); addField(new TextString(buffer, offset + 662, 32, CRE_DEATH_VAR_INC)); - addField(new Bitmap(buffer, offset + 694, 2, CRE_LOCATION_SAVED, s_noyes)); + addField(new Bitmap(buffer, offset + 694, 2, CRE_LOCATION_SAVED, OPTION_NOYES)); addField(new DecNumber(buffer, offset + 696, 2, CRE_SAVED_LOCATION_X)); addField(new DecNumber(buffer, offset + 698, 2, CRE_SAVED_LOCATION_Y)); addField(new DecNumber(buffer, offset + 700, 2, CRE_SAVED_ORIENTATION)); diff --git a/src/org/infinity/resource/cre/KnownSpells.java b/src/org/infinity/resource/cre/KnownSpells.java index e052232c3..cbd97d3e0 100644 --- a/src/org/infinity/resource/cre/KnownSpells.java +++ b/src/org/infinity/resource/cre/KnownSpells.java @@ -21,7 +21,7 @@ public final class KnownSpells extends AbstractStruct implements AddRemovable public static final String CRE_KNOWN_LEVEL = "Level"; public static final String CRE_KNOWN_TYPE = "Type"; - private static final String[] s_spelltype = {"Priest", "Wizard", "Innate"}; + public static final String[] s_spelltype = {"Priest", "Wizard", "Innate"}; KnownSpells() throws Exception { diff --git a/src/org/infinity/resource/cre/SpellMemorization.java b/src/org/infinity/resource/cre/SpellMemorization.java index e6410c800..faff0765e 100644 --- a/src/org/infinity/resource/cre/SpellMemorization.java +++ b/src/org/infinity/resource/cre/SpellMemorization.java @@ -26,8 +26,6 @@ public final class SpellMemorization extends AbstractStruct implements AddRemova public static final String CRE_MEMORIZATION_SPELL_TABLE_INDEX = "Spell table index"; public static final String CRE_MEMORIZATION_SPELL_COUNT = "Spell count"; - private static final String[] s_spelltype = {"Priest", "Wizard", "Innate"}; - SpellMemorization() throws Exception { super(null, CRE_MEMORIZATION, StreamUtils.getByteBuffer(16), 0); @@ -91,7 +89,7 @@ public int read(ByteBuffer buffer, int offset) addField(new DecNumber(buffer, offset, 2, CRE_MEMORIZATION_LEVEL)); addField(new DecNumber(buffer, offset + 2, 2, CRE_MEMORIZATION_NUM_MEMORIZABLE_TOTAL)); addField(new DecNumber(buffer, offset + 4, 2, CRE_MEMORIZATION_NUM_MEMORIZABLE_CURRENT)); - addField(new Bitmap(buffer, offset + 6, 2, CRE_MEMORIZATION_TYPE, s_spelltype)); + addField(new Bitmap(buffer, offset + 6, 2, CRE_MEMORIZATION_TYPE, KnownSpells.s_spelltype)); addField(new DecNumber(buffer, offset + 8, 4, CRE_MEMORIZATION_SPELL_TABLE_INDEX)); addField(new DecNumber(buffer, offset + 12, 4, CRE_MEMORIZATION_SPELL_COUNT)); return offset + 16; diff --git a/src/org/infinity/resource/gam/GamResource.java b/src/org/infinity/resource/gam/GamResource.java index e3477df85..d3839893e 100644 --- a/src/org/infinity/resource/gam/GamResource.java +++ b/src/org/infinity/resource/gam/GamResource.java @@ -104,7 +104,6 @@ public final class GamResource extends AbstractStruct implements Resource, HasAd public static final String GAM_UNKNOWN_STRUCTURE = "Unknown structure"; public static final String GAM_POCKET_PLANE = "Pocket plane"; - public static final String[] s_noyes = {"No", "Yes"}; public static final String[] s_formation = {"Button 1", "Button 2", "Button 3", "Button 4", "Button 5"}; public static final String[] s_weather = {"No weather", "Raining", "Snowing", "Light weather", "Medium weather", "Light wind", "Medium wind", "Rare lightning", @@ -417,7 +416,7 @@ else if (Profile.getEngine() == Profile.Engine.IWD2) { // V2.2 (V1.1 & V2.0 in B addField(numIWD2); offIWD2 = new SectionOffset(buffer, offset + 104, GAM_OFFSET_UNKNOWN, UnknownSection3.class); addField(offIWD2); - addField(new Bitmap(buffer, offset + 108, 4, GAM_NIGHTMARE_MODE, s_noyes)); + addField(new Bitmap(buffer, offset + 108, 4, GAM_NIGHTMARE_MODE, OPTION_NOYES)); addField(new Unknown(buffer, offset + 112, 68)); } diff --git a/src/org/infinity/resource/gam/ModronMaze.java b/src/org/infinity/resource/gam/ModronMaze.java index f4cf3ef94..10e89b608 100644 --- a/src/org/infinity/resource/gam/ModronMaze.java +++ b/src/org/infinity/resource/gam/ModronMaze.java @@ -29,8 +29,6 @@ public final class ModronMaze extends AbstractStruct public static final String GAM_MAZE_MAZE_BLOCKER_MADE = "Foyer maze blocker made"; public static final String GAM_MAZE_ENGINE_BLOCKER_MADE = "Foyer engine blocker made"; - private static final String[] s_noyes = {"No", "Yes"}; - public ModronMaze(AbstractStruct superStruct, ByteBuffer buffer, int offset) throws Exception { super(superStruct, GAM_MAZE, buffer, offset); @@ -71,11 +69,11 @@ public int read(ByteBuffer buffer, int offset) throws Exception curOfs += 4; addField(new DecNumber(buffer, curOfs, 4, GAM_MAZE_NUM_TRAPS)); curOfs += 4; - addField(new Bitmap(buffer, curOfs, 4, GAM_MAZE_INITIALIZED, s_noyes)); + addField(new Bitmap(buffer, curOfs, 4, GAM_MAZE_INITIALIZED, OPTION_NOYES)); curOfs += 4; - addField(new Bitmap(buffer, curOfs, 4, GAM_MAZE_MAZE_BLOCKER_MADE, s_noyes)); + addField(new Bitmap(buffer, curOfs, 4, GAM_MAZE_MAZE_BLOCKER_MADE, OPTION_NOYES)); curOfs += 4; - addField(new Bitmap(buffer, curOfs, 4, GAM_MAZE_ENGINE_BLOCKER_MADE, s_noyes)); + addField(new Bitmap(buffer, curOfs, 4, GAM_MAZE_ENGINE_BLOCKER_MADE, OPTION_NOYES)); curOfs += 4; return curOfs; diff --git a/src/org/infinity/resource/gam/ModronMazeEntry.java b/src/org/infinity/resource/gam/ModronMazeEntry.java index 6cdd61587..d291ab567 100644 --- a/src/org/infinity/resource/gam/ModronMazeEntry.java +++ b/src/org/infinity/resource/gam/ModronMazeEntry.java @@ -22,7 +22,6 @@ public final class ModronMazeEntry extends AbstractStruct public static final String GAM_MAZE_ENTRY_EXITS = "Exits"; public static final String GAM_MAZE_ENTRY_POLULATED = "Populated"; - private static final String[] s_noyes = {"No", "Yes"}; private static final String[] s_traps = {"TrapA", "TrapB", "TrapC"}; private static final String[] s_walls = {"None", "East", "West", "North", "South"}; @@ -34,13 +33,13 @@ public ModronMazeEntry(AbstractStruct superStruct, ByteBuffer buffer, int offset @Override public int read(ByteBuffer buffer, int offset) throws Exception { - addField(new Bitmap(buffer, offset, 4, GAM_MAZE_ENTRY_USED, s_noyes)); - addField(new Bitmap(buffer, offset + 4, 4, GAM_MAZE_ENTRY_ACCESSIBLE, s_noyes)); - addField(new Bitmap(buffer, offset + 8, 4, GAM_MAZE_ENTRY_IS_VALID, s_noyes)); - addField(new Bitmap(buffer, offset + 12, 4, GAM_MAZE_ENTRY_IS_TRAPPED, s_noyes)); + addField(new Bitmap(buffer, offset, 4, GAM_MAZE_ENTRY_USED, OPTION_NOYES)); + addField(new Bitmap(buffer, offset + 4, 4, GAM_MAZE_ENTRY_ACCESSIBLE, OPTION_NOYES)); + addField(new Bitmap(buffer, offset + 8, 4, GAM_MAZE_ENTRY_IS_VALID, OPTION_NOYES)); + addField(new Bitmap(buffer, offset + 12, 4, GAM_MAZE_ENTRY_IS_TRAPPED, OPTION_NOYES)); addField(new Bitmap(buffer, offset + 16, 4, GAM_MAZE_ENTRY_TRAP_TYPE, s_traps)); addField(new Flag(buffer, offset + 20, 2, GAM_MAZE_ENTRY_EXITS, s_walls)); - addField(new Bitmap(buffer, offset + 22, 4, GAM_MAZE_ENTRY_POLULATED, s_noyes)); + addField(new Bitmap(buffer, offset + 22, 4, GAM_MAZE_ENTRY_POLULATED, OPTION_NOYES)); return offset + 26; } } diff --git a/src/org/infinity/resource/gam/PartyNPC.java b/src/org/infinity/resource/gam/PartyNPC.java index 1e1f0f995..b4ccebdfb 100644 --- a/src/org/infinity/resource/gam/PartyNPC.java +++ b/src/org/infinity/resource/gam/PartyNPC.java @@ -25,7 +25,6 @@ import org.infinity.resource.HasViewerTabs; import org.infinity.resource.Profile; import org.infinity.resource.StructEntry; -import org.infinity.resource.are.Actor; import org.infinity.resource.cre.CreResource; import org.infinity.util.LongIntegerHashMap; import org.infinity.util.io.StreamUtils; @@ -87,7 +86,6 @@ public class PartyNPC extends AbstractStruct implements HasViewerTabs, HasAddRem private static final LongIntegerHashMap partyOrder = new LongIntegerHashMap(); private static final LongIntegerHashMap m_selected = new LongIntegerHashMap(); - private static final String s_noyes[] = {"No", "Yes"}; static { partyOrder.put(0L, "Slot 1"); @@ -226,7 +224,7 @@ public int read(ByteBuffer buffer, int offset) throws Exception } else { addField(new ResourceRef(buffer, offset + 12, GAM_NPC_CHARACTER, "CRE")); } - addField(new Bitmap(buffer, offset + 20, 4, GAM_NPC_ORIENTATION, Actor.s_orientation)); + addField(new Bitmap(buffer, offset + 20, 4, GAM_NPC_ORIENTATION, OPTION_ORIENTATION)); addField(new ResourceRef(buffer, offset + 24, GAM_NPC_CURRENT_AREA, "ARE")); addField(new DecNumber(buffer, offset + 32, 2, GAM_NPC_LOCATION_X)); addField(new DecNumber(buffer, offset + 34, 2, GAM_NPC_LOCATION_Y)); @@ -433,7 +431,7 @@ private int readCharStats(ByteBuffer buffer, int offset) addField(new DecNumber(buffer, offset + 4, 4, GAM_NPC_STAT_XP_FOE_VANQUISHED)); addField(new DecNumber(buffer, offset + 8, 4, GAM_NPC_STAT_TIME_IN_PARTY)); addField(new DecNumber(buffer, offset + 12, 4, GAM_NPC_STAT_JOIN_TIME)); - addField(new Bitmap(buffer, offset + 16, 1, GAM_NPC_STAT_IN_PARTY, s_noyes)); + addField(new Bitmap(buffer, offset + 16, 1, GAM_NPC_STAT_IN_PARTY, OPTION_NOYES)); addField(new Unknown(buffer, offset + 17, 2)); addField(new TextString(buffer, offset + 19, 1, GAM_NPC_STAT_INITIAL_CHAR)); addField(new DecNumber(buffer, offset + 20, 4, GAM_NPC_STAT_KILLS_XP_CHAPTER)); diff --git a/src/org/infinity/resource/itm/Ability.java b/src/org/infinity/resource/itm/Ability.java index 39addec09..168f34cc0 100644 --- a/src/org/infinity/resource/itm/Ability.java +++ b/src/org/infinity/resource/itm/Ability.java @@ -47,7 +47,6 @@ public final class Ability extends AbstractAbility implements AddRemovable, HasA public static final String ITM_ABIL_IS_BOLT = "Is bolt?"; public static final String ITM_ABIL_IS_BULLET = "Is bullet?"; - public static final String[] s_noyes = {"No", "Yes"}; public static final String[] s_launcher = {"None", "Bow", "Crossbow", "Sling"}; public static final String[] s_abilityuse = {"", "Weapon", "Spell", "Item", "Ability", "reserved"}; public static final String[] s_recharge = { @@ -182,9 +181,9 @@ public int read(ByteBuffer buffer, int offset) throws Exception addField(new DecNumber(buffer, offset + 44, 2, ITM_ABIL_ANIM_OVERHAND)); addField(new DecNumber(buffer, offset + 46, 2, ITM_ABIL_ANIM_BACKHAND)); addField(new DecNumber(buffer, offset + 48, 2, ITM_ABIL_ANIM_THRUST)); - addField(new Bitmap(buffer, offset + 50, 2, ITM_ABIL_IS_ARROW, s_noyes)); - addField(new Bitmap(buffer, offset + 52, 2, ITM_ABIL_IS_BOLT, s_noyes)); - addField(new Bitmap(buffer, offset + 54, 2, ITM_ABIL_IS_BULLET, s_noyes)); + addField(new Bitmap(buffer, offset + 50, 2, ITM_ABIL_IS_ARROW, OPTION_NOYES)); + addField(new Bitmap(buffer, offset + 52, 2, ITM_ABIL_IS_BOLT, OPTION_NOYES)); + addField(new Bitmap(buffer, offset + 54, 2, ITM_ABIL_IS_BULLET, OPTION_NOYES)); return offset + 56; } diff --git a/src/org/infinity/resource/maze/MazeEntry.java b/src/org/infinity/resource/maze/MazeEntry.java index 9e4de105e..95269dda3 100644 --- a/src/org/infinity/resource/maze/MazeEntry.java +++ b/src/org/infinity/resource/maze/MazeEntry.java @@ -13,7 +13,6 @@ public class MazeEntry extends AbstractStruct { - private static final String[] s_noyes = {"No", "Yes"}; private static final String[] s_traps = {"TrapA", "TrapB", "TrapC"}; private static final String[] s_walls = {"None", "East", "West", "North", "South"}; @@ -25,13 +24,13 @@ public MazeEntry(AbstractStruct superStruct, ByteBuffer buffer, int offset, int @Override public int read(ByteBuffer buffer, int offset) throws Exception { - addField(new Bitmap(buffer, offset, 4, ModronMazeEntry.GAM_MAZE_ENTRY_USED, s_noyes)); - addField(new Bitmap(buffer, offset + 4, 4, ModronMazeEntry.GAM_MAZE_ENTRY_ACCESSIBLE, s_noyes)); - addField(new Bitmap(buffer, offset + 8, 4, ModronMazeEntry.GAM_MAZE_ENTRY_IS_VALID, s_noyes)); - addField(new Bitmap(buffer, offset + 12, 4, ModronMazeEntry.GAM_MAZE_ENTRY_IS_TRAPPED, s_noyes)); + addField(new Bitmap(buffer, offset, 4, ModronMazeEntry.GAM_MAZE_ENTRY_USED, OPTION_NOYES)); + addField(new Bitmap(buffer, offset + 4, 4, ModronMazeEntry.GAM_MAZE_ENTRY_ACCESSIBLE, OPTION_NOYES)); + addField(new Bitmap(buffer, offset + 8, 4, ModronMazeEntry.GAM_MAZE_ENTRY_IS_VALID, OPTION_NOYES)); + addField(new Bitmap(buffer, offset + 12, 4, ModronMazeEntry.GAM_MAZE_ENTRY_IS_TRAPPED, OPTION_NOYES)); addField(new Bitmap(buffer, offset + 16, 4, ModronMazeEntry.GAM_MAZE_ENTRY_TRAP_TYPE, s_traps)); addField(new Flag(buffer, offset + 20, 4, ModronMazeEntry.GAM_MAZE_ENTRY_EXITS, s_walls)); - addField(new Bitmap(buffer, offset + 24, 4, ModronMazeEntry.GAM_MAZE_ENTRY_POLULATED, s_noyes)); + addField(new Bitmap(buffer, offset + 24, 4, ModronMazeEntry.GAM_MAZE_ENTRY_POLULATED, OPTION_NOYES)); return offset + 28; } } diff --git a/src/org/infinity/resource/maze/MazeResource.java b/src/org/infinity/resource/maze/MazeResource.java index b10e371d6..480cc8b14 100644 --- a/src/org/infinity/resource/maze/MazeResource.java +++ b/src/org/infinity/resource/maze/MazeResource.java @@ -35,8 +35,6 @@ */ public class MazeResource extends AbstractStruct implements Resource, HasViewerTabs { - private static final String[] s_noyes = {"No", "Yes"}; - private StructHexViewer hexViewer; public MazeResource(ResourceEntry entry) throws Exception @@ -83,11 +81,11 @@ public int read(ByteBuffer buffer, int offset) throws Exception curOfs += 4; addField(new DecNumber(buffer, curOfs, 4, ModronMaze.GAM_MAZE_NUM_TRAPS)); curOfs += 4; - addField(new Bitmap(buffer, curOfs, 4, ModronMaze.GAM_MAZE_INITIALIZED, s_noyes)); + addField(new Bitmap(buffer, curOfs, 4, ModronMaze.GAM_MAZE_INITIALIZED, OPTION_NOYES)); curOfs += 4; - addField(new Bitmap(buffer, curOfs, 4, ModronMaze.GAM_MAZE_MAZE_BLOCKER_MADE, s_noyes)); + addField(new Bitmap(buffer, curOfs, 4, ModronMaze.GAM_MAZE_MAZE_BLOCKER_MADE, OPTION_NOYES)); curOfs += 4; - addField(new Bitmap(buffer, curOfs, 4, ModronMaze.GAM_MAZE_ENGINE_BLOCKER_MADE, s_noyes)); + addField(new Bitmap(buffer, curOfs, 4, ModronMaze.GAM_MAZE_ENGINE_BLOCKER_MADE, OPTION_NOYES)); curOfs += 4; return curOfs; diff --git a/src/org/infinity/resource/other/VvcResource.java b/src/org/infinity/resource/other/VvcResource.java index 894d8e416..b0604f546 100644 --- a/src/org/infinity/resource/other/VvcResource.java +++ b/src/org/infinity/resource/other/VvcResource.java @@ -98,7 +98,6 @@ public final class VvcResource extends AbstractStruct implements Resource, HasVi "Purgeable", "Not covered by wall", "Mid-level brighten", "High-level brighten"}; public static final String[] s_face = {"Use current", "Face target", "Follow target", "Follow path", "Lock orientation"}; - public static final String[] s_noyes = {"No", "Yes"}; private StructHexViewer hexViewer; @@ -121,7 +120,7 @@ public int read(ByteBuffer buffer, int offset) throws Exception addField(new Unknown(buffer, offset + 36, 4)); addField(new DecNumber(buffer, offset + 40, 4, VVC_POSITION_X)); addField(new DecNumber(buffer, offset + 44, 4, VVC_POSITION_Y)); - addField(new Bitmap(buffer, offset + 48, 4, VVC_DRAW_ORIENTED, s_noyes)); + addField(new Bitmap(buffer, offset + 48, 4, VVC_DRAW_ORIENTED, OPTION_NOYES)); addField(new DecNumber(buffer, offset + 52, 4, VVC_FRAME_RATE)); addField(new DecNumber(buffer, offset + 56, 4, VVC_NUM_ORIENTATIONS)); addField(new DecNumber(buffer, offset + 60, 4, VVC_PRIMARY_ORIENTATION)); @@ -136,7 +135,7 @@ public int read(ByteBuffer buffer, int offset) throws Exception addField(new DecNumber(buffer, offset + 104, 4, VVC_FIRST_ANIMATION_INDEX)); addField(new DecNumber(buffer, offset + 108, 4, VVC_SECOND_ANIMATION_INDEX)); addField(new DecNumber(buffer, offset + 112, 4, VVC_CURRENT_ANIMATION_INDEX)); - addField(new Bitmap(buffer, offset + 116, 4, VVC_CONTINUOUS_PLAYBACK, s_noyes)); + addField(new Bitmap(buffer, offset + 116, 4, VVC_CONTINUOUS_PLAYBACK, OPTION_NOYES)); addField(new ResourceRef(buffer, offset + 120, VVC_SOUND_STARTING, "WAV")); addField(new ResourceRef(buffer, offset + 128, VVC_SOUND_DURATION, "WAV")); addField(new ResourceRef(buffer, offset + 136, VVC_ALPHA_MASK, "BAM")); diff --git a/src/org/infinity/resource/sto/ItemSale.java b/src/org/infinity/resource/sto/ItemSale.java index a7db3caab..c50426bfc 100644 --- a/src/org/infinity/resource/sto/ItemSale.java +++ b/src/org/infinity/resource/sto/ItemSale.java @@ -27,7 +27,6 @@ public final class ItemSale extends AbstractStruct implements AddRemovable public static final String[] s_itemflag = {"No flags set", "Identified", "Not stealable", "Stolen", "Undroppable"}; - public static final String[] s_noyes = { "No", "Yes" }; ItemSale() throws Exception { @@ -59,7 +58,7 @@ public int read(ByteBuffer buffer, int offset) throws Exception } addField(new Flag(buffer, offset + 16, 4, STO_SALE_FLAGS, s_itemflag)); addField(new DecNumber(buffer, offset + 20, 4, STO_SALE_NUM_IN_STOCK)); - addField(new Bitmap(buffer, offset + 24, 4, STO_SALE_INFINITE_SUPPLY, s_noyes)); + addField(new Bitmap(buffer, offset + 24, 4, STO_SALE_INFINITE_SUPPLY, OPTION_NOYES)); return offset + 28; } } diff --git a/src/org/infinity/resource/sto/ItemSale11.java b/src/org/infinity/resource/sto/ItemSale11.java index c29c5a4a6..f4e7445e3 100644 --- a/src/org/infinity/resource/sto/ItemSale11.java +++ b/src/org/infinity/resource/sto/ItemSale11.java @@ -29,7 +29,6 @@ public final class ItemSale11 extends AbstractStruct implements AddRemovable public static final String STO_SALE_TRIGGER = "Sale trigger"; public static final String[] s_itemflag = {"No flags set", "Identified", "Not stealable", "Stolen"}; - public static final String[] s_noyes = { "No", "Yes" }; ItemSale11() throws Exception { @@ -61,7 +60,7 @@ public int read(ByteBuffer buffer, int offset) throws Exception } addField(new Flag(buffer, offset + 16, 4, STO_SALE_FLAGS, s_itemflag)); addField(new DecNumber(buffer, offset + 20, 4, STO_SALE_NUM_IN_STOCK)); - addField(new Bitmap(buffer, offset + 24, 4, STO_SALE_INFINITE_SUPPLY, s_noyes)); + addField(new Bitmap(buffer, offset + 24, 4, STO_SALE_INFINITE_SUPPLY, OPTION_NOYES)); addField(new StringRef(buffer, offset + 28, STO_SALE_TRIGGER)); addField(new Unknown(buffer, offset + 32, 56)); return offset + 88; diff --git a/src/org/infinity/resource/vef/AbstractComponent.java b/src/org/infinity/resource/vef/AbstractComponent.java index 0ad579320..7bb599cbd 100644 --- a/src/org/infinity/resource/vef/AbstractComponent.java +++ b/src/org/infinity/resource/vef/AbstractComponent.java @@ -23,8 +23,6 @@ public class AbstractComponent extends AbstractStruct implements AddRemovable public static final String VEF_COMP_TICKS_LOOP = "Ticks until loop"; public static final String VEF_COMP_CONTINUOUS = "Continuous cycles?"; - public static final String[] s_noyes = {"No", "Yes"}; - protected AbstractComponent(String label) throws Exception { super(null, label, StreamUtils.getByteBuffer(224), 0); @@ -58,7 +56,7 @@ public int read(ByteBuffer buffer, int offset) throws Exception offset = type.readAttributes(buffer, offset + 16, list); addFields(getFields().size() - 1, list); - addField(new Bitmap(buffer, offset, 4, VEF_COMP_CONTINUOUS, s_noyes)); + addField(new Bitmap(buffer, offset, 4, VEF_COMP_CONTINUOUS, OPTION_NOYES)); addField(new Unknown(buffer, offset + 4, 196)); offset += 200; return offset; diff --git a/src/org/infinity/resource/wed/Door.java b/src/org/infinity/resource/wed/Door.java index 81521df89..a55971388 100644 --- a/src/org/infinity/resource/wed/Door.java +++ b/src/org/infinity/resource/wed/Door.java @@ -33,8 +33,6 @@ public final class Door extends AbstractStruct implements AddRemovable, HasAddRe public static final String WED_DOOR_OFFSET_POLYGONS_CLOSED = "Polygons closed offset"; public static final String WED_DOOR_TILEMAP_INDEX = "Tilemap index"; - public static final String[] s_noyes = {"No", "Yes"}; - public Door() throws Exception { super(null, WED_DOOR, StreamUtils.getByteBuffer(26), 0); @@ -130,7 +128,7 @@ public void updatePolygonsOffset(int offset) public int read(ByteBuffer buffer, int offset) throws Exception { addField(new TextString(buffer, offset, 8, WED_DOOR_NAME)); - addField(new Bitmap(buffer, offset + 8, 2, WED_DOOR_IS_DOOR, s_noyes)); + addField(new Bitmap(buffer, offset + 8, 2, WED_DOOR_IS_DOOR, OPTION_NOYES)); DecNumber indexTileCell = new DecNumber(buffer, offset + 10, 2, WED_DOOR_TILEMAP_LOOKUP_INDEX); addField(indexTileCell); SectionCount countTileCell = new SectionCount(buffer, offset + 12, 2, WED_DOOR_NUM_TILEMAP_INDICES, From 68754a91e91ead82f9c1efa2075ac83470bad823 Mon Sep 17 00:00:00 2001 From: Argent77 <4519923+Argent77@users.noreply.github.com> Date: Sat, 25 Apr 2020 11:20:04 +0200 Subject: [PATCH 23/25] Update ARE Animation flags description --- src/org/infinity/resource/are/Animation.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/org/infinity/resource/are/Animation.java b/src/org/infinity/resource/are/Animation.java index e4ae8eb67..060e0477c 100644 --- a/src/org/infinity/resource/are/Animation.java +++ b/src/org/infinity/resource/are/Animation.java @@ -11,6 +11,7 @@ import org.infinity.datatype.ResourceRef; import org.infinity.datatype.TextString; import org.infinity.datatype.Unknown; +import org.infinity.datatype.UnsignDecNumber; import org.infinity.resource.AbstractStruct; import org.infinity.resource.AddRemovable; import org.infinity.resource.Profile; @@ -38,7 +39,7 @@ public final class Animation extends AbstractStruct implements AddRemovable public static final String[] s_flag = {"Not shown", "Is shown", "No shadow", "Not light source", "Partial animation", - "Synchronized draw", "Random start","Not covered by wall", "Static animation", + "Synchronized draw", "Random start","Not covered by wall", "Disable on slow machines", "Draw as background", "Play all frames", "Recolored by palette", "Mirror Y axis", "Don't remove in combat", "EE: Use WBM", "EE: Draw stenciled", "EE: Use PVRZ"}; @@ -81,7 +82,7 @@ public int read(ByteBuffer buffer, int offset) throws Exception addField(new DecNumber(buffer, offset + 58, 2, ARE_ANIMATION_TRANSLUCENCY)); addField(new DecNumber(buffer, offset + 60, 2, ARE_ANIMATION_START_RANGE)); addField(new DecNumber(buffer, offset + 62, 1, ARE_ANIMATION_LOOP_PROBABILITY)); - addField(new DecNumber(buffer, offset + 63, 1, ARE_ANIMATION_START_DELAY)); + addField(new UnsignDecNumber(buffer, offset + 63, 1, ARE_ANIMATION_START_DELAY)); if (Profile.getEngine() == Profile.Engine.BG2 || Profile.getEngine() == Profile.Engine.IWD2 || Profile.isEnhancedEdition()) { From b3e67f07e711c07065f086a1b703eeb8c92d5f80 Mon Sep 17 00:00:00 2001 From: Argent77 <4519923+Argent77@users.noreply.github.com> Date: Sat, 25 Apr 2020 11:22:08 +0200 Subject: [PATCH 24/25] Update fields in GAM and GAM > PartyNPC structure --- .../infinity/resource/gam/GamResource.java | 5 +-- src/org/infinity/resource/gam/PartyNPC.java | 31 ++++++++++--------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/org/infinity/resource/gam/GamResource.java b/src/org/infinity/resource/gam/GamResource.java index d3839893e..89e5d7c2a 100644 --- a/src/org/infinity/resource/gam/GamResource.java +++ b/src/org/infinity/resource/gam/GamResource.java @@ -20,6 +20,7 @@ import org.infinity.datatype.Bitmap; import org.infinity.datatype.DecNumber; import org.infinity.datatype.Flag; +import org.infinity.datatype.HashBitmap; import org.infinity.datatype.HexNumber; import org.infinity.datatype.IsNumeric; import org.infinity.datatype.ResourceRef; @@ -61,7 +62,7 @@ public final class GamResource extends AbstractStruct implements Resource, HasAd public static final String GAM_SELECTED_FORMATION = "Selected formation"; public static final String GAM_FORMATION_BUTTON_FMT = "Formation button %d"; public static final String GAM_PARTY_GOLD = "Party gold"; - public static final String GAM_NUM_NPCS_IN_PARTY = "# NPCs in party"; + public static final String GAM_VIEW_PLAYER_AREA = "View area of party member at"; public static final String GAM_WEATHER = "Weather"; public static final String GAM_OFFSET_PARTY_MEMBERS = "Party members offset"; public static final String GAM_NUM_PARTY_MEMBERS = "# party members"; @@ -311,7 +312,7 @@ public int read(ByteBuffer buffer, int offset) throws Exception addField(new DecNumber(buffer, offset + 14 + (i * 2), 2, String.format(GAM_FORMATION_BUTTON_FMT, i+1))); } addField(new DecNumber(buffer, offset + 24, 4, GAM_PARTY_GOLD)); - addField(new DecNumber(buffer, offset + 28, 2, GAM_NUM_NPCS_IN_PARTY)); + addField(new HashBitmap(buffer, offset + 28, 2, GAM_VIEW_PLAYER_AREA, PartyNPC.m_partyOrder)); addField(new Flag(buffer, offset + 30, 2, GAM_WEATHER, s_weather)); SectionOffset offset_partynpc = new SectionOffset(buffer, offset + 32, GAM_OFFSET_PARTY_MEMBERS, PartyNPC.class); diff --git a/src/org/infinity/resource/gam/PartyNPC.java b/src/org/infinity/resource/gam/PartyNPC.java index b4ccebdfb..1077fdc7e 100644 --- a/src/org/infinity/resource/gam/PartyNPC.java +++ b/src/org/infinity/resource/gam/PartyNPC.java @@ -10,6 +10,7 @@ import org.infinity.datatype.Bitmap; import org.infinity.datatype.DecNumber; +import org.infinity.datatype.Flag; import org.infinity.datatype.HashBitmap; import org.infinity.datatype.HexNumber; import org.infinity.datatype.IdsBitmap; @@ -84,22 +85,24 @@ public class PartyNPC extends AbstractStruct implements HasViewerTabs, HasAddRem public static final String GAM_NPC_STAT_FAV_WEAPON_FMT = "Favorite weapon %d"; public static final String GAM_NPC_STAT_FAV_WEAPON_COUNT_FMT = "Favorite weapon counter %d"; - private static final LongIntegerHashMap partyOrder = new LongIntegerHashMap(); - private static final LongIntegerHashMap m_selected = new LongIntegerHashMap(); + public static final LongIntegerHashMap m_partyOrder = new LongIntegerHashMap(); +// private static final LongIntegerHashMap m_selected = new LongIntegerHashMap(); + + private static final String[] s_selected = {"Not selected", "Selected", null, null, null, null, null, null, null, null, null, null, null, null, null, null, "Dead" }; static { - partyOrder.put(0L, "Slot 1"); - partyOrder.put(1L, "Slot 2"); - partyOrder.put(2L, "Slot 3"); - partyOrder.put(3L, "Slot 4"); - partyOrder.put(4L, "Slot 5"); - partyOrder.put(5L, "Slot 6"); + m_partyOrder.put(0L, "Slot 1"); + m_partyOrder.put(1L, "Slot 2"); + m_partyOrder.put(2L, "Slot 3"); + m_partyOrder.put(3L, "Slot 4"); + m_partyOrder.put(4L, "Slot 5"); + m_partyOrder.put(5L, "Slot 6"); // partyOrder.put(0x8000L, "In party, dead"); - partyOrder.put(0xffffL, "Not in party"); + m_partyOrder.put(0xffffL, "Not in party"); - m_selected.put(0L, "Not selected"); - m_selected.put(1L, "Selected"); - m_selected.put(0x8000L, "Dead"); +// m_selected.put(0L, "Not selected"); +// m_selected.put(1L, "Selected"); +// m_selected.put(0x8000L, "Dead"); } PartyNPC() throws Exception @@ -214,8 +217,8 @@ void updateCREOffset() @Override public int read(ByteBuffer buffer, int offset) throws Exception { - addField(new HashBitmap(buffer, offset, 2, GAM_NPC_SELECTION_STATE, m_selected)); - addField(new HashBitmap(buffer, offset + 2, 2, GAM_NPC_PARTY_POSITION, partyOrder)); + addField(new Flag(buffer, offset, 2, GAM_NPC_SELECTION_STATE, s_selected)); + addField(new HashBitmap(buffer, offset + 2, 2, GAM_NPC_PARTY_POSITION, m_partyOrder)); HexNumber creOffset = new HexNumber(buffer, offset + 4, 4, GAM_NPC_OFFSET_CRE); addField(creOffset); addField(new DecNumber(buffer, offset + 8, 4, GAM_NPC_CRE_SIZE)); From 4b93ab63f6e5047eaadd6f48f465e3a17c13e93d Mon Sep 17 00:00:00 2001 From: Argent77 <4519923+Argent77@users.noreply.github.com> Date: Sat, 25 Apr 2020 13:25:02 +0200 Subject: [PATCH 25/25] Version 2.1-20200425 --- src/org/infinity/gui/BrowserMenuBar.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/infinity/gui/BrowserMenuBar.java b/src/org/infinity/gui/BrowserMenuBar.java index dee599ea2..44abd01d9 100644 --- a/src/org/infinity/gui/BrowserMenuBar.java +++ b/src/org/infinity/gui/BrowserMenuBar.java @@ -106,7 +106,7 @@ public final class BrowserMenuBar extends JMenuBar implements KeyEventDispatcher { - public static final String VERSION = "v2.1-20200419"; + public static final String VERSION = "v2.1-20200425"; public static final LookAndFeelInfo DEFAULT_LOOKFEEL = new LookAndFeelInfo("Metal", "javax.swing.plaf.metal.MetalLookAndFeel");