// Near Infinity - An Infinity Engine Browser and Editor // Copyright (C) 2001 - 2005 Jon Olav Hauglid // See LICENSE.txt for license information package org.infinity.resource.wed; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.Arrays; import java.util.Comparator; import java.util.List; import javax.swing.JComponent; import org.infinity.datatype.DecNumber; import org.infinity.datatype.HexNumber; import org.infinity.datatype.RemovableDecNumber; import org.infinity.datatype.SectionCount; import org.infinity.datatype.SectionOffset; import org.infinity.datatype.TextString; import org.infinity.gui.StructViewer; import org.infinity.gui.hexview.BasicColorMap; import org.infinity.gui.hexview.StructHexViewer; import org.infinity.resource.AbstractStruct; import org.infinity.resource.AddRemovable; import org.infinity.resource.HasAddRemovable; import org.infinity.resource.HasViewerTabs; import org.infinity.resource.Resource; import org.infinity.resource.StructEntry; import org.infinity.resource.key.ResourceEntry; import org.infinity.resource.vertex.Vertex; import org.infinity.util.ArrayUtil; import org.infinity.util.Misc; public final class WedResource extends AbstractStruct implements Resource, HasAddRemovable, HasViewerTabs { // WED-specific field labels public static final String WED_NUM_OVERLAYS = "# overlays"; public static final String WED_NUM_DOORS = "# doors"; public static final String WED_OFFSET_OVERLAYS = "Overlays offset"; public static final String WED_OFFSET_SECOND_HEADER = "Second header offset"; public static final String WED_OFFSET_DOORS = "Doors offset"; public static final String WED_OFFSET_DOOR_TILEMAP_LOOKUP = "Door tilemap lookup offset"; public static final String WED_NUM_WALL_POLYGONS = "# wall polygons"; public static final String WED_OFFSET_WALL_POLYGONS = "Wall polygons offset"; public static final String WED_OFFSET_VERTICES = "Vertices offset"; public static final String WED_OFFSET_WALL_GROUPS = "Wall groups offset"; public static final String WED_OFFSET_WALL_POLYGON_LOOKUP = "Wall polygon lookup offset"; public static final String WED_WALL_POLYGON_INDEX = "Wall polygon index"; private StructHexViewer hexViewer; public WedResource(ResourceEntry entry) throws Exception { super(entry); } // --------------------- Begin Interface HasAddRemovable --------------------- @Override public AddRemovable[] getAddRemovables() throws Exception { return new AddRemovable[]{new Door(), new WallPolygon(), new Wallgroup()}; } @Override public AddRemovable confirmAddEntry(AddRemovable entry) throws Exception { return entry; } @Override public boolean confirmRemoveEntry(AddRemovable entry) throws Exception { return true; } // --------------------- End Interface HasAddRemovable --------------------- // --------------------- Begin Interface Writeable --------------------- @Override public void write(OutputStream os) throws IOException { super.writeFlatList(os); } // --------------------- End Interface Writeable --------------------- //--------------------- Begin Interface HasViewerTabs --------------------- @Override public int getViewerTabCount() { return 1; } @Override public String getViewerTabName(int index) { return StructViewer.TAB_RAW; } @Override public JComponent getViewerTab(int index) { if (hexViewer == null) { hexViewer = new StructHexViewer(this, new BasicColorMap(this, true)); } return hexViewer; } @Override public boolean viewerTabAddedBefore(int index) { return false; } //--------------------- End Interface HasViewerTabs --------------------- @Override protected void viewerInitialized(StructViewer viewer) { viewer.addTabChangeListener(hexViewer); } @Override protected void datatypeAdded(AddRemovable datatype) { updateSectionOffsets(datatype, datatype.getSize()); if (hexViewer != null) { hexViewer.dataModified(); } } @Override protected void datatypeAddedInChild(AbstractStruct child, AddRemovable datatype) { updateSectionOffsets(datatype, datatype.getSize()); if (datatype instanceof Vertex) updateVertices(); else if (datatype instanceof RemovableDecNumber && child instanceof Door) { Door childDoor = (Door)child; int childIndex = childDoor.getTilemapIndex().getValue(); for (int i = 0; i < getFieldCount(); i++) { Object o = getField(i); if (o instanceof Door && o != childDoor) { DecNumber tilemapIndex = ((Door)o).getTilemapIndex(); if (tilemapIndex.getValue() >= childIndex) tilemapIndex.incValue(1); } } } if (hexViewer != null) { hexViewer.dataModified(); } } @Override protected void datatypeRemoved(AddRemovable datatype) { updateSectionOffsets(datatype, -datatype.getSize()); if (hexViewer != null) { hexViewer.dataModified(); } } @Override protected void datatypeRemovedInChild(AbstractStruct child, AddRemovable datatype) { updateSectionOffsets(datatype, -datatype.getSize()); if (datatype instanceof Vertex) updateVertices(); else if (datatype instanceof RemovableDecNumber && child instanceof Door) { Door childDoor = (Door)child; int childIndex = childDoor.getTilemapIndex().getValue(); for (int i = 0; i < getFieldCount(); i++) { Object o = getField(i); if (o instanceof Door && o != childDoor) { DecNumber tilemapIndex = ((Door)o).getTilemapIndex(); if (tilemapIndex.getValue() > childIndex) tilemapIndex.incValue(-1); } } } if (hexViewer != null) { hexViewer.dataModified(); } } @Override public int read(ByteBuffer buffer, int offset) throws Exception { int startOffset = offset; addField(new TextString(buffer, offset, 4, COMMON_SIGNATURE)); addField(new TextString(buffer, offset + 4, 4, COMMON_VERSION)); SectionCount countOverlays = new SectionCount(buffer, offset + 8, 4, WED_NUM_OVERLAYS, Overlay.class); addField(countOverlays); SectionCount countDoors = new SectionCount(buffer, offset + 12, 4, WED_NUM_DOORS, Door.class); addField(countDoors); SectionOffset offsetOverlays = new SectionOffset(buffer, offset + 16, WED_OFFSET_OVERLAYS, Overlay.class); addField(offsetOverlays); SectionOffset offsetHeader2 = new SectionOffset(buffer, offset + 20, WED_OFFSET_SECOND_HEADER, null); addField(offsetHeader2); SectionOffset offsetDoors = new SectionOffset(buffer, offset + 24, WED_OFFSET_DOORS, Door.class); addField(offsetDoors); HexNumber offsetDoortile = new HexNumber(buffer, offset + 28, 4, WED_OFFSET_DOOR_TILEMAP_LOOKUP); addField(offsetDoortile); offset = offsetOverlays.getValue(); for (int i = 0; i < countOverlays.getValue(); i++) { Overlay overlay = new Overlay(this, buffer, offset, i); offset = overlay.getEndOffset(); addField(overlay); } offset = offsetHeader2.getValue(); SectionCount countWallpolygons = new SectionCount(buffer, offset, 4, WED_NUM_WALL_POLYGONS, WallPolygon.class); addField(countWallpolygons); SectionOffset offsetPolygons = new SectionOffset(buffer, offset + 4, WED_OFFSET_WALL_POLYGONS, WallPolygon.class); addField(offsetPolygons); HexNumber offsetVertices = new HexNumber(buffer, offset + 8, 4, WED_OFFSET_VERTICES); addField(offsetVertices); SectionOffset offsetWallgroups = new SectionOffset(buffer, offset + 12, WED_OFFSET_WALL_GROUPS, Wallgroup.class); addField(offsetWallgroups); SectionOffset offsetPolytable = new SectionOffset(buffer, offset + 16, WED_OFFSET_WALL_POLYGON_LOOKUP, RemovableDecNumber.class); addField(offsetPolytable); HexNumber offsets[] = new HexNumber[]{offsetOverlays, offsetHeader2, offsetDoors, offsetDoortile, offsetPolygons, offsetWallgroups, offsetPolytable, new HexNumber(ByteBuffer.wrap(Misc .intToArray(buffer.limit() - startOffset)) .order(ByteOrder.LITTLE_ENDIAN), 0, 4, "")}; Arrays.sort(offsets, new Comparator<HexNumber>() { @Override public int compare(HexNumber s1, HexNumber s2) { return s1.getValue() - s2.getValue(); } }); offset = offsetDoors.getValue(); for (int i = 0; i < countDoors.getValue(); i++) { Door door = new Door(this, buffer, offset, i); offset = door.getEndOffset(); door.readVertices(buffer, offsetVertices.getValue()); addField(door); } offset = offsetWallgroups.getValue(); int countPolytable = 0; int countWallgroups = (offsets[ArrayUtil.indexOf(offsets, offsetWallgroups) + 1].getValue() - offsetWallgroups.getValue()) / 4; for (int i = 0; i < countWallgroups; i++) { Wallgroup wall = new Wallgroup(this, buffer, offset, i); offset = wall.getEndOffset(); countPolytable = Math.max(countPolytable, wall.getNextPolygonIndex()); addField(wall); } offset = offsetPolygons.getValue(); for (int i = 0; i < countWallpolygons.getValue(); i++) { Polygon poly = new WallPolygon(this, buffer, offset, i); offset = poly.getEndOffset(); poly.readVertices(buffer, offsetVertices.getValue()); addField(poly); } offset = offsetPolytable.getValue(); for (int i = 0; i < countPolytable; i++) { addField(new DecNumber(buffer, offset + i * 2, 2, WED_WALL_POLYGON_INDEX + " " + i)); } int endoffset = offset; List<StructEntry> flatList = getFlatList(); for (int i = 0; i < flatList.size(); i++) { StructEntry entry = flatList.get(i); if (entry.getOffset() + entry.getSize() > endoffset) { endoffset = entry.getOffset() + entry.getSize(); } } return endoffset; } private void updateSectionOffsets(AddRemovable datatype, int size) { if (!(datatype instanceof Vertex)) { HexNumber offset_vertices = (HexNumber)getAttribute(WED_OFFSET_VERTICES); if (datatype.getOffset() <= offset_vertices.getValue()) { offset_vertices.incValue(size); } } if (!(datatype instanceof RemovableDecNumber)) { HexNumber offset_doortilemap = (HexNumber)getAttribute(WED_OFFSET_DOOR_TILEMAP_LOOKUP); if (datatype.getOffset() <= offset_doortilemap.getValue()) { offset_doortilemap.incValue(size); } } for (int i = 0; i < getFieldCount(); i++) { Object o = getField(i); if (o instanceof Overlay) { ((Overlay)o).updateOffsets(datatype.getOffset(), size); } } // Assumes polygon offset is correct int offset = ((SectionOffset)getAttribute(WED_OFFSET_WALL_POLYGONS)).getValue(); offset += ((SectionCount)getAttribute(WED_NUM_WALL_POLYGONS)).getValue() * 18; for (int i = 0; i < getFieldCount(); i++) { Object o = getField(i); if (o instanceof Door) { ((Door)o).updatePolygonsOffset(offset); } } } private void updateVertices() { // Assumes vertices offset is correct int offset = ((HexNumber)getAttribute(WED_OFFSET_VERTICES)).getValue(); int count = 0; for (int i = 0; i < getFieldCount(); i++) { Object o = getField(i); if (o instanceof Polygon) { Polygon polygon = (Polygon)o; int vertNum = polygon.updateVertices(offset, count); offset += 4 * vertNum; count += vertNum; } else if (o instanceof Door) { Door door = (Door)o; for (int j = 0; j < door.getFieldCount(); j++) { StructEntry q = door.getField(j); if (q instanceof Polygon) { Polygon polygon = (Polygon)q; int vertNum = polygon.updateVertices(offset, count); offset += 4 * vertNum; count += vertNum; } } } } } }