// 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.are;
import java.awt.Component;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Set;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JScrollPane;
import org.infinity.datatype.DecNumber;
import org.infinity.datatype.Flag;
import org.infinity.datatype.HexNumber;
import org.infinity.datatype.ResourceRef;
import org.infinity.datatype.SectionCount;
import org.infinity.datatype.SectionOffset;
import org.infinity.datatype.TextString;
import org.infinity.datatype.Unknown;
import org.infinity.datatype.UnsignDecNumber;
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.Profile;
import org.infinity.resource.Resource;
import org.infinity.resource.ResourceFactory;
import org.infinity.resource.StructEntry;
import org.infinity.resource.are.viewer.AreaViewer;
import org.infinity.resource.key.ResourceEntry;
import org.infinity.resource.vertex.Vertex;
import org.infinity.search.SearchOptions;
import org.infinity.util.IdsMap;
import org.infinity.util.IdsMapCache;
import org.infinity.util.IdsMapEntry;
import org.infinity.util.io.StreamUtils;
public final class AreResource extends AbstractStruct implements Resource, HasAddRemovable, HasViewerTabs
{
// ARE-specific field labels
public static final String ARE_WED_RESOURCE = "WED resource";
public static final String ARE_LAST_SAVED = "Last saved";
public static final String ARE_AREA_TYPE = "Area type";
public static final String ARE_AREA_NORTH = "Area north";
public static final String ARE_AREA_EAST = "Area east";
public static final String ARE_AREA_SOUTH = "Area south";
public static final String ARE_AREA_WEST = "Area west";
public static final String ARE_EDGE_FLAGS_NORTH = "Edge flags north";
public static final String ARE_EDGE_FLAGS_EAST = "Edge flags east";
public static final String ARE_EDGE_FLAGS_SOUTH = "Edge flags south";
public static final String ARE_EDGE_FLAGS_WEST = "Edge flags west";
public static final String ARE_LOCATION = "Location";
public static final String ARE_PROBABILITY_RAIN = "Rain probability";
public static final String ARE_PROBABILITY_SNOW = "Snow probability";
public static final String ARE_PROBABILITY_FOG = "Fog probability";
public static final String ARE_PROBABILITY_LIGHTNING = "Lightning probability";
public static final String ARE_WIND_SPEED = "Wind speed";
public static final String ARE_OVERLAY_TRANSPARENCY = "Overlay transparency";
public static final String ARE_AREA_DIFFICULTY_2 = "Area difficulty 2";
public static final String ARE_AREA_DIFFICULTY_3 = "Area difficulty 3";
public static final String ARE_OFFSET_ACTORS = "Actors offset";
public static final String ARE_OFFSET_TRIGGERS = "Triggers offset";
public static final String ARE_OFFSET_SPAWN_POINTS = "Spawn points offset";
public static final String ARE_OFFSET_ENTRANCES = "Entrances offset";
public static final String ARE_OFFSET_CONTAINERS = "Containers offset";
public static final String ARE_OFFSET_ITEMS = "Items offset";
public static final String ARE_OFFSET_VERTICES = "Vertices offset";
public static final String ARE_OFFSET_AMBIENTS = "Ambients offset";
public static final String ARE_OFFSET_VARIABLES = "Variables offset";
public static final String ARE_OFFSET_OBJECT_FLAGS = "Object flags offset";
public static final String ARE_OFFSET_EXPLORED_BITMAP = "Explored bitmap offset";
public static final String ARE_OFFSET_DOORS = "Doors offset";
public static final String ARE_OFFSET_ANIMATIONS = "Animations offset";
public static final String ARE_OFFSET_TILED_OBJECTS = "Tiled objects offset";
public static final String ARE_OFFSET_SONGS = "Songs offset";
public static final String ARE_OFFSET_REST_ENCOUNTERS = "Rest encounters offset";
public static final String ARE_OFFSET_AUTOMAP_NOTES = "Automap notes offset";
public static final String ARE_OFFSET_PROJECTILE_TRAPS = "Projectile traps offset";
public static final String ARE_NUM_ACTORS = "# actors";
public static final String ARE_NUM_TRIGGERS = "# triggers";
public static final String ARE_NUM_SPAWN_POINTS = "# spawn points";
public static final String ARE_NUM_ENTRANCES = "# entrances";
public static final String ARE_NUM_CONTAINERS = "# containers";
public static final String ARE_NUM_ITEMS = "# items";
public static final String ARE_NUM_VERTICES = "# vertices";
public static final String ARE_NUM_AMBIENTS = "# ambients";
public static final String ARE_NUM_VARIABLES = "# variables";
public static final String ARE_NUM_OBJECT_FLAGS = "# object flags";
public static final String ARE_NUM_DOORS = "# doors";
public static final String ARE_NUM_ANIMATIONS = "# animations";
public static final String ARE_NUM_TILED_OBJECTS = "# tiled objects";
public static final String ARE_NUM_AUTOMAP_NOTES = "# automap notes";
public static final String ARE_NUM_PROJECTILE_TRAPS = "# projectile traps";
public static final String ARE_SIZE_EXPLORED_BITMAP = "Explored bitmap size";
public static final String ARE_AREA_SCRIPT = "Area script";
public static final String ARE_REST_MOVIE_DAY = "Rest movie (day)";
public static final String ARE_REST_MOVIE_NIGHT = "Rest movie (night)";
public static final String ARE_EXPLORED_BITMAP = "Explored bitmap";
public static final String[] s_flag = {"No flags set", "Outdoor", "Day/Night",
"Weather", "City", "Forest", "Dungeon",
"Extended night", "Can rest"};
public static final String[] s_flag_torment = {"Indoors", "Hive", "Hive Night", "Clerk's ward",
"Lower ward", "Ravel's maze", "Baator", "Rubikon",
"Negative material plane", "Curst", "Carceri",
"Allow day/night"};
public static final String[] s_atype = {"Normal", "Can't save game", "Tutorial area", "Dead magic zone",
"Dream area"};
public static final String[] s_atype_ee = {"Normal", "Can't save game", "Tutorial area", "Dead magic zone",
"Dream area", "Player1 can die;Allows death of party leader without ending the game"};
public static final String[] s_atype_torment = {"Can rest", "Cannot save",
"Cannot rest", "Cannot save", "Too dangerous to rest",
"Cannot save", "Can rest with permission"};
public static final String[] s_atype_iwd2 = {"Normal", "Can't save game", "Cannot rest", "Lock battle music"};
public static final String[] s_edge = {"No flags set", "Party required", "Party enabled"};
private StructHexViewer hexViewer;
private AreaViewer areaViewer;
public static void addScriptNames(Set<String> scriptNames, ByteBuffer buffer)
{
int offset = 0;
if (StreamUtils.readString(buffer, 4, 4).equalsIgnoreCase("V9.1"))
offset = 16;
// Actors
addScriptNames(scriptNames, buffer, buffer.getInt(offset + 84),
buffer.getShort(offset + 88), 272, true);
// ITEPoints
addScriptNames(scriptNames, buffer, buffer.getInt(offset + 92),
buffer.getShort(offset + 90), 196);
// Spawnpoints
addScriptNames(scriptNames, buffer, buffer.getInt(offset + 96),
buffer.getInt(offset + 100), 200);
// Entrances
// addScriptNames(scriptNames, buffer, buffer.getInt(offset + 104),
// buffer.getInt(offset + 108), 104);
// Containers
addScriptNames(scriptNames, buffer, buffer.getInt(offset + 112),
buffer.getShort(offset + 116), 192);
// Ambients
addScriptNames(scriptNames, buffer, buffer.getInt(offset + 132),
buffer.getShort(offset + 130), 212);
// Variables
// addScriptNames(scriptNames, buffer, buffer.getInt(offset + 136),
// buffer.getInt(offset + 140), 84);
// Doors
addScriptNames(scriptNames, buffer, buffer.getInt(offset + 168),
buffer.getInt(offset + 164), 200);
// Animations
addScriptNames(scriptNames, buffer, buffer.getInt(offset + 176),
buffer.getInt(offset + 172), 76);
// Tiled objects
addScriptNames(scriptNames, buffer, buffer.getInt(offset + 184),
buffer.getInt(offset + 180), 108);
// Rest spawn
// addScriptNames(scriptNames, buffer, DynamicArray.getInt(buffer, offset + 192), 1, 228);
}
private static void addScriptNames(Set<String> scriptNames, ByteBuffer buffer, int offset,
int count, int size)
{
addScriptNames(scriptNames, buffer, offset, count, size, false);
}
private static void addScriptNames(Set<String> scriptNames, ByteBuffer buffer, int offset,
int count, int size, boolean checkOverride)
{
for (int i = 0; i < count; i++) {
int curOfs = offset + i*size;
// Bit 3 of "Flags" field determines whether to override the actor's script name
if (!checkOverride || ((buffer.get(curOfs + 40) & 8) == 8)) {
StringBuilder sb = new StringBuilder(32);
for (int j = 0; j < 32; j++) {
byte b = buffer.get(curOfs + j);
if (b == 0x00) {
break;
} else if (b != 0x20) { // Space
sb.append(Character.toLowerCase((char)b));
}
}
synchronized (scriptNames) {
scriptNames.add(sb.toString());
}
}
}
}
public AreResource(ResourceEntry entry) throws Exception
{
super(entry);
}
//--------------------- Begin Interface Closeable ---------------------
@Override
public void close() throws Exception
{
super.close();
if (areaViewer != null) {
areaViewer.close();
areaViewer = null;
}
}
//--------------------- End Interface Closeable ---------------------
// --------------------- Begin Interface HasAddRemovable ---------------------
@Override
public AddRemovable[] getAddRemovables() throws Exception
{
if (Profile.getEngine() == Profile.Engine.PST) {
return new AddRemovable[]{new Actor(), new ITEPoint(), new SpawnPoint(),
new Entrance(), new Container(), new Ambient(),
new Variable(), new Door(), new Animation(),
new TiledObject(), new AutomapNotePST()};
} else if (Profile.getEngine() == Profile.Engine.BG2 || Profile.isEnhancedEdition()) {
return new AddRemovable[]{new Actor(), new ITEPoint(), new SpawnPoint(),
new Entrance(), new Container(), new Ambient(),
new Variable(), new Door(), new Animation(),
new TiledObject(), new AutomapNote(),
new ProTrap()};
} else {
return new AddRemovable[]{new Actor(), new ITEPoint(), new SpawnPoint(),
new Entrance(), new Container(), new Ambient(),
new Variable(), new Door(), new Animation(),
new TiledObject()};
}
}
@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 HasViewerTabs ---------------------
@Override
public int getViewerTabCount()
{
return 2;
}
@Override
public String getViewerTabName(int index)
{
switch (index) {
case 0:
return StructViewer.TAB_VIEW;
case 1:
return StructViewer.TAB_RAW;
}
return null;
}
@Override
public JComponent getViewerTab(int index)
{
switch (index) {
case 0: // view tab
{
JScrollPane scroll = new JScrollPane(new Viewer(this));
scroll.setBorder(BorderFactory.createEmptyBorder());
return scroll;
}
case 1: // raw tab
{
if (hexViewer == null) {
hexViewer = new StructHexViewer(this, new BasicColorMap(this, true));
}
return hexViewer;
}
}
return null;
}
@Override
public boolean viewerTabAddedBefore(int index)
{
return (index == 0);
}
// --------------------- End Interface HasViewerTabs ---------------------
// --------------------- Begin Interface Writeable ---------------------
@Override
public void write(OutputStream os) throws IOException
{
super.writeFlatList(os);
}
// --------------------- End Interface Writeable ---------------------
@Override
protected void viewerInitialized(StructViewer viewer)
{
viewer.addTabChangeListener(hexViewer);
}
@Override
protected void datatypeAdded(AddRemovable datatype)
{
HexNumber offset_vertices = (HexNumber)getAttribute(ARE_OFFSET_VERTICES);
if (datatype.getOffset() <= offset_vertices.getValue())
offset_vertices.incValue(datatype.getSize());
HexNumber offset_items = (HexNumber)getAttribute(ARE_OFFSET_ITEMS);
if (datatype.getOffset() <= offset_items.getValue())
offset_items.incValue(datatype.getSize());
if (datatype instanceof HasVertices)
updateVertices();
if (datatype instanceof Container)
updateItems();
updateActorCREOffsets();
if (hexViewer != null) {
hexViewer.dataModified();
}
}
@Override
protected void datatypeAddedInChild(AbstractStruct child, AddRemovable datatype)
{
if (datatype instanceof Vertex)
updateVertices();
else {
HexNumber offset_vertices = (HexNumber)getAttribute(ARE_OFFSET_VERTICES);
if (datatype.getOffset() <= offset_vertices.getValue()) {
offset_vertices.incValue(datatype.getSize());
updateVertices();
}
}
if (datatype instanceof Item)
updateItems();
else {
HexNumber offset_items = (HexNumber)getAttribute(ARE_OFFSET_ITEMS);
if (datatype.getOffset() <= offset_items.getValue()) {
offset_items.incValue(datatype.getSize());
updateItems();
}
}
updateActorCREOffsets();
if (hexViewer != null) {
hexViewer.dataModified();
}
}
@Override
protected void datatypeRemoved(AddRemovable datatype)
{
HexNumber offset_vertices = (HexNumber)getAttribute(ARE_OFFSET_VERTICES);
if (datatype.getOffset() < offset_vertices.getValue())
offset_vertices.incValue(-datatype.getSize());
HexNumber offset_items = (HexNumber)getAttribute(ARE_OFFSET_ITEMS);
if (datatype.getOffset() < offset_items.getValue())
offset_items.incValue(-datatype.getSize());
if (datatype instanceof HasVertices)
updateVertices();
if (datatype instanceof Container)
updateItems();
updateActorCREOffsets();
if (hexViewer != null) {
hexViewer.dataModified();
}
}
@Override
protected void datatypeRemovedInChild(AbstractStruct child, AddRemovable datatype)
{
if (datatype instanceof Vertex)
updateVertices();
else {
HexNumber offset_vertices = (HexNumber)getAttribute(ARE_OFFSET_VERTICES);
if (datatype.getOffset() < offset_vertices.getValue()) {
offset_vertices.incValue(-datatype.getSize());
updateVertices();
}
}
if (datatype instanceof Item)
updateItems();
else {
HexNumber offset_items = (HexNumber)getAttribute(ARE_OFFSET_ITEMS);
if (datatype.getOffset() < offset_items.getValue()) {
offset_items.incValue(-datatype.getSize());
updateItems();
}
}
updateActorCREOffsets();
if (hexViewer != null) {
hexViewer.dataModified();
}
}
@Override
public int read(ByteBuffer buffer, int offset) throws Exception
{
addField(new TextString(buffer, offset, 4, COMMON_SIGNATURE));
TextString version = new TextString(buffer, offset + 4, 4, COMMON_VERSION);
addField(version);
addField(new ResourceRef(buffer, offset + 8, ARE_WED_RESOURCE, "WED"));
addField(new DecNumber(buffer, offset + 16, 4, ARE_LAST_SAVED));
if (version.toString().equalsIgnoreCase("V9.1")) {
addField(new Flag(buffer, offset + 20, 4, ARE_AREA_TYPE, getUpdatedIdsFlags(s_atype_iwd2, "AREAFLAG.IDS", 4, false)));
} else if (Profile.getEngine() == Profile.Engine.PST) {
addField(new Flag(buffer, offset + 20, 4, ARE_AREA_TYPE, getUpdatedIdsFlags(s_atype_torment, null, 4, false)));
} else if (Profile.isEnhancedEdition()) {
addField(new Flag(buffer, offset + 20, 4, ARE_AREA_TYPE, getUpdatedIdsFlags(s_atype_ee, "AREAFLAG.IDS", 4, false)));
} else {
addField(new Flag(buffer, offset + 20, 4, ARE_AREA_TYPE, getUpdatedIdsFlags(s_atype, "AREAFLAG.IDS", 4, false)));
}
addField(new ResourceRef(buffer, offset + 24, ARE_AREA_NORTH, "ARE"));
addField(new Flag(buffer, offset + 32, 4, ARE_EDGE_FLAGS_NORTH, s_edge));
addField(new ResourceRef(buffer, offset + 36, ARE_AREA_EAST, "ARE"));
addField(new Flag(buffer, offset + 44, 4, ARE_EDGE_FLAGS_EAST, s_edge));
addField(new ResourceRef(buffer, offset + 48, ARE_AREA_SOUTH, "ARE"));
addField(new Flag(buffer, offset + 56, 4, ARE_EDGE_FLAGS_SOUTH, s_edge));
addField(new ResourceRef(buffer, offset + 60, ARE_AREA_WEST, "ARE"));
addField(new Flag(buffer, offset + 68, 4, ARE_EDGE_FLAGS_WEST, s_edge));
if (Profile.getGame() == Profile.Game.PST) {
addField(new Flag(buffer, offset + 72, 2, ARE_LOCATION, getUpdatedIdsFlags(s_flag_torment, null, 2, false)));
} else {
addField(new Flag(buffer, offset + 72, 2, ARE_LOCATION, getUpdatedIdsFlags(s_flag, "AREATYPE.IDS", 2, false)));
}
addField(new DecNumber(buffer, offset + 74, 2, ARE_PROBABILITY_RAIN));
addField(new DecNumber(buffer, offset + 76, 2, ARE_PROBABILITY_SNOW));
addField(new DecNumber(buffer, offset + 78, 2, ARE_PROBABILITY_FOG));
addField(new DecNumber(buffer, offset + 80, 2, ARE_PROBABILITY_LIGHTNING));
if (Profile.isEnhancedEdition()) {
addField(new UnsignDecNumber(buffer, offset + 82, 1, ARE_OVERLAY_TRANSPARENCY));
addField(new Unknown(buffer, offset + 83, 1));
} else {
addField(new DecNumber(buffer, offset + 82, 2, ARE_WIND_SPEED));
}
if (version.toString().equalsIgnoreCase("V9.1")) {
addField(new DecNumber(buffer, offset + 84, 1, ARE_AREA_DIFFICULTY_2));
addField(new DecNumber(buffer, offset + 85, 1, ARE_AREA_DIFFICULTY_3));
addField(new Unknown(buffer, offset + 86, 14));
offset += 16;
}
SectionOffset offset_actors = new SectionOffset(buffer, offset + 84, ARE_OFFSET_ACTORS,
Actor.class);
addField(offset_actors);
SectionCount count_actors = new SectionCount(buffer, offset + 88, 2, ARE_NUM_ACTORS,
Actor.class);
addField(count_actors);
SectionCount count_itepoints = new SectionCount(buffer, offset + 90, 2, ARE_NUM_TRIGGERS,
ITEPoint.class);
addField(count_itepoints);
SectionOffset offset_itepoints = new SectionOffset(buffer, offset + 92,
ARE_OFFSET_TRIGGERS,
ITEPoint.class);
addField(offset_itepoints);
SectionOffset offset_spoints = new SectionOffset(buffer, offset + 96, ARE_OFFSET_SPAWN_POINTS,
SpawnPoint.class);
addField(offset_spoints);
SectionCount count_spoints = new SectionCount(buffer, offset + 100, 4, ARE_NUM_SPAWN_POINTS,
SpawnPoint.class);
addField(count_spoints);
SectionOffset offset_entrances = new SectionOffset(buffer, offset + 104, ARE_OFFSET_ENTRANCES,
Entrance.class);
addField(offset_entrances);
SectionCount count_entrances = new SectionCount(buffer, offset + 108, 4, ARE_NUM_ENTRANCES,
Entrance.class);
addField(count_entrances);
SectionOffset offset_containers = new SectionOffset(buffer, offset + 112, ARE_OFFSET_CONTAINERS,
Container.class);
addField(offset_containers);
SectionCount count_containers = new SectionCount(buffer, offset + 116, 2, ARE_NUM_CONTAINERS,
Container.class);
addField(count_containers);
DecNumber count_items = new DecNumber(buffer, offset + 118, 2, ARE_NUM_ITEMS);
addField(count_items);
HexNumber offset_items = new HexNumber(buffer, offset + 120, 4, ARE_OFFSET_ITEMS);
addField(offset_items);
HexNumber offset_vertices = new HexNumber(buffer, offset + 124, 4, ARE_OFFSET_VERTICES);
addField(offset_vertices);
DecNumber count_vertices = new DecNumber(buffer, offset + 128, 2, ARE_NUM_VERTICES);
addField(count_vertices);
SectionCount count_ambients = new SectionCount(buffer, offset + 130, 2, ARE_NUM_AMBIENTS,
Ambient.class);
addField(count_ambients);
SectionOffset offset_ambients = new SectionOffset(buffer, offset + 132, ARE_OFFSET_AMBIENTS,
Ambient.class);
addField(offset_ambients);
SectionOffset offset_variables = new SectionOffset(buffer, offset + 136, ARE_OFFSET_VARIABLES,
Variable.class);
addField(offset_variables);
SectionCount count_variables = new SectionCount(buffer, offset + 140, 2, ARE_NUM_VARIABLES,
Variable.class);
addField(count_variables);
addField(new HexNumber(buffer, offset + 142, 2, ARE_NUM_OBJECT_FLAGS));
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);
addField(size_exploredbitmap);
SectionOffset offset_exploredbitmap = new SectionOffset(buffer, offset + 160, ARE_OFFSET_EXPLORED_BITMAP,
Unknown.class);
addField(offset_exploredbitmap);
SectionCount count_doors = new SectionCount(buffer, offset + 164, 4, ARE_NUM_DOORS,
Door.class);
addField(count_doors);
SectionOffset offset_doors = new SectionOffset(buffer, offset + 168, ARE_OFFSET_DOORS,
Door.class);
addField(offset_doors);
SectionCount count_animations = new SectionCount(buffer, offset + 172, 4, ARE_NUM_ANIMATIONS,
Animation.class);
addField(count_animations);
SectionOffset offset_animations = new SectionOffset(buffer, offset + 176, ARE_OFFSET_ANIMATIONS,
Animation.class);
addField(offset_animations);
SectionCount count_tiledobjects = new SectionCount(buffer, offset + 180, 4, ARE_NUM_TILED_OBJECTS,
TiledObject.class);
addField(count_tiledobjects);
SectionOffset offset_tiledobjects = new SectionOffset(buffer, offset + 184, ARE_OFFSET_TILED_OBJECTS,
TiledObject.class);
addField(offset_tiledobjects);
SectionOffset offset_songs = new SectionOffset(buffer, offset + 188, ARE_OFFSET_SONGS,
Song.class);
addField(offset_songs);
SectionOffset offset_rest = new SectionOffset(buffer, offset + 192, ARE_OFFSET_REST_ENCOUNTERS,
RestSpawn.class);
addField(offset_rest);
SectionOffset offset_automapnote = null, offset_protrap = null;
SectionCount count_automapnote = null, count_protrap = null;
if (Profile.getEngine() == Profile.Engine.PST) {
addField(new Unknown(buffer, offset + 196, 4));
offset_automapnote = new SectionOffset(buffer, offset + 200, ARE_OFFSET_AUTOMAP_NOTES,
AutomapNotePST.class);
addField(offset_automapnote);
count_automapnote = new SectionCount(buffer, offset + 204, 4, ARE_NUM_AUTOMAP_NOTES,
AutomapNotePST.class);
addField(count_automapnote);
addField(new Unknown(buffer, offset + 208, 76));
}
else if (Profile.getEngine() == Profile.Engine.BG2 || Profile.isEnhancedEdition()) {
offset_automapnote = new SectionOffset(buffer, offset + 196, ARE_OFFSET_AUTOMAP_NOTES,
AutomapNote.class);
addField(offset_automapnote);
count_automapnote = new SectionCount(buffer, offset + 200, 4, ARE_NUM_AUTOMAP_NOTES,
AutomapNote.class);
addField(count_automapnote);
offset_protrap = new SectionOffset(buffer, offset + 204, ARE_OFFSET_PROJECTILE_TRAPS,
ProTrap.class);
addField(offset_protrap);
count_protrap = new SectionCount(buffer, offset + 208, 4, ARE_NUM_PROJECTILE_TRAPS,
ProTrap.class);
addField(count_protrap);
final String[] movieExt = (Profile.isEnhancedEdition()) ? new String[]{"MVE", "WBM"} : new String[]{"MVE"};
addField(new ResourceRef(buffer, offset + 212, ARE_REST_MOVIE_DAY, movieExt));
addField(new ResourceRef(buffer, offset + 220, ARE_REST_MOVIE_NIGHT, movieExt));
addField(new Unknown(buffer, offset + 228, 56));
}
else if (Profile.getEngine() == Profile.Engine.IWD2) {
offset_automapnote = new SectionOffset(buffer, offset + 196, ARE_OFFSET_AUTOMAP_NOTES,
AutomapNote.class);
addField(offset_automapnote);
count_automapnote = new SectionCount(buffer, offset + 200, 4, ARE_NUM_AUTOMAP_NOTES,
AutomapNote.class);
addField(count_automapnote);
addField(new Unknown(buffer, offset + 204, 80));
}
else {
addField(new Unknown(buffer, offset + 196, 88));
}
offset = offset_actors.getValue();
for (int i = 0; i < count_actors.getValue(); i++) {
Actor actor = new Actor(this, buffer, offset, i);
offset = actor.getEndOffset();
addField(actor);
}
offset = offset_itepoints.getValue();
for (int i = 0; i < count_itepoints.getValue(); i++) {
ITEPoint ite = new ITEPoint(this, buffer, offset, i);
offset = ite.getEndOffset();
addField(ite);
}
offset = offset_spoints.getValue();
for (int i = 0; i < count_spoints.getValue(); i++) {
SpawnPoint sp = new SpawnPoint(this, buffer, offset, i);
offset = sp.getEndOffset();
addField(sp);
}
offset = offset_entrances.getValue();
for (int i = 0; i < count_entrances.getValue(); i++) {
Entrance ent = new Entrance(this, buffer, offset, i);
offset = ent.getEndOffset();
addField(ent);
}
offset = offset_containers.getValue();
for (int i = 0; i < count_containers.getValue(); i++) {
Container con = new Container(this, buffer, offset, i);
offset = con.getEndOffset();
addField(con);
}
offset = offset_ambients.getValue();
for (int i = 0; i < count_ambients.getValue(); i++) {
Ambient ambi = new Ambient(this, buffer, offset, i);
offset = ambi.getEndOffset();
addField(ambi);
}
offset = offset_variables.getValue();
for (int i = 0; i < count_variables.getValue(); i++) {
Variable var = new Variable(this, buffer, offset, i);
offset = var.getEndOffset();
addField(var);
}
offset = offset_exploredbitmap.getValue();
if (size_exploredbitmap.getValue() > 0) {
addField(new Unknown(buffer, offset, size_exploredbitmap.getValue(), ARE_EXPLORED_BITMAP));
}
offset = offset_doors.getValue();
for (int i = 0; i < count_doors.getValue(); i++) {
Door door = new Door(this, buffer, offset, i);
offset = door.getEndOffset();
addField(door);
}
offset = offset_animations.getValue();
for (int i = 0; i < count_animations.getValue(); i++) {
Animation anim = new Animation(this, buffer, offset, i);
offset = anim.getEndOffset();
addField(anim);
}
offset = offset_tiledobjects.getValue();
for (int i = 0; i < count_tiledobjects.getValue(); i++) {
TiledObject tile = new TiledObject(this, buffer, offset, i);
offset = tile.getEndOffset();
addField(tile);
}
if (offset_automapnote != null) { // Torment, BG2
offset = offset_automapnote.getValue();
if (Profile.getEngine() == Profile.Engine.PST) {
for (int i = 0; i < count_automapnote.getValue(); i++) {
AutomapNotePST note = new AutomapNotePST(this, buffer, offset, i);
offset = note.getEndOffset();
addField(note);
}
}
else {
for (int i = 0; i < count_automapnote.getValue(); i++) {
AutomapNote note = new AutomapNote(this, buffer, offset, i);
offset = note.getEndOffset();
addField(note);
}
}
}
if (offset_protrap != null) { // BG2
offset = offset_protrap.getValue();
for (int i = 0; i < count_protrap.getValue(); i++) {
ProTrap trap = new ProTrap(this, buffer, offset, i);
offset = trap.getEndOffset();
addField(trap);
}
}
offset = offset_items.getValue();
for (int i = 0; i < getFieldCount(); i++) {
Object o = getField(i);
if (o instanceof Container)
((Container)o).readItems(buffer, offset);
}
offset = offset_vertices.getValue();
for (int i = 0; i < getFieldCount(); i++) {
Object o = getField(i);
if (o instanceof HasVertices)
((HasVertices)o).readVertices(buffer, offset);
}
if (offset_songs.getValue() > 0) {
addField(new Song(this, buffer, offset_songs.getValue()));
}
if (offset_rest.getValue() > 0) {
addField(new RestSpawn(this, buffer, offset_rest.getValue()));
}
int endoffset = offset;
for (int i = 0; i < getFieldCount(); i++) {
StructEntry entry = getField(i);
if (entry instanceof HasVertices) {
// may contain additional elements
for (int j = 0, count = ((AbstractStruct)entry).getFieldCount(); j < count; j++) {
StructEntry subEntry = ((AbstractStruct)entry).getField(j);
endoffset = Math.max(endoffset, subEntry.getOffset() + subEntry.getSize());
}
} else {
if (entry.getOffset() + entry.getSize() > endoffset) {
endoffset = Math.max(endoffset, entry.getOffset() + entry.getSize());
}
}
}
return endoffset;
}
private void updateActorCREOffsets()
{
for (int i = 0; i < getFieldCount(); i++) {
Object o = getField(i);
if (o instanceof Actor) {
((Actor)o).updateCREOffset();
}
}
}
private void updateItems()
{
// Assumes items offset is correct
int offset = ((HexNumber)getAttribute(ARE_OFFSET_ITEMS)).getValue();
int count = 0;
for (int i = 0; i < getFieldCount(); i++) {
Object o = getField(i);
if (o instanceof Container) {
Container container = (Container)o;
int itemNum = container.updateItems(offset, count);
offset += 20 * itemNum;
count += itemNum;
}
}
((DecNumber)getAttribute(ARE_NUM_ITEMS)).setValue(count);
}
private void updateVertices()
{
// Assumes vertices offset is correct
int offset = ((HexNumber)getAttribute(ARE_OFFSET_VERTICES)).getValue();
int count = 0;
for (int i = 0; i < getFieldCount(); i++) {
Object o = getField(i);
if (o instanceof HasVertices) {
HasVertices vert = (HasVertices)o;
int vertNum = vert.updateVertices(offset, count);
offset += 4 * vertNum;
count += vertNum;
}
}
((DecNumber)getAttribute(ARE_NUM_VERTICES)).setValue(count);
}
/**
* Returns a String array for Flag datatypes, that has been updated or overwritten with entries
* from the specified IDS resource.
* @param flags A static String array used as basis for Flag labels.
* @param idsFile IDS resource to take entries from.
* @param size Size of flags field in bytes. (Range: 1..4)
* @param overwrite If {@code true}, then static flag label will be overwritten with entries
* from the IDS resource.
* If {@code false}, then entries from IDS resource will be used only for
* missing or empty entries in the {@code flags} array.
* @return
*/
public static String[] getUpdatedIdsFlags(String[] flags, String idsFile, int size, boolean overwrite)
{
ArrayList<String> list = new ArrayList<String>(32);
size = Math.max(1, Math.min(4, size));
// adding static labels
if (flags != null && flags.length > 1) {
for (final String f: flags) {
list.add(f);
}
} else {
list.add(null); // empty flags label
}
// updating list with labels from IDS entries
if (ResourceFactory.resourceExists(idsFile)) {
IdsMap map = IdsMapCache.get(idsFile);
if (map != null) {
int numBits = size * 8;
for (int i = 0; i < numBits; i++) {
IdsMapEntry entry = map.getValue((long)(1 << i));
String s = (entry != null) ? entry.getString() : null;
if (i < list.size() - 1) {
if (overwrite || list.get(i+1) == null) {
list.set(i+1, s);
}
} else {
list.add(s);
}
}
}
}
// cleaning up trailing null labels
while (list.size() > 1 && list.get(list.size() - 1) == null) {
list.remove(list.size() - 1);
}
// converting list into array
String[] retVal = new String[list.size()];
for (int i = 0; i < retVal.length; i++) {
retVal[i] = list.get(i);
}
return retVal;
}
/** Displays the area viewer for this ARE resource. */
AreaViewer showAreaViewer(Component parent)
{
if (areaViewer == null) {
areaViewer = new AreaViewer(parent, this);
} else if (!areaViewer.isVisible()) {
areaViewer.setVisible(true);
areaViewer.toFront();
} else {
areaViewer.toFront();
}
return areaViewer;
}
// Called by "Extended Search"
// Checks whether the specified resource entry matches all available search options.
public static boolean matchSearchOptions(ResourceEntry entry, SearchOptions searchOptions)
{
if (entry != null && searchOptions != null) {
try {
AreResource are = new AreResource(entry);
Actor[] actors;
Animation[] animations;
Item[][] items;
boolean retVal = true;
String key;
Object o;
// preparing substructures
DecNumber ofs = (DecNumber)are.getAttribute(ARE_OFFSET_ACTORS, false);
DecNumber cnt = (DecNumber)are.getAttribute(ARE_NUM_ACTORS, false);
if (ofs != null && ofs.getValue() > 0 && cnt != null && cnt.getValue() > 0) {
actors = new Actor[cnt.getValue()];
for (int idx = 0; idx < actors.length; idx++) {
actors[idx] = (Actor)are.getAttribute(String.format(SearchOptions.getResourceName(SearchOptions.ARE_Actor), idx), false);
}
} else {
actors = new Actor[0];
}
ofs = (DecNumber)are.getAttribute(ARE_OFFSET_ANIMATIONS, false);
cnt = (DecNumber)are.getAttribute(ARE_NUM_ANIMATIONS, false);
if (ofs != null && ofs.getValue() > 0 && cnt != null && cnt.getValue() > 0) {
animations = new Animation[cnt.getValue()];
for (int idx = 0; idx < animations.length; idx++) {
animations[idx] = (Animation)are.getAttribute(String.format(SearchOptions.getResourceName(SearchOptions.ARE_Animation), idx), false);
}
} else {
animations = new Animation[0];
}
ofs = (DecNumber)are.getAttribute(ARE_OFFSET_CONTAINERS, false);
cnt = (DecNumber)are.getAttribute(ARE_NUM_CONTAINERS, false);
if (ofs != null && ofs.getValue() > 0 && cnt != null && cnt.getValue() > 0) {
items = new Item[cnt.getValue()][];
for (int i = 0; i < cnt.getValue(); i++) {
String label = String.format(SearchOptions.getResourceName(SearchOptions.ARE_Container), i);
Container container = (Container)are.getAttribute(label, false);
if (container != null) {
DecNumber cnt2 = (DecNumber)container.getAttribute(ARE_NUM_ITEMS, false);
if (cnt2 != null && cnt2.getValue() > 0) {
items[i] = new Item[cnt2.getValue()];
for (int j = 0; j < cnt2.getValue(); j++) {
label = String.format(SearchOptions.getResourceName(SearchOptions.ARE_Container_Item), j);
items[i][j] = (Item)container.getAttribute(label, false);
}
} else {
items[i] = new Item[0];
}
} else {
items[i] = new Item[0];
}
}
} else {
items = new Item[0][];
}
// checking options
String[] keyList = new String[]{SearchOptions.ARE_AreaType,
SearchOptions.ARE_Location};
for (int idx = 0; idx < keyList.length; idx++) {
if (retVal) {
key = keyList[idx];
o = searchOptions.getOption(key);
StructEntry struct = are.getAttribute(SearchOptions.getResourceName(key), false);
retVal &= SearchOptions.Utils.matchFlags(struct, o);
} else {
break;
}
}
if (retVal) {
key = SearchOptions.ARE_AreaScript;
o = searchOptions.getOption(key);
StructEntry struct = are.getAttribute(SearchOptions.getResourceName(key), false);
retVal &= SearchOptions.Utils.matchResourceRef(struct, o, false);
}
if (retVal) {
key = SearchOptions.ARE_Actor_Character;
o = searchOptions.getOption(key);
boolean found = false;
for (int idx = 0; idx < actors.length; idx++) {
if (actors[idx] != null) {
StructEntry struct = actors[idx].getAttribute(SearchOptions.getResourceName(key), false);
found |= SearchOptions.Utils.matchResourceRef(struct, o, false);
}
}
retVal &= found || (o == null);
}
if (retVal) {
key = SearchOptions.ARE_Animation_Animation;
o = searchOptions.getOption(key);
boolean found = false;
for (int idx = 0; idx < animations.length; idx++) {
if (animations[idx] != null) {
StructEntry struct = animations[idx].getAttribute(SearchOptions.getResourceName(key), false);
found |= SearchOptions.Utils.matchResourceRef(struct, o, false);
}
}
retVal &= found || (o == null);
}
if (retVal) {
key = SearchOptions.ARE_Container_Item_Item;
o = searchOptions.getOption(key);
boolean found = false;
for (int idx = 0; idx < items.length; idx++) {
for (int idx2 = 0; idx2 < items[idx].length; idx2++) {
if (items[idx][idx2] != null) {
StructEntry struct = items[idx][idx2].getAttribute(SearchOptions.getResourceName(key), false);
found |= SearchOptions.Utils.matchResourceRef(struct, o, false);
}
if (found) {
break;
}
}
if (found) {
break;
}
}
retVal &= found || (o == null);
}
keyList = new String[]{SearchOptions.ARE_Custom1, SearchOptions.ARE_Custom2,
SearchOptions.ARE_Custom3, SearchOptions.ARE_Custom4};
for (int idx = 0; idx < keyList.length; idx++) {
if (retVal) {
key = keyList[idx];
o = searchOptions.getOption(key);
retVal &= SearchOptions.Utils.matchCustomFilter(are, o);
} else {
break;
}
}
return retVal;
} catch (Exception e) {
}
}
return false;
}
}