diff --git a/README.md b/README.md index 5961b438c..a9637d53b 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ +[![GitHub release (latest by date)](https://img.shields.io/github/v/release/Argent77/NearInfinity?color=darkred&include_prereleases&label=latest%20release)](https://GitHub.com/Argent77/NearInfinity/releases/latest) +[![GitHub release date (latest by date)](https://img.shields.io/github/release-date/Argent77/NearInfinity?color=gold)](https://GitHub.com/Argent77/NearInfinity/releases/latest) +[![Github downloads (total)](https://img.shields.io/github/downloads/Argent77/NearInfinity/total.svg?color=blueviolet)](https://GitHub.com/Argent77/NearInfinity/releases) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/b5d10ca2684c4e30b6632e34584f1241)](https://www.codacy.com/manual/Mingun/NearInfinity) # Near Infinity diff --git a/build.xml b/build.xml index 1cd8a94b7..db1f615ad 100644 --- a/build.xml +++ b/build.xml @@ -55,7 +55,8 @@ + includes="**/*" + level="9"> diff --git a/src/org/infinity/NearInfinity.java b/src/org/infinity/NearInfinity.java index d09bf4eea..373906bfb 100644 --- a/src/org/infinity/NearInfinity.java +++ b/src/org/infinity/NearInfinity.java @@ -38,6 +38,7 @@ import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.ArrayList; +import java.util.Arrays; import java.util.Enumeration; import java.util.List; import java.util.Locale; @@ -91,6 +92,9 @@ import org.infinity.resource.Viewable; import org.infinity.resource.ViewableContainer; import org.infinity.resource.bcs.Signatures; +import org.infinity.resource.cre.decoder.util.ItemInfo; +import org.infinity.resource.cre.decoder.util.SpriteUtils; +import org.infinity.resource.graphics.ColorConvert; import org.infinity.resource.key.FileResourceEntry; import org.infinity.resource.key.ResourceEntry; import org.infinity.resource.key.ResourceTreeModel; @@ -208,6 +212,8 @@ public static void printHelp(String jarFile) System.out.println("\nOptions:"); System.out.println(" -v, -version Display version information."); System.out.println(" -h, -help Display this help."); + System.out.println(" -i Disable support of case-sensitive filesystems"); + System.out.println(" (temporary workaround for buggy file access on Linux systems)"); System.out.println(" -t type Force the current or specified game to be of"); System.out.println(" specific type. (Use with care!)"); System.out.println(" Supported game types:"); @@ -232,6 +238,12 @@ public static void advanceProgress(String note) public static void main(String args[]) { + // TODO: remove option when detection of case-sensitive filesystems has been fixed + if (Arrays.asList(args).contains("-i")) { + // must be set before first file access through FileManager class + Profile.addProperty(Profile.Key.GET_GLOBAL_FILE_CASE_CHECK, Profile.Type.BOOLEAN, Boolean.valueOf(false)); + } + Profile.Game forcedGame = null; Path gameOverride = null; @@ -1043,6 +1055,9 @@ private static void clearCache(boolean refreshOnly) StringTable.resetAll(); ProRef.clearCache(); Signatures.clearCache(); + ColorConvert.clearCache(); + SpriteUtils.clearCache(); + ItemInfo.clearCache(); } private static void showProgress(String msg, int max) @@ -1105,7 +1120,7 @@ private void storePreferences() private void setAppIcon() { - List list = new ArrayList(); + List list = new ArrayList<>(); for (int i = 4; i < 8; i++) { list.add(Icons.getImage(String.format("App%d.png", 1 << i))); } diff --git a/src/org/infinity/check/AbstractChecker.java b/src/org/infinity/check/AbstractChecker.java index a21cf3d6d..8ddc7f4db 100644 --- a/src/org/infinity/check/AbstractChecker.java +++ b/src/org/infinity/check/AbstractChecker.java @@ -84,7 +84,6 @@ protected boolean runCheck(List entries) return runSearch("Checking", entries); } - // @Override public void actionPerformed(ActionEvent event) { @@ -99,5 +98,4 @@ else if (event.getSource() == bCancel) { settingsWindow.setVisible(false); } } - // } diff --git a/src/org/infinity/check/CreInvChecker.java b/src/org/infinity/check/CreInvChecker.java index fd98c76c9..a47bb8657 100644 --- a/src/org/infinity/check/CreInvChecker.java +++ b/src/org/infinity/check/CreInvChecker.java @@ -25,6 +25,7 @@ import org.infinity.NearInfinity; import org.infinity.datatype.DecNumber; +import org.infinity.datatype.IsNumeric; import org.infinity.gui.BrowserMenuBar; import org.infinity.gui.Center; import org.infinity.gui.ChildFrame; @@ -199,7 +200,7 @@ private void checkCreature(CreResource cre) { final List items = new ArrayList<>(); final List slots = new ArrayList<>(); - final DecNumber slots_offset = (DecNumber)cre.getAttribute(CreResource.CRE_OFFSET_ITEM_SLOTS); + final IsNumeric slots_offset = (IsNumeric)cre.getAttribute(CreResource.CRE_OFFSET_ITEM_SLOTS); for (final StructEntry entry : cre.getFields()) { if (entry instanceof Item) { items.add((Item)entry); diff --git a/src/org/infinity/check/EffectsIndexChecker.java b/src/org/infinity/check/EffectsIndexChecker.java index ed7e48b93..0420c2380 100644 --- a/src/org/infinity/check/EffectsIndexChecker.java +++ b/src/org/infinity/check/EffectsIndexChecker.java @@ -5,8 +5,7 @@ package org.infinity.check; import org.infinity.NearInfinity; -import org.infinity.datatype.DecNumber; -import org.infinity.datatype.SectionCount; +import org.infinity.datatype.IsNumeric; import org.infinity.resource.AbstractAbility; import org.infinity.resource.AbstractStruct; import org.infinity.resource.Resource; @@ -56,12 +55,12 @@ protected Runnable newWorker(ResourceEntry entry) private void search(ResourceEntry entry, AbstractStruct struct) { - final int numGlobalEffects = ((SectionCount) struct.getAttribute(SplResource.SPL_NUM_GLOBAL_EFFECTS)).getValue(); + final int numGlobalEffects = ((IsNumeric) struct.getAttribute(SplResource.SPL_NUM_GLOBAL_EFFECTS)).getValue(); int expectedEffectsIndex = numGlobalEffects; for (final StructEntry e : struct.getFields()) { if (e instanceof AbstractAbility) { final AbstractAbility abil = (AbstractAbility) e; - final int effectsIndex = ((DecNumber) abil.getAttribute(AbstractAbility.ABILITY_FIRST_EFFECT_INDEX)).getValue(); + final int effectsIndex = ((IsNumeric) abil.getAttribute(AbstractAbility.ABILITY_FIRST_EFFECT_INDEX)).getValue(); if (effectsIndex != expectedEffectsIndex) { synchronized (hitFrame) { hitFrame.addHit(entry, entry.getSearchString(), abil); diff --git a/src/org/infinity/check/IDSRefChecker.java b/src/org/infinity/check/IDSRefChecker.java index ff248d608..3ac8bb258 100644 --- a/src/org/infinity/check/IDSRefChecker.java +++ b/src/org/infinity/check/IDSRefChecker.java @@ -57,7 +57,7 @@ private void search(ResourceEntry entry, AbstractStruct struct) if (e instanceof IdsBitmap) { final IdsBitmap ref = (IdsBitmap)e; final long value = ref.getLongValue(); - if (value != 0L && ref.getValueOf(value) == null) { + if (value != 0L && ref.getDataOf(value) == null) { synchronized (hitFrame) { hitFrame.addHit(entry, entry.getSearchString(), ref); } diff --git a/src/org/infinity/check/StructChecker.java b/src/org/infinity/check/StructChecker.java index be11dc7a7..c00b7ed15 100644 --- a/src/org/infinity/check/StructChecker.java +++ b/src/org/infinity/check/StructChecker.java @@ -25,10 +25,8 @@ import org.infinity.NearInfinity; import org.infinity.datatype.DecNumber; -import org.infinity.datatype.Flag; -import org.infinity.datatype.ResourceRef; -import org.infinity.datatype.SectionCount; -import org.infinity.datatype.SectionOffset; +import org.infinity.datatype.IsNumeric; +import org.infinity.datatype.IsReference; import org.infinity.datatype.TextString; import org.infinity.gui.BrowserMenuBar; import org.infinity.gui.Center; @@ -50,7 +48,7 @@ public final class StructChecker extends AbstractChecker implements ListSelectio { private static final String[] FILETYPES = {"ARE", "CHR", "CHU", "CRE", "DLG", "EFF", "GAM", "ITM", "PRO", "SPL", "STO", "VEF", "VVC", "WED", "WMP"}; - private static final HashMap fileInfo = new HashMap(); + private static final HashMap fileInfo = new HashMap<>(); static { fileInfo.put("ARE", new StructInfo("AREA", new String[]{"V1.0", "V9.1"})); fileInfo.put("CHR", new StructInfo("CHR ", new String[]{"V1.0", "V1.2", "V2.0", "V2.1", "V2.2", "V9.0"})); @@ -290,8 +288,8 @@ private List getWedCorruption(ResourceEntry entry, AbstractStruct st final List list = new ArrayList<>(); if (entry.getExtension().equalsIgnoreCase("WED")) { final int ovlSize = 0x18; // size of an Overlay structure - int ovlCount = ((SectionCount)struct.getAttribute(8, false)).getValue(); // # overlays - int ovlStartOfs = ((SectionOffset)struct.getAttribute(16, false)).getValue(); // Overlays offset + int ovlCount = ((IsNumeric)struct.getAttribute(8, false)).getValue(); // # overlays + int ovlStartOfs = ((IsNumeric)struct.getAttribute(16, false)).getValue(); // Overlays offset for (int ovlIdx = 0; ovlIdx < ovlCount; ovlIdx++) { int ovlOfs = ovlStartOfs + ovlIdx*ovlSize; @@ -299,11 +297,11 @@ private List getWedCorruption(ResourceEntry entry, AbstractStruct st if (overlay == null) { continue; } - int width = ((DecNumber)overlay.getAttribute(ovlOfs + 0, false)).getValue(); - int height = ((DecNumber)overlay.getAttribute(ovlOfs + 2, false)).getValue(); - String tisName = ((ResourceRef)overlay.getAttribute(ovlOfs + 4, false)).getResourceName(); - int tileStartOfs = ((SectionOffset)overlay.getAttribute(ovlOfs + 16, false)).getValue(); - int indexStartOfs = ((SectionOffset)overlay.getAttribute(ovlOfs + 20, false)).getValue(); + int width = ((IsNumeric)overlay.getAttribute(ovlOfs + 0, false)).getValue(); + int height = ((IsNumeric)overlay.getAttribute(ovlOfs + 2, false)).getValue(); + String tisName = ((IsReference)overlay.getAttribute(ovlOfs + 4, false)).getResourceName(); + int tileStartOfs = ((IsNumeric)overlay.getAttribute(ovlOfs + 16, false)).getValue(); + int indexStartOfs = ((IsNumeric)overlay.getAttribute(ovlOfs + 20, false)).getValue(); if (tisName == null || tisName.isEmpty() || !ResourceFactory.resourceExists(tisName)) { continue; } @@ -359,7 +357,7 @@ private List getWedCorruption(ResourceEntry entry, AbstractStruct st mapTiles.put(Integer.valueOf(index), (Tilemap)item); } else if (item.getOffset() > indexStartOfs && curOfs < indexEndOfs && item instanceof DecNumber) { int index = (curOfs - indexStartOfs) / 2; - mapIndices.put(Integer.valueOf(index), Integer.valueOf(((DecNumber)item).getValue())); + mapIndices.put(Integer.valueOf(index), Integer.valueOf(((IsNumeric)item).getValue())); } } // checking indices @@ -368,11 +366,11 @@ private List getWedCorruption(ResourceEntry entry, AbstractStruct st if (tile != null) { int tileOfs = tile.getOffset(); int tileIdx = (tileOfs - tileStartOfs) / tileSize; - int tileIdxPri = ((DecNumber)tile.getAttribute(tileOfs + 0, false)).getValue(); - int tileCountPri = ((DecNumber)tile.getAttribute(tileOfs + 2, false)).getValue(); - int tileIdxSec = ((DecNumber)tile.getAttribute(tileOfs + 4, false)).getValue(); - Flag tileFlag = (Flag)tile.getAttribute(tileOfs + 6, false); - int tileFlagValue = (int)tileFlag.getValue(); + int tileIdxPri = ((IsNumeric)tile.getAttribute(tileOfs + 0, false)).getValue(); + int tileCountPri = ((IsNumeric)tile.getAttribute(tileOfs + 2, false)).getValue(); + int tileIdxSec = ((IsNumeric)tile.getAttribute(tileOfs + 4, false)).getValue(); + IsNumeric tileFlag = (IsNumeric)tile.getAttribute(tileOfs + 6, false); + int tileFlagValue = tileFlag.getValue(); for (int j = tileIdxPri, count = tileIdxPri + tileCountPri; j < count; j++) { Integer tileLookupIndex = mapIndices.get(Integer.valueOf(j)); if (tileLookupIndex != null) { diff --git a/src/org/infinity/datatype/AbstractBitmap.java b/src/org/infinity/datatype/AbstractBitmap.java new file mode 100644 index 000000000..79c17bab4 --- /dev/null +++ b/src/org/infinity/datatype/AbstractBitmap.java @@ -0,0 +1,534 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.datatype; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; +import java.util.TreeMap; +import java.util.function.BiFunction; + +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; + +import org.infinity.gui.StructViewer; +import org.infinity.gui.TextListPanel; +import org.infinity.gui.ViewerUtil; +import org.infinity.icon.Icons; +import org.infinity.resource.AbstractStruct; +import org.infinity.util.Misc; + +/** + * Common base class for fields that represent an integer enumeration of some values. + * @param type of the symbolic representation of the numeric value + */ +public class AbstractBitmap extends Datatype implements Editable, IsNumeric +{ + // The default formatter if none is specified in the constructor + private final BiFunction formatterDefault = (value, item) -> { + String s; + if (isShowAsHex()) { + switch (getSize()) { + case 1: + s = String.format("0x%02X", value); + break; + case 2: + s = String.format("0x%04X", value); + break; + case 4: + s = String.format("0x%08X", value); + break; + default: + s = String.format("0x%X", value); + } + } else { + s = value.toString(); + } + if (item != null) { + return item.toString() + " - " + s; + } else { + return "Unknown - " + s; + } + }; + + private final TreeMap itemMap; + private final List buttonList; + + private BiFunction formatter; + private TextListPanel> list; + private JButton bUpdate; + private long value; + private boolean signed; + private boolean sortByName; + private boolean showAsHex; + + /** + * Constructs a new field that represents an integer enumeration using a default formatter. + * By default value is treated as unsigned and shown in decimal notation. List is sorted by value. + * @param buffer the buffer object + * @param offset offset of data in the buffer + * @param length size of data object in bytes + * @param name field name + * @param items a collection of number-to-symbol mappings + */ + public AbstractBitmap(ByteBuffer buffer, int offset, int length, String name, TreeMap items) + { + this(buffer, offset, length, name, items, null, false); + } + + /** + * Constructs a new field that represents an integer enumeration. + * By default value is treated as unsigned and shown in decimal notation. List is sorted by value. + * @param buffer the buffer object + * @param offset offset of data in the buffer + * @param length size of data object in bytes + * @param name field name + * @param items a collection of number-to-symbol mappings + * @param formatter a function that is used to produce the textual output + */ + public AbstractBitmap(ByteBuffer buffer, int offset, int length, String name, TreeMap items, + BiFunction formatter) + { + this(buffer, offset, length, name, items, formatter, false); + } + + /** + * Constructs a new field that represents an integer enumeration. + * By default value is shown in decimal notation. List is sorted by value. + * @param buffer the buffer object + * @param offset offset of data in the buffer + * @param length size of data object in bytes + * @param name field name + * @param items a collection of number-to-symbol mappings + * @param formatter a function that is used to produce the textual output + * @param signed indicates whether numeric value is treated as a signed value + */ + public AbstractBitmap(ByteBuffer buffer, int offset, int length, String name, TreeMap items, + BiFunction formatter, boolean signed) + { + super(offset, length, name); + this.itemMap = items; + this.signed = signed; + this.formatter = (formatter != null) ? formatter : formatterDefault; + this.buttonList = new ArrayList<>(); + this.bUpdate = new JButton("Update value", Icons.getIcon(Icons.ICON_REFRESH_16)); + this.buttonList.add(this.bUpdate); + + read(buffer, offset); + } + + //--------------------- Begin Interface Writeable --------------------- + + @Override + public void write(OutputStream os) throws IOException + { + writeLong(os, value); + } + + //--------------------- End Interface Writeable --------------------- + + //--------------------- Begin Interface Readable --------------------- + + @Override + public int read(ByteBuffer buffer, int offset) + { + buffer.position(offset); + switch (getSize()) { + case 1: + value = buffer.get() & 0xffL; + break; + case 2: + value = buffer.getShort() & 0xffffL; + break; + case 4: + value = buffer.getInt() & 0xffffffffL; + break; + default: + throw new IllegalArgumentException(); + } + setValue(value); // called to ensure correct signedness + return offset + getSize(); + } + + //--------------------- End Interface Readable --------------------- + + //--------------------- Begin Interface IsNumeric --------------------- + + @Override + public long getLongValue() + { + return value; + } + + @Override + public int getValue() + { + return (int)value; + } + + //--------------------- End Interface IsNumeric --------------------- + + //--------------------- Begin Interface Editable --------------------- + + @Override + public JComponent edit(ActionListener container) + { + FormattedData selected = null; + final List> items = new ArrayList<>(itemMap.size()); + for (final Long key : itemMap.keySet()) { + FormattedData item = new FormattedData<>(key, itemMap.get(key), formatter); + items.add(item); + if (key.intValue() == value) { + selected = item; + } + } + + list = new TextListPanel<>(items, sortByName); + list.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent event) + { + if (event.getClickCount() == 2) { + container.actionPerformed(new ActionEvent(this, 0, StructViewer.UPDATE_VALUE)); + } + } + }); + list.addListSelectionListener(new ListSelectionListener() { + @Override + public void valueChanged(ListSelectionEvent event) + { + if (!event.getValueIsAdjusting()) { + listItemChanged(); + } + } + }); + + if (selected != null) { + list.setSelectedValue(selected, true); + } + + bUpdate.addActionListener(container); + bUpdate.setActionCommand(StructViewer.UPDATE_VALUE); + + // lay out controls + JPanel panel = new JPanel(new GridBagLayout()); + GridBagConstraints c = new GridBagConstraints(); + + c = ViewerUtil.setGBC(c, 0, 0, 1, buttonList.size() + 2, 1.0, 1.0, GridBagConstraints.CENTER, + GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0); + panel.add(list, c); + + c = ViewerUtil.setGBC(c, 1, 0, 1, 1, 1.0, 1.0, GridBagConstraints.LINE_START, + GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0); + panel.add(new JPanel(), c); + for (int i = 0; i < buttonList.size(); i++) { + c = ViewerUtil.setGBC(c, 1, 1 + i, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(0, 6, 8, 0), 0, 0); + panel.add(buttonList.get(i), c); + } + c = ViewerUtil.setGBC(c, 1, 1 + buttonList.size(), 1, 1, 1.0, 1.0, GridBagConstraints.LINE_START, + GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0); + panel.add(new JPanel(), c); + + panel.setMinimumSize(Misc.getScaledDimension(DIM_MEDIUM)); + return panel; + } + + @Override + public void select() + { + list.ensureIndexIsVisible(list.getSelectedIndex()); + } + + @Override + public boolean updateValue(AbstractStruct struct) + { + long oldValue = getLongValue(); + + // updating value + FormattedData item = list.getSelectedValue(); + if (item != null) { + setValue(item.getValue().longValue()); + } else { + return false; + } + + // notifying listeners + if (getLongValue() != oldValue) { + fireValueUpdated(new UpdateEvent(this, struct)); + } + + return true; + } + + //--------------------- End Interface Editable --------------------- + + @Override + public String toString() + { + return toString(value); + } + + @Override + public int hashCode() + { + int hash = super.hashCode(); + + // taking care of signedness + long mask = (1L << (getSize() * 8)) - 1L; + hash = 31 * hash + Long.hashCode(value & mask); + + return hash; + } + + @Override + public boolean equals(Object o) + { + if (!super.equals(o) || !(o instanceof AbstractBitmap)) { + return false; + } + AbstractBitmap other = (AbstractBitmap)o; + + // taking care of signedness + long mask = (1L << (getSize() * 8)) - 1L; + boolean retVal = ((value & mask) == (other.value & mask)); + + return retVal; + } + + /** Returns the TextListPanel control used by this datatype. */ + public JComponent getUiControl() + { + return list; + } + + /** + * Returns the bitmap table used to associated numeric values with symbolic data. + */ + public TreeMap getBitmap() + { + return itemMap; + } + + /** + * Returns the numeric value of the selected entry in the list control. + * Returns {@code null} if no valid list item is selected. + */ + public Long getSelectedValue() + { + Long retVal = null; + FormattedData item = list.getSelectedValue(); + if (item != null) { + retVal = item.getValue(); + } + return retVal; + } + + /** + * Returns the data object associated with the specified argument. + * @param value a numeric value + * @return the data object associated with the numeric value, {@code null} otherwise. + */ + public T getDataOf(long value) + { + return itemMap.get(Long.valueOf(value)); + } + + /** + * Calls the formatter on the data associated with the specified argument and returns the result. + * @param value a numeric value + * @return a {@code String} based on the data associated with the specified argument + */ + public String toString(long value) + { + Long number = Long.valueOf(value); + T data = itemMap.get(Long.valueOf(value)); + return formatter.apply(number, data); + } + + /** Returns the function object that is used to create the return value of the {@link #toString()} method. */ + public BiFunction getFormatter() + { + return formatter; + } + + /** + * Assigns a new formatter function object that is used to create the textual representation of the field value. + * @param formatter the new formatter function object. Specify {@code null} too use the default formatter. + */ + public void setFormatter(BiFunction formatter) + { + if (formatter == null) { + formatter = formatterDefault; + } + this.formatter = formatter; + } + + /** Returns whether the value is treated as a signed number. */ + public boolean isSigned() + { + return signed; + } + + /** Sets whether the value is treated as a signed number. The current value is converted accordingly. */ + public void setSigned(boolean b) + { + if (signed != b) { + signed = b; + setValue(getLongValue()); + } + } + + /** Returns whether whether entries are sorted by name instead of value. */ + public boolean isSortByName() + { + return sortByName; + } + + /** Sets whether whether entries are sorted by name instead of value. + * Does nothing if this method is called after the UI control is created. */ + public void setSortByName(boolean b) + { + if (list == null) { + sortByName = b; + } + } + + /** Returns whether numeric value is shown in hexadecimal notation. */ + public boolean isShowAsHex() + { + return showAsHex; + } + + /** Sets whether numeric value is shown in hexadecimal notation. */ + public void setShowAsHex(boolean b) + { + showAsHex = b; + } + + /** + * Adds the specified data to the item list. Existing entries will be overwritten. + * @param value the numeric value + * @param data data associated with the value + */ + protected void putItem(long value, T data) + { + itemMap.put(Long.valueOf(value), data); + } + + /** Assigns a new value. */ + protected void setValue(long newValue) + { + final long oldValue = getLongValue(); + + // ensure correct signedness + switch (getSize()) { + case 1: + newValue = isSigned() ? ((newValue << 56) >> 56) : (newValue & 0xffL); + break; + case 2: + newValue = isSigned() ? ((newValue << 48) >> 48) : (newValue & 0xffffL); + break; + case 4: + newValue = isSigned() ? ((newValue << 32) >> 32) : (newValue & 0xffffffffL); + break; + } + + this.value = newValue; + if (oldValue != this.value) { + firePropertyChange(oldValue, newValue); + } + } + + /** + * Allows derived classes to add custom buttons to the UI control. + * Only effective if called before the UI control is created. + */ + protected void addButtons(JButton... buttons) + { + // It makes no sense to register new buttons if the list panel has already been created + if (list == null) { + for (final JButton btn : buttons) { + if (btn != null) { + buttonList.add(btn); + } + } + } + } + + /** Called whenever the user selects a new list item. */ + protected void listItemChanged() + { + } + + /** + * Helper method: returns the specified number in hexadecimal notation. + * @param value the value to return as hexadecimal representation. + * @param size size of the value in bytes. + * @return String containing hexadecimal notation of the specified value. + */ + protected String getHexValue(long value) + { + switch (getSize()) { + case 1: + return String.format("0x%02X", value); + case 2: + return String.format("0x%04X", value); + case 4: + return String.format("0x%08X", value); + default: + return String.format("0x%X", value); + } + } + + //-------------------------- INNER CLASSES -------------------------- + + /** + * A helper class used to encapsulate the formatter function object. + */ + protected static class FormattedData + { + private final Long value; + private final T data; + private final BiFunction formatter; + + public FormattedData(long value, T data, BiFunction formatter) + { + this.value = Long.valueOf(value); + this.data = data; + this.formatter = formatter; + } + + public Long getValue() { return value; } + + public T getData() { return data; } + + public BiFunction getFormatter() { return formatter; } + + @Override + public String toString() + { + if (formatter != null) { + return formatter.apply(value, data); + } else if (data != null) { + return data.toString(); + } else { + return "(null)"; + } + } + } +} diff --git a/src/org/infinity/datatype/AnimateBitmap.java b/src/org/infinity/datatype/AnimateBitmap.java index 93d48e38f..91abf3ed0 100644 --- a/src/org/infinity/datatype/AnimateBitmap.java +++ b/src/org/infinity/datatype/AnimateBitmap.java @@ -7,8 +7,10 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.nio.ByteBuffer; +import java.util.function.BiFunction; import javax.swing.JButton; +import javax.swing.JComponent; import org.infinity.gui.ViewFrame; import org.infinity.icon.Icons; @@ -24,13 +26,28 @@ */ public class AnimateBitmap extends IdsBitmap implements ActionListener { + private final BiFunction formatterAnimateBitmap = (value, item) -> { + String number; + if (isShowAsHex()) { + number = String.format("0x%04X", value); + } else { + number = value.toString(); + } + if (item != null) { + return item.getSymbol() + " - " + number; + } else { + return "UNKNOWN - " + number; + } + }; + private final JButton showIni; private IdsMap idsMap; private boolean useIni; public AnimateBitmap(ByteBuffer buffer, int offset, int length, String name) { - super(buffer, offset, length, name, "ANIMATE.IDS"); + super(buffer, offset, length, name, "ANIMATE.IDS", true, true, false); + setFormatter(formatterAnimateBitmap); if (Profile.isEnhancedEdition() || ResourceFactory.resourceExists("ANISND.IDS")) { showIni = new JButton("View/Edit", Icons.getIcon(Icons.ICON_ZOOM_16)); @@ -52,27 +69,45 @@ public AnimateBitmap(ByteBuffer buffer, int offset, int length, String name) } } + //--------------------- Begin Interface Editable --------------------- + + @Override + public JComponent edit(ActionListener container) + { + if (getDataOf(getLongValue()) == null) { + putItem(getLongValue(), new IdsMapEntry(getLongValue(), "UNKNOWN")); + } + + return super.edit(container); + } + + //--------------------- End Interface Editable --------------------- + + //--------------------- Begin Interface ActionListener --------------------- + @Override public void actionPerformed(ActionEvent e) { if (e.getSource() == showIni) { - final Long value = getCurrentValue(); + final Long value = getSelectedValue(); String animRes = value == null ? null : getAnimResource(value.longValue()); if (animRes != null) { ResourceEntry entry = ResourceFactory.getResourceEntry(animRes); if (entry != null) { - new ViewFrame(getListPanel().getTopLevelAncestor(), ResourceFactory.getResource(entry)); + new ViewFrame(getUiControl().getTopLevelAncestor(), ResourceFactory.getResource(entry)); } } } } + //--------------------- End Interface ActionListener --------------------- + @Override protected void listItemChanged() { if (showIni != null) { boolean b = false; - final Long value = getCurrentValue(); + final Long value = getSelectedValue(); if (value != null) { b = (getAnimResource(value) != null); } diff --git a/src/org/infinity/datatype/Bestiary.java b/src/org/infinity/datatype/Bestiary.java index c2bf27db2..b2d12993d 100644 --- a/src/org/infinity/datatype/Bestiary.java +++ b/src/org/infinity/datatype/Bestiary.java @@ -100,7 +100,6 @@ public final class Bestiary extends Datatype implements Editable, TableModel /** Color, used to highligth background of party members. */ private static final Color PARTY_COLOR = Color.CYAN; - // /** * List of used entries in a bestiary field. Other bytes in the {@link #known} * field are unused. @@ -109,7 +108,6 @@ public final class Bestiary extends Datatype implements Editable, TableModel * hardcoded in the game. */ private final List creatures; - // /** * If byte contains non zero value, then player knowns creature from {@link #creatures} * array. Only first 88 bytes is used ({@code creatures.length}). @@ -218,7 +216,7 @@ public KillVariable findVariable(GamResource game) if (killVarName == null) return null; final SectionOffset offset = game.getSectionOffset(KillVariable.class); - KillVariable var = (KillVariable)game.getAttribute(offset.getValue(), KillVariable.class, false); + KillVariable var = game.getAttribute(offset.getValue(), KillVariable.class, false); if (var == null) return null; final List fields = game.getFields(); @@ -409,7 +407,6 @@ public Component getTableCellRendererComponent(JTable table, Object value, setPreferredSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE)); } - // @Override public void valueChanged(ListSelectionEvent e) { @@ -431,17 +428,13 @@ public void valueChanged(ListSelectionEvent e) revalidate(); } } - // - // @Override public void tableChanged(TableModelEvent e) { container.actionPerformed(new ActionEvent(e.getSource(), ACTION_PERFORMED, UPDATE_VALUE)); } - // - // @Override public void mouseClicked(MouseEvent e) { @@ -466,9 +459,7 @@ public void mouseEntered(MouseEvent e) {} @Override public void mouseExited(MouseEvent e) {} - // - // @Override public void actionPerformed(ActionEvent e) { @@ -516,7 +507,6 @@ public void actionPerformed(ActionEvent e) } } } - // /** * Setup context menu and show it for table. @@ -577,7 +567,6 @@ public Bestiary(ByteBuffer buffer, int offset, String name) creatures = readCreatures(); } - // @Override public JComponent edit(ActionListener container) { return new Viewer(container); } @@ -592,17 +581,13 @@ public boolean updateValue(AbstractStruct struct) return true; } - // - // @Override public void write(OutputStream os) throws IOException { os.write(known); } - // - // @Override public int read(ByteBuffer buffer, int offset) { @@ -610,9 +595,7 @@ public int read(ByteBuffer buffer, int offset) buffer.get(known); return offset + known.length; } - // - // @Override public int getRowCount() { return creatures.size(); } @Override @@ -679,7 +662,6 @@ public void removeTableModelListener(TableModelListener l) { listenerList.remove(TableModelListener.class, l); } - // /** * Returns {@code true} if specified creature is included in the bestiary. @@ -756,6 +738,26 @@ public String toString() return first ? "No known creatures" : sb.toString(); } + @Override + public int hashCode() + { + int hash = super.hashCode(); + hash = 31 * hash + ((known == null) ? 0 : known.hashCode()); + return hash; + } + + @Override + public boolean equals(Object o) + { + if (!super.equals(o) || !(o instanceof Bestiary)) { + return false; + } + Bestiary other = (Bestiary)o; + boolean retVal = (known == null && other.known == null) || + (known != null && known.equals(other.known)); + return retVal; + } + /** * Returns resource entry from which creatures will be read. * @return Pointer to resource with creatures in the bestiary. Never {@code null} diff --git a/src/org/infinity/datatype/Bitmap.java b/src/org/infinity/datatype/Bitmap.java index f4f35d240..5743e954a 100644 --- a/src/org/infinity/datatype/Bitmap.java +++ b/src/org/infinity/datatype/Bitmap.java @@ -4,28 +4,15 @@ package org.infinity.datatype; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; import java.beans.PropertyChangeEvent; -import java.io.IOException; -import java.io.OutputStream; import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.List; +import java.util.TreeMap; +import java.util.function.BiFunction; -import javax.swing.JButton; import javax.swing.JComponent; -import javax.swing.JPanel; -import org.infinity.gui.StructViewer; -import org.infinity.gui.TextListPanel; -import org.infinity.icon.Icons; import org.infinity.resource.AbstractStruct; -import org.infinity.util.Misc; /** * Field that represents an integer enumeration of some values. @@ -40,189 +27,61 @@ *
  • Value meaning: value of the enumeration
  • * */ -public class Bitmap extends Datatype implements Editable, IsNumeric +public class Bitmap extends AbstractBitmap { - private final String[] table; - - private TextListPanel list; - private int base; // numeric value of first item index - private int value; - - public Bitmap(ByteBuffer buffer, int offset, int length, String name, String[] table) - { - super(offset, length, name); - this.base = 0; - this.table = table; - read(buffer, offset); - } - - // - @Override - public JComponent edit(final ActionListener container) - { - base = 0; - int capacity = table.length; - if (value < 0) { - capacity += Math.abs(value); - base = value; - } else if (value >= table.length) { - capacity = value + 1; - } - final List values = new ArrayList<>(capacity); - for (int i = 0; i < capacity; i++) { - values.add(toString(base + i)); + private final BiFunction formatterBitmap = (value, item) -> { + String number; + if (isShowAsHex()) { + number = getHexValue(value.longValue()); + } else { + number = value.toString(); } - list = new TextListPanel<>(values, false); - list.addMouseListener(new MouseAdapter() - { - @Override - public void mouseClicked(MouseEvent event) - { - if (event.getClickCount() == 2) { - container.actionPerformed(new ActionEvent(this, 0, StructViewer.UPDATE_VALUE)); - } - } - }); - if (value >= base && value < list.getModel().getSize() + base) { - int index = 0; - while (!list.getModel().getElementAt(index).equals(toString(value))) { - index++; - } - list.setSelectedIndex(index); + if (item != null) { + return item + " (" + number + ")"; + } else { + return "Unknown (" + number + ")"; } + }; - JButton bUpdate = new JButton("Update value", Icons.getIcon(Icons.ICON_REFRESH_16)); - bUpdate.addActionListener(container); - bUpdate.setActionCommand(StructViewer.UPDATE_VALUE); - - GridBagLayout gbl = new GridBagLayout(); - GridBagConstraints gbc = new GridBagConstraints(); - JPanel panel = new JPanel(gbl); - - gbc.weightx = 1.0; - gbc.weighty = 1.0; - gbc.fill = GridBagConstraints.BOTH; - gbl.setConstraints(list, gbc); - panel.add(list); - - gbc.weightx = 0.0; - gbc.fill = GridBagConstraints.NONE; - gbc.insets.left = 6; - gbl.setConstraints(bUpdate, gbc); - panel.add(bUpdate); - - panel.setMinimumSize(Misc.getScaledDimension(DIM_MEDIUM)); - return panel; - } - - @Override - public void select() - { - list.ensureIndexIsVisible(list.getSelectedIndex()); - } - - @Override - public boolean updateValue(AbstractStruct struct) - { - // updating value - setValue(calcValue()); - - // notifying listeners - fireValueUpdated(new UpdateEvent(this, struct)); - - return true; - } - // - - // - @Override - public void write(OutputStream os) throws IOException + public Bitmap(ByteBuffer buffer, int offset, int length, String name, String[] table) { - writeInt(os, value); + this(buffer, offset, length, name, table, true, false); } - // - // - @Override - public int read(ByteBuffer buffer, int offset) + public Bitmap(ByteBuffer buffer, int offset, int length, String name, String[] table, boolean signed) { - buffer.position(offset); - switch (getSize()) { - case 1: - value = buffer.get(); - break; - case 2: - value = buffer.getShort(); - break; - case 4: - value = buffer.getInt(); - break; - default: - throw new IllegalArgumentException(); - } - - return offset + getSize(); + this(buffer, offset, length, name, table, true, false); } - // - // - @Override - public long getLongValue() + public Bitmap(ByteBuffer buffer, int offset, int length, String name, String[] table, boolean signed, boolean showAsHex) { - return (long)value & 0xffffffffL; + super(buffer, offset, length, name, createMap(table), null, signed); + setShowAsHex(showAsHex); + setFormatter(formatterBitmap); } - @Override - public int getValue() - { - return value; - } - // + //--------------------- Begin Interface Editable --------------------- @Override - public String toString() + public JComponent edit(ActionListener container) { - return toString(value); - } - - /** Returns the unformatted description of the specified value. */ - protected String getString(int nr) - { - if (nr >= 0 && nr < table.length) { - return table[nr]; + if (getDataOf(getLongValue()) == null) { + putItem(getLongValue(), "Unknown"); } - return null; - } - /** Returns a formatted description of the specified value. */ - private String toString(int nr) - { - if (nr < 0 || nr >= table.length) { - return "Unknown (" + nr + ')'; - } else if (table[nr] == null || table[nr].isEmpty()) { - return "Unknown (" + nr + ')'; - } else { - return table[nr] + " (" + nr + ')'; - } + return super.edit(container); } - private void setValue(int newValue) - { - final int oldValue = value; - value = newValue; - if (oldValue != newValue) { - firePropertyChange(oldValue, newValue); - } - } + //--------------------- End Interface Editable --------------------- - private int calcValue() + private static TreeMap createMap(String[] symbols) { - final String svalue = list.getSelectedValue(); - int val = base; - //FIXME: Ineffective code - while (!svalue.equals(toString(val))) { - val++; + TreeMap retVal = new TreeMap<>(); + for (int i = 0; i < symbols.length; i++) { + String symbol = (symbols[i] != null) ? symbols[i] : "(Unlabeled)"; + retVal.put(Long.valueOf(i), symbol); } - return val; + + return retVal; } } diff --git a/src/org/infinity/datatype/ColorPicker.java b/src/org/infinity/datatype/ColorPicker.java index cab7150f2..a0e234439 100644 --- a/src/org/infinity/datatype/ColorPicker.java +++ b/src/org/infinity/datatype/ColorPicker.java @@ -441,6 +441,25 @@ public String toString() format.getRed(value), format.getGreen(value), format.getBlue(value)); } + @Override + public int hashCode() + { + int hash = super.hashCode(); + hash = 31 * hash + Integer.hashCode(value); + return hash; + } + + @Override + public boolean equals(Object o) + { + if (!super.equals(o) || !(o instanceof ColorPicker)) { + return false; + } + ColorPicker other = (ColorPicker)o; + boolean retVal = (value == other.value); + return retVal; + } + //--------------------- Begin Interface IsNumeric --------------------- @Override diff --git a/src/org/infinity/datatype/ColorValue.java b/src/org/infinity/datatype/ColorValue.java index 4efdabae2..6c17a94fd 100644 --- a/src/org/infinity/datatype/ColorValue.java +++ b/src/org/infinity/datatype/ColorValue.java @@ -27,8 +27,9 @@ import java.io.OutputStream; import java.nio.ByteBuffer; import java.util.ArrayList; -import java.util.HashSet; +import java.util.HashMap; import java.util.List; +import java.util.Objects; import javax.swing.AbstractListModel; import javax.swing.BorderFactory; @@ -78,26 +79,50 @@ public class ColorValue extends Datatype implements Editable, IsNumeric private static final int DEFAULT_COLOR_WIDTH = 16; private static final int DEFAULT_COLOR_HEIGHT = 24; + private final HashMap randomColors = new HashMap<>(); + private int number; private JList colorList; private ResourceEntry colorEntry; // the source of color ranges private IdsMap colorMap; // provides an optional symbolic color name - public ColorValue(ByteBuffer buffer, int offset, int length, String name) + public ColorValue(ByteBuffer buffer, int offset, int length, String name, boolean allowRandom) { - this(buffer, offset, length, name, null); + this(buffer, offset, length, name, allowRandom, null); } - public ColorValue(ByteBuffer buffer, int offset, int length, String name, String bmpFile) + public ColorValue(ByteBuffer buffer, int offset, int length, String name, boolean allowRandom, String bmpFile) { super(offset, length, name); + init(bmpFile, allowRandom); + read(buffer, offset); + } + + private void init(String bmpFile, boolean allowRandom) + { if (bmpFile != null && ResourceFactory.resourceExists(bmpFile)) { this.colorEntry = ResourceFactory.getResourceEntry(bmpFile); } if (ResourceFactory.resourceExists("CLOWNCLR.IDS")) { this.colorMap = IdsMapCache.get("CLOWNCLR.IDS"); } - read(buffer, offset); + + ResourceEntry randomEntry = null; + if (allowRandom && ResourceFactory.resourceExists("RANDCOLR.2DA")) { + randomEntry = ResourceFactory.getResourceEntry("RANDCOLR.2DA"); + } + + // collecting valid random color indices + if (randomEntry != null) { + Table2da table = Table2daCache.get(randomEntry); + for (int col = 1, count = table.getColCount(); col < count; col++) { + int index = Misc.toNumber(table.get(0, col), -1); + String name = Misc.prettifySymbol(table.getHeader(col)); + if (index >= 0 && index < 256) { + randomColors.put(index, name); + } + } + } } //--------------------- Begin Interface Editable --------------------- @@ -106,8 +131,9 @@ public ColorValue(ByteBuffer buffer, int offset, int length, String name, String public JComponent edit(ActionListener container) { int defaultColorWidth = (colorEntry == null) ? DEFAULT_COLOR_WIDTH : 0; - colorList = new JList<>(new ColorListModel(defaultColorWidth, DEFAULT_COLOR_HEIGHT, colorEntry)); - colorList.setCellRenderer(new ColorCellRenderer(colorMap)); + ColorListModel colorModel = new ColorListModel(this, defaultColorWidth, DEFAULT_COLOR_HEIGHT); + colorList = new JList<>(colorModel); + colorList.setCellRenderer(new ColorCellRenderer(this)); colorList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); colorList.setBorder(BorderFactory.createLineBorder(Color.GRAY)); colorList.addMouseListener(new MouseAdapter() { @@ -167,10 +193,13 @@ public boolean updateValue(AbstractStruct struct) { if (colorList.getSelectedIndex() >= 0) { if (number != colorList.getSelectedIndex()) { + long oldValue = getLongValue(); setValue(colorList.getSelectedIndex()); // notifying listeners - fireValueUpdated(new UpdateEvent(this, struct)); + if (getLongValue() != oldValue) { + fireValueUpdated(new UpdateEvent(this, struct)); + } } return true; @@ -234,13 +263,45 @@ public int getValue() public String toString() { String retVal = "Color index " + Integer.toString(number); - String name = lookupColorName(colorMap, number, true); + String name = getColorName(number); if (name != null) { retVal += " (" + name + ")"; } return retVal; } + @Override + public int hashCode() + { + int hash = super.hashCode(); + hash = 31 * hash + Integer.hashCode(number); + return hash; + } + + @Override + public boolean equals(Object o) + { + if (!super.equals(o) || !(o instanceof ColorValue)) { + return false; + } + ColorValue other = (ColorValue)o; + boolean retVal = (number == other.number); + return retVal; + } + + /** + * Returns the name associated with the specified color entry. + * Returns {@code null} if no name is available. + */ + public String getColorName(int index) + { + String retVal = randomColors.get(Integer.valueOf(index)); + if (retVal == null) { + retVal = lookupColorName(colorMap, index, true); + } + return retVal; + } + /** * Returns a symbolic color name based on the specified IDS lookup. * Returns {@code null} if no lookup table is available. @@ -274,12 +335,12 @@ private static final class ColorCellRenderer extends DefaultListCellRenderer { private static final Border DEFAULT_NO_FOCUS_BORDER = new EmptyBorder(1, 1, 1, 1); - private final IdsMap colorMap; + private final ColorValue colorValue; - public ColorCellRenderer(IdsMap colorMap) + public ColorCellRenderer(ColorValue cv) { super(); - this.colorMap = colorMap; + this.colorValue = Objects.requireNonNull(cv); setVerticalAlignment(SwingConstants.CENTER); setHorizontalTextPosition(SwingConstants.RIGHT); setVerticalTextPosition(SwingConstants.CENTER); @@ -299,7 +360,7 @@ public Component getListCellRendererComponent(JList list, Object value, int i } String label = "Index " + index; - String name = lookupColorName(colorMap, index, true); + String name = colorValue.getColorName(index); if (name != null) { label += " (" + name + ")"; } @@ -335,12 +396,13 @@ public Component getListCellRendererComponent(JList list, Object value, int i private static final class ColorListModel extends AbstractListModel { - private final HashSet randomColors = new HashSet<>(); private final List colors = new ArrayList<>(256); + private final ColorValue colorValue; - public ColorListModel(int defaultWidth, int defaultHeight, ResourceEntry colorsEntry) + public ColorListModel(ColorValue cv, int defaultWidth, int defaultHeight) { - initEntries(defaultWidth, defaultHeight, colorsEntry); + this.colorValue = Objects.requireNonNull(cv); + initEntries(defaultWidth, defaultHeight); } @Override @@ -358,42 +420,37 @@ public Image getElementAt(int index) return null; } - private void initEntries(int defaultWidth, int defaultHeight, ResourceEntry colorsEntry) + private void initEntries(int defaultWidth, int defaultHeight) { - if (colorsEntry == null) { + if (colorValue.colorEntry == null) { if (ResourceFactory.resourceExists("RANGES12.BMP")) { - colorsEntry = ResourceFactory.getResourceEntry("RANGES12.BMP"); + colorValue.colorEntry = ResourceFactory.getResourceEntry("RANGES12.BMP"); } else if (ResourceFactory.resourceExists("MPALETTE.BMP")) { - colorsEntry = ResourceFactory.getResourceEntry("MPALETTE.BMP"); + colorValue.colorEntry = ResourceFactory.getResourceEntry("MPALETTE.BMP"); } } - ResourceEntry randomEntry = null; - if (ResourceFactory.resourceExists("RANDCOLR.2DA")) { - randomEntry = ResourceFactory.getResourceEntry("RANDCOLR.2DA"); - } - // adding color gradients - if (colorsEntry != null) { + // scanning range of colors + int maxValue = 255; // default size + if (colorValue.colorEntry != null) { BufferedImage image = null; try { - image = new GraphicsResource(colorsEntry).getImage(); + image = new GraphicsResource(colorValue.colorEntry).getImage(); + maxValue = Math.max(maxValue, image.getHeight() - 1); if (defaultWidth <= 0) { // auto-calculate color width defaultWidth = 192 / image.getWidth(); } - for (int y = 0; y < image.getHeight(); y++) { - BufferedImage range = new BufferedImage(image.getWidth() * defaultWidth, defaultHeight, image.getType()); - Graphics2D g = range.createGraphics(); - try { - g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); -// g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); - g.drawImage(image, 0, 0, range.getWidth(), range.getHeight(), 0, y, image.getWidth(), y+1, null); - g.setColor(Color.BLACK); - g.setStroke(new BasicStroke(1.0f)); - g.drawRect(0, 0, range.getWidth() - 1, range.getHeight() - 1); - } finally { - g.dispose(); + + for (int idx = 0; idx <= maxValue; idx++) { + BufferedImage range; + if (!colorValue.randomColors.containsKey(Integer.valueOf(idx)) && idx < image.getHeight()) { + // fixed color + range = getFixedColor(image, idx, defaultWidth, defaultHeight); + } else { + // random color or invalid entry + range = getVirtualColor(idx, defaultWidth, defaultHeight); } colors.add(range); } @@ -401,52 +458,58 @@ private void initEntries(int defaultWidth, int defaultHeight, ResourceEntry colo e.printStackTrace(); } } + } - // collecting valid random color indices - int maxValue = colors.size() - 1; - if (randomEntry != null) { - Table2da table = Table2daCache.get(randomEntry); - for (int col = 1; col < table.getColCount(); col++) { - String s = table.get(0, col); - if (s != null) { - try { - Integer v = Integer.valueOf(Integer.parseInt(s)); - if (v >= colors.size() && v < 256 && !randomColors.contains(v)) { - randomColors.add(v); - maxValue = Math.max(maxValue, v.intValue()); - } - } catch (NumberFormatException e) { - } - } - } - } + // Returns an image derived from the specified color range bitmap + private BufferedImage getFixedColor(BufferedImage colorRanges, int index, int width, int height) + { + BufferedImage retVal = null; - // adding random/invalid color placeholder - maxValue = Math.max(maxValue, 255) + 1; - Color invalidColor = new Color(0xe0e0e0); - for (int i = colors.size(); i < maxValue; i++) { - boolean isRandom = randomColors.contains(Integer.valueOf(i)); - BufferedImage range = new BufferedImage(12 * defaultWidth, defaultHeight, BufferedImage.TYPE_INT_RGB); - Graphics2D g = range.createGraphics(); + if (colorRanges != null && index >= 0 && index < colorRanges.getHeight()) { + retVal = new BufferedImage(colorRanges.getWidth() * width, height, colorRanges.getType()); + Graphics2D g = retVal.createGraphics(); try { - g.setColor(isRandom ? Color.LIGHT_GRAY : invalidColor); - g.fillRect(0, 0, range.getWidth(), range.getHeight()); - g.setFont(new JLabel().getFont()); + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); + g.drawImage(colorRanges, 0, 0, retVal.getWidth(), retVal.getHeight(), 0, index, colorRanges.getWidth(), index+1, null); g.setColor(Color.BLACK); g.setStroke(new BasicStroke(1.0f)); - g.drawRect(0, 0, range.getWidth() - 1, range.getHeight() - 1); - g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_GASP); - String msg = isRandom ? "(Random)" : "(Invalid)"; - FontMetrics fm = g.getFontMetrics(); - Rectangle2D rect = fm.getStringBounds(msg, g); - g.drawString(msg, - (float)(range.getWidth() - rect.getWidth()) / 2.0f, - (float)(range.getHeight() - rect.getY()) / 2.0f); + g.drawRect(0, 0, retVal.getWidth() - 1, retVal.getHeight() - 1); } finally { g.dispose(); } - colors.add(range); } + + return retVal; + } + + // Returns an image describing a random color or invalid color entry + private BufferedImage getVirtualColor(int index, int width, int height) + { + BufferedImage retVal = null; + + Color invalidColor = new Color(0xe0e0e0); + boolean isRandom = colorValue.randomColors.containsKey(Integer.valueOf(index)); + retVal = new BufferedImage(12 * width, height, BufferedImage.TYPE_INT_RGB); + Graphics2D g = retVal.createGraphics(); + try { + g.setColor(isRandom ? Color.LIGHT_GRAY : invalidColor); + g.fillRect(0, 0, retVal.getWidth(), retVal.getHeight()); + g.setFont(new JLabel().getFont()); + g.setColor(Color.BLACK); + g.setStroke(new BasicStroke(1.0f)); + g.drawRect(0, 0, retVal.getWidth() - 1, retVal.getHeight() - 1); + g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_GASP); + String msg = isRandom ? "(Random)" : "(Invalid)"; + FontMetrics fm = g.getFontMetrics(); + Rectangle2D rect = fm.getStringBounds(msg, g); + g.drawString(msg, + (float)(retVal.getWidth() - rect.getWidth()) / 2.0f, + (float)(retVal.getHeight() - rect.getY()) / 2.0f); + } finally { + g.dispose(); + } + + return retVal; } } } diff --git a/src/org/infinity/datatype/Datatype.java b/src/org/infinity/datatype/Datatype.java index dd23ddd62..cbd2cf62e 100644 --- a/src/org/infinity/datatype/Datatype.java +++ b/src/org/infinity/datatype/Datatype.java @@ -64,6 +64,32 @@ public int compareTo(StructEntry o) // --------------------- End Interface Comparable --------------------- + @Override + public int hashCode() + { + int hash = 7; + hash = 31 * hash + length; + hash = 31 * hash + ((name == null) ? 0 : name.hashCode()); + hash = 31 * hash + offset; + return hash; + } + + @Override + public boolean equals(Object o) + { + if (o == this) { + return true; + } + if (!(o instanceof Datatype)) { + return false; + } + Datatype other = (Datatype)o; + boolean retVal = (length == other.length); + retVal &= (name == null && other.name == null) || + (name != null && name.equals(other.name)); + retVal &= (offset == other.offset); + return retVal; + } // --------------------- Begin Interface StructEntry --------------------- @@ -216,8 +242,8 @@ protected void fireValueUpdated(UpdateEvent event) */ protected void firePropertyChange(Object oldValue, Object newValue) { - if (parent instanceof AbstractStruct) { - ((AbstractStruct)parent).propertyChange(new PropertyChangeEvent(parent, getName(), oldValue, newValue)); + if (parent != null) { + parent.propertyChange(new PropertyChangeEvent(parent, getName(), oldValue, newValue)); } } diff --git a/src/org/infinity/datatype/DecNumber.java b/src/org/infinity/datatype/DecNumber.java index 742ed46fa..9e78a2984 100644 --- a/src/org/infinity/datatype/DecNumber.java +++ b/src/org/infinity/datatype/DecNumber.java @@ -45,11 +45,18 @@ protected DecNumber(ByteBuffer buffer, int offset, int length, String name, bool public boolean update(Object value) { try { + long oldVal = getLongValue(); setValue(parseNumber(value, getSize(), signed, true)); + + // notifying listeners + if (getLongValue() != oldVal) { + fireValueUpdated(new UpdateEvent(this, getParent())); + } return true; } catch (Exception e) { e.printStackTrace(); } + return false; } @@ -139,22 +146,55 @@ public String toString() return Long.toString(number); } - /** Attempts to parse the specified string into a decimal or, optionally, hexadecimal number. */ - static long parseNumber(Object value, int size, boolean negativeAllowed, boolean hexAllowed) throws Exception + @Override + public int hashCode() + { + int hash = super.hashCode(); + hash = 31 * hash + Long.hashCode(number); + return hash; + } + + @Override + public boolean equals(Object o) + { + if (!super.equals(o) || !(o instanceof DecNumber)) { + return false; + } + DecNumber other = (DecNumber)o; + boolean retVal = (number == other.number); + return retVal; + } + + /** Attempts to parse the specified object into a decimal or, optionally, hexadecimal number. */ + public static long parseNumber(Object value, int size, boolean negativeAllowed, boolean hexAllowed) throws Exception { if (value == null) { throw new NullPointerException(); } - String s = value.toString().trim().toLowerCase(Locale.ENGLISH); - int radix = 10; - if (hexAllowed && s.startsWith("0x")) { - s = s.substring(2); - radix = 16; - } else if (hexAllowed && s.endsWith("h")) { - s = s.substring(0, s.length() - 1).trim(); - radix = 16; + + long newNumber; + if (value instanceof IsNumeric) { + newNumber = ((IsNumeric)value).getLongValue(); + } else { + String s; + if (value instanceof IsTextual) { + s = ((IsTextual)value).getText(); + } else { + s = (value != null) ? value.toString() : ""; + } + s = s.toLowerCase(Locale.ENGLISH); + + int radix = 10; + if (hexAllowed && s.startsWith("0x")) { + s = s.substring(2); + radix = 16; + } else if (hexAllowed && s.endsWith("h")) { + s = s.substring(0, s.length() - 1).trim(); + radix = 16; + } + newNumber = Long.parseLong(s, radix); } - long newNumber = Long.parseLong(s, radix); + long discard = negativeAllowed ? 1L : 0L; long maxNum = (1L << ((long)size*8L - discard)) - 1L; long minNum = negativeAllowed ? -(maxNum+1L) : 0; diff --git a/src/org/infinity/datatype/Flag.java b/src/org/infinity/datatype/Flag.java index 5173dd02f..2e75aa112 100644 --- a/src/org/infinity/datatype/Flag.java +++ b/src/org/infinity/datatype/Flag.java @@ -7,7 +7,8 @@ import java.awt.BorderLayout; import java.awt.Color; import java.awt.FlowLayout; -import java.awt.GridLayout; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -23,6 +24,7 @@ import javax.swing.UIManager; import org.infinity.gui.StructViewer; +import org.infinity.gui.ViewerUtil; import org.infinity.resource.AbstractStruct; import org.infinity.util.Misc; @@ -72,7 +74,6 @@ public Flag(ByteBuffer buffer, int offset, int length, String name, String[] sta setFlagDescriptions(length, stable, 1); } - // @Override public void actionPerformed(ActionEvent event) { @@ -86,9 +87,7 @@ else if (event.getSource() == bNone) { } container.actionPerformed(new ActionEvent(this, 0, StructViewer.UPDATE_VALUE)); } - // - // @Override public JComponent edit(ActionListener container) { @@ -122,21 +121,22 @@ public JComponent edit(ActionListener container) bPanel.add(bNone); bPanel.add(new JLabel("None = " + nodesc)); - JPanel boxPanel = new JPanel(new GridLayout(0, 4)); - int rows = checkBoxes.length >> 2; - if (rows << 2 != checkBoxes.length) { - for (int i = 0; i < checkBoxes.length; i++) { - boxPanel.add(checkBoxes[i]); - checkBoxes[i].setSelected(isFlagSet(i)); + // spreading flags over columns with 8 rows each + JPanel boxPanel = new JPanel(new GridBagLayout()); + int cols = checkBoxes.length / 8; + GridBagConstraints c = new GridBagConstraints(); + for (int col = 0; col < cols; col++) { + JPanel colPanel = new JPanel(new GridBagLayout()); + for (int row = 0; row < 8; row++) { + int idx = (col * 8) + row; + c = ViewerUtil.setGBC(c, 0, row, 1, 1, 1.0, 1.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); + colPanel.add(checkBoxes[idx], c); + checkBoxes[idx].setSelected(isFlagSet(idx)); } - } - else { - for (int i = 0; i < rows; i++) - for (int j = 0; j < 4; j++) { - int index = i + j * rows; - boxPanel.add(checkBoxes[index]); - checkBoxes[index].setSelected(isFlagSet(index)); - } + c = ViewerUtil.setGBC(c, col, 0, 1, 1, 1.0, 1.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.BOTH, new Insets(0, 8, 0, 8), 0, 0); + boxPanel.add(colPanel, c); } JPanel panel = new JPanel(new BorderLayout()); @@ -156,23 +156,25 @@ public void select() @Override public boolean updateValue(AbstractStruct struct) { + long oldValue = getLongValue(); + // updating value setValue(calcValue()); + // notifying listeners - fireValueUpdated(new UpdateEvent(this, struct)); + if (getLongValue() != oldValue) { + fireValueUpdated(new UpdateEvent(this, struct)); + } return true; } - // @Override public void write(OutputStream os) throws IOException { writeLong(os, value); } - // - // @Override public int read(ByteBuffer buffer, int offset) { @@ -193,8 +195,6 @@ public int read(ByteBuffer buffer, int offset) return offset + getSize(); } - // - // @Override public String toString() @@ -214,6 +214,25 @@ public String toString() return sb.toString(); } + @Override + public int hashCode() + { + int hash = super.hashCode(); + hash = 31 * hash + Long.hashCode(value); + return hash; + } + + @Override + public boolean equals(Object o) + { + if (!super.equals(o) || !(o instanceof Flag)) { + return false; + } + Flag other = (Flag)o; + boolean retVal = (value == other.value); + return retVal; + } + /** * Returns label of flag {@code i} or {@code null}, if such flag does not exist. * @@ -231,7 +250,6 @@ public boolean isFlagSet(int i) return (value & bitnr) == bitnr; } - // @Override public long getLongValue() { @@ -243,7 +261,6 @@ public int getValue() { return (int)value; } - // public void setValue(long newValue) { diff --git a/src/org/infinity/datatype/FloatNumber.java b/src/org/infinity/datatype/FloatNumber.java index 2d0b799e1..6b79aec96 100644 --- a/src/org/infinity/datatype/FloatNumber.java +++ b/src/org/infinity/datatype/FloatNumber.java @@ -44,7 +44,14 @@ public boolean update(Object value) if (getSize() == 4) { newValue = Double.valueOf(newValue).floatValue(); } + double oldValue = getValue(); setValue(newValue); + + // notifying listeners + if (getValue() != oldValue) { + fireValueUpdated(new UpdateEvent(this, getParent())); + } + return true; } catch (NumberFormatException e) { e.printStackTrace(); @@ -89,6 +96,25 @@ public String toString() return Double.toString(value); } + @Override + public int hashCode() + { + int hash = super.hashCode(); + hash = 31 * hash + Double.hashCode(value); + return hash; + } + + @Override + public boolean equals(Object o) + { + if (!super.equals(o) || !(o instanceof FloatNumber)) { + return false; + } + FloatNumber other = (FloatNumber)o; + boolean retVal = (value == other.value); + return retVal; + } + public double getValue() { return value; diff --git a/src/org/infinity/datatype/HashBitmap.java b/src/org/infinity/datatype/HashBitmap.java index 86ce1165b..040af18f2 100644 --- a/src/org/infinity/datatype/HashBitmap.java +++ b/src/org/infinity/datatype/HashBitmap.java @@ -4,32 +4,12 @@ package org.infinity.datatype; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.io.IOException; -import java.io.OutputStream; +import java.beans.PropertyChangeEvent; import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; +import java.util.TreeMap; +import java.util.function.BiFunction; -import javax.swing.JButton; -import javax.swing.JComponent; -import javax.swing.JPanel; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; - -import org.infinity.gui.StructViewer; -import org.infinity.gui.TextListPanel; -import org.infinity.icon.Icons; import org.infinity.resource.AbstractStruct; -import org.infinity.util.LongIntegerHashMap; -import org.infinity.util.Misc; -import org.infinity.util.ObjectString; /** * Field that represents an integer enumeration of some values. @@ -44,272 +24,45 @@ *
  • Value meaning: numerical value of this field
  • * */ -public class HashBitmap extends Datatype implements Editable, IsNumeric//TODO: try to unify with Bitmap +public class HashBitmap extends AbstractBitmap { - private final LongIntegerHashMap idsmap; - private final List buttonList; - private final JButton bUpdate; - private final boolean sortByName; - private TextListPanel list; - private long value; - - public HashBitmap(ByteBuffer buffer, int offset, int length, String name, - LongIntegerHashMap idsmap) - { - this(buffer, offset, length, name, idsmap, true); - } - - public HashBitmap(ByteBuffer buffer, int offset, int length, String name, - LongIntegerHashMap idsmap, boolean sortByName) - { - super(offset, length, name); - this.idsmap = normalizeHashMap(idsmap); - this.sortByName = sortByName; - this.bUpdate = new JButton("Update value", Icons.getIcon(Icons.ICON_REFRESH_16)); - this.buttonList = new ArrayList<>(); - this.buttonList.add(bUpdate); - - read(buffer, offset); - } - - // - @Override - public JComponent edit(final ActionListener container) - { - final List items = new ArrayList<>(idsmap.size()); - for (Object o : idsmap.values()) { - if (o != null) {//TODO: It seems that map never contains nulls and this check can be removed - items.add(o); - } - } - list = new TextListPanel<>(items, sortByName); - list.addMouseListener(new MouseAdapter() - { - @Override - public void mouseClicked(MouseEvent event) - { - if (event.getClickCount() == 2) - container.actionPerformed(new ActionEvent(this, 0, StructViewer.UPDATE_VALUE)); - } - }); - list.addListSelectionListener(new ListSelectionListener() { - @Override - public void valueChanged(ListSelectionEvent e) - { - if (!e.getValueIsAdjusting()) { - listItemChanged(); - } - } - }); - Object selected = idsmap.get(value); - if (selected != null) { - list.setSelectedValue(selected, true); - } - - bUpdate.addActionListener(container); - bUpdate.setActionCommand(StructViewer.UPDATE_VALUE); - - GridBagLayout gbl = new GridBagLayout(); - GridBagConstraints gbc = new GridBagConstraints(); - JPanel panel = new JPanel(gbl); - - gbc.weightx = 1.0; - gbc.weighty = 1.0; - gbc.gridheight = buttonList.size() + 2; - gbc.fill = GridBagConstraints.BOTH; - gbl.setConstraints(list, gbc); - panel.add(list); - - gbc.weightx = 0.0; - gbc.gridheight = 1; - gbc.gridx = 1; - gbc.gridy = 0; - gbc.insets.left = 6; - gbc.insets.top = 4; - gbc.insets.bottom = 4; - // dummy component to center list of buttons vertically - JPanel p = new JPanel(); - gbl.setConstraints(p, gbc); - panel.add(p); - ++gbc.gridy; - gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.weighty = 0.0; - for (final JButton btn: buttonList) { - gbl.setConstraints(btn, gbc); - panel.add(btn); - ++gbc.gridy; - } - // dummy component to center list of buttons vertically - gbc.weighty = 1.0; - gbc.fill = GridBagConstraints.BOTH; - p = new JPanel(); - gbl.setConstraints(p, gbc); - panel.add(p); - - panel.setMinimumSize(Misc.getScaledDimension(DIM_MEDIUM)); - return panel; - } - - @Override - public void select() - { - list.ensureIndexIsVisible(list.getSelectedIndex()); - } - - @Override - public boolean updateValue(AbstractStruct struct) - { - // updating value - Long number = getValueOfItem(list.getSelectedValue()); - if (number != null) { - setValue(number.longValue()); + private final BiFunction formatterHashBitmap = (value, item) -> { + String number; + if (isShowAsHex()) { + number = getHexValue(value.longValue()); } else { - return false; - } - - // notifying listeners - fireValueUpdated(new UpdateEvent(this, struct)); - - return true; - } - - // - @Override - public void write(OutputStream os) throws IOException - { - writeLong(os, value); - } - // - - // - @Override - public int read(ByteBuffer buffer, int offset) - { - buffer.position(offset); - switch (getSize()) { - case 1: - value = buffer.get() & 0xffL; - break; - case 2: - value = buffer.getShort() & 0xffffL; - break; - case 4: - value = buffer.getInt() & 0xffffffffL; - break; - default: - throw new IllegalArgumentException(); + number = value.toString(); } - - return offset + getSize(); - } - // - // - - @Override - public String toString() - { - final Object o = getValueOf(value); - return o == null ? "Unknown - " + value : o.toString(); - } - - // - @Override - public long getLongValue() - { - return value; - } - - @Override - public int getValue() - { - return (int)value; - } - // - - protected void setValue(long newValue) - { - final long oldValue = value; - this.value = newValue; - if (oldValue != newValue) { - firePropertyChange(oldValue, newValue); - } - } - - /** Called whenever the user selects a new list item. */ - protected void listItemChanged() - { - } - - /** - * Can be used to register one or more custom buttons to the bitmap control. - * Only effective if called before the UI control is created. - */ - protected void addButtons(JButton... buttons) - { - if (list == null) { - for (final JButton button: buttons) { - if (button != null) { - buttonList.add(button); - } - } + if (item != null) { + return item.toString() + " - " + number; + } else { + return "Unknown - " + number; } - } - - /** Returns the TextListPanel control used by this datatype. */ - public TextListPanel getListPanel() - { - return list; - } - - /** Returns the textual representation of the specified IDS value. */ - public Object getValueOf(long key) - { - return idsmap.get(Long.valueOf(key)); - } + }; - protected Long getCurrentValue() + public HashBitmap(ByteBuffer buffer, int offset, int length, String name, TreeMap idsmap) { - return getValueOfItem(list.getSelectedValue()); + this(buffer, offset, length, name, idsmap, true, false, false); } - /** Attempts to extract the IDS value from the specified list item. */ - private Long getValueOfItem(Object item) + public HashBitmap(ByteBuffer buffer, int offset, int length, String name, TreeMap idsmap, + boolean sortByName) { - Long retVal = null; - if (item != null) { - if (item instanceof ObjectString && ((ObjectString)item).getObject() instanceof Number) { - retVal = ((Number)((ObjectString)item).getObject()).longValue(); - } else { - int i = item.toString().lastIndexOf(" - ");//FIXME: Smell code - try { - retVal = Long.parseLong(item.toString().substring(i + 3)); - } catch (NumberFormatException e) { - retVal = null; - } - } - } - return retVal; + this(buffer, offset, length, name, idsmap, sortByName, false, false); } - protected LongIntegerHashMap getHashBitmap() + public HashBitmap(ByteBuffer buffer, int offset, int length, String name, TreeMap idsmap, + boolean sortByName, boolean signed) { - return idsmap; + this(buffer, offset, length, name, idsmap, sortByName, signed, false); } - private static LongIntegerHashMap normalizeHashMap(LongIntegerHashMap map) + public HashBitmap(ByteBuffer buffer, int offset, int length, String name, TreeMap idsmap, + boolean sortByName, boolean signed, boolean showAsHex) { - //TODO: The smelling code. It seems that there is a check on the fact that the map contains String's - if (map != null && !map.isEmpty() && map.firstEntry().getValue() instanceof String) { - final LongIntegerHashMap retVal = new LongIntegerHashMap<>(); - for (Map.Entry e : map.entrySet()) { - retVal.put(e.getKey(), new ObjectString(e.getValue().toString(), e.getKey(), - ObjectString.FMT_OBJECT_HYPHEN)); - } - return retVal; - } else if (map == null) { - return new LongIntegerHashMap(); - } else { - return map; - } + super(buffer, offset, length, name, idsmap, null, signed); + setSortByName(sortByName); + setShowAsHex(showAsHex); + setFormatter(formatterHashBitmap); } } diff --git a/src/org/infinity/datatype/IdsBitmap.java b/src/org/infinity/datatype/IdsBitmap.java index df980426d..6e281f64d 100644 --- a/src/org/infinity/datatype/IdsBitmap.java +++ b/src/org/infinity/datatype/IdsBitmap.java @@ -4,27 +4,66 @@ package org.infinity.datatype; +import java.awt.event.ActionListener; import java.nio.ByteBuffer; +import java.util.TreeMap; +import java.util.function.BiFunction; + +import javax.swing.JComponent; import org.infinity.util.IdsMap; import org.infinity.util.IdsMapCache; import org.infinity.util.IdsMapEntry; -import org.infinity.util.LongIntegerHashMap; -public class IdsBitmap extends HashBitmap +public class IdsBitmap extends AbstractBitmap { + private final BiFunction formatterIdsBitmap = (value, item) -> { + String number; + if (isShowAsHex()) { + number = getHexValue(value.longValue()); + } else { + number = value.toString(); + } + if (item != null) { + return item.getSymbol() + " - " + number; + } else { + return "Unknown - " + number; + } + }; + public IdsBitmap(ByteBuffer buffer, int offset, int length, String name, String resource) { - super(buffer, offset, length, name, createResourceList(resource), true); + this(buffer, offset, length, name, resource, true, false, false); + } + + public IdsBitmap(ByteBuffer buffer, int offset, int length, String name, String resource, boolean sortByName) + { + this(buffer, offset, length, name, resource, sortByName, false, false); } + public IdsBitmap(ByteBuffer buffer, int offset, int length, String name, String resource, boolean sortByName, + boolean showAsHex, boolean signed) + { + super(buffer, offset, length, name, createResourceList(resource), null, signed); + setSortByName(sortByName); + setShowAsHex(showAsHex); + setFormatter(formatterIdsBitmap); + } + + //--------------------- Begin Interface Editable --------------------- + @Override - @SuppressWarnings("unchecked") - public LongIntegerHashMap getHashBitmap() + public JComponent edit(ActionListener container) { - return (LongIntegerHashMap)super.getHashBitmap(); + if (getDataOf(getLongValue()) == null) { + putItem(getLongValue(), new IdsMapEntry(getLongValue(), "Unknown")); + } + + return super.edit(container); } + //--------------------- End Interface Editable --------------------- + /** * Add to bitmap specified entry, id entry with such key not yet registered, * otherwise do nothing. @@ -33,15 +72,15 @@ public LongIntegerHashMap getHashBitmap() */ public void addIdsMapEntry(IdsMapEntry entry) { - getHashBitmap().putIfAbsent(entry.getID(), entry); + getBitmap().putIfAbsent(entry.getID(), entry); } - private static LongIntegerHashMap createResourceList(String resource) + private static TreeMap createResourceList(String resource) { - LongIntegerHashMap retVal = null; + TreeMap retVal = null; IdsMap idsMap = IdsMapCache.get(resource); if (idsMap != null) { - retVal = new LongIntegerHashMap<>(); + retVal = new TreeMap<>(); for (final IdsMapEntry e: idsMap.getAllValues()) { final long id = e.getID(); retVal.put(id, new IdsMapEntry(id, e.getSymbol())); diff --git a/src/org/infinity/datatype/IdsTargetType.java b/src/org/infinity/datatype/IdsTargetType.java index 6db486e77..980c62bbe 100644 --- a/src/org/infinity/datatype/IdsTargetType.java +++ b/src/org/infinity/datatype/IdsTargetType.java @@ -130,7 +130,7 @@ public StructEntry createIdsValueFromType(ByteBuffer buffer, int offset) public StructEntry createIdsValueFromType(ByteBuffer buffer, int offset, int size, String name) { int value = getValue(); - String type = getString(value); + String type = getDataOf(value); if (type != null) { if (ResourceFactory.resourceExists(type)) { return new IdsBitmap(buffer, offset, size, createFieldName(name, index, DEFAULT_NAME_VALUE), type); @@ -155,7 +155,7 @@ public StructEntry createResourceFromType(ByteBuffer buffer, int offset) public StructEntry createResourceFromType(ByteBuffer buffer, int offset, String name) { int value = getValue(); - String type = getString(value); + String type = getDataOf(value); if (type != null) { if (value == 11 && !type.isEmpty() && diff --git a/src/org/infinity/datatype/InlineEditable.java b/src/org/infinity/datatype/InlineEditable.java index fe80d80d9..a508bec5d 100644 --- a/src/org/infinity/datatype/InlineEditable.java +++ b/src/org/infinity/datatype/InlineEditable.java @@ -4,10 +4,47 @@ package org.infinity.datatype; +import java.awt.Color; + +import javax.swing.JComponent; +import javax.swing.JTextField; +import javax.swing.UIManager; +import javax.swing.border.LineBorder; + +import org.infinity.gui.BrowserMenuBar; import org.infinity.resource.StructEntry; +import org.infinity.util.Misc; public interface InlineEditable extends StructEntry { + /** The background color used for table cells. */ + static final Color GRID_BACKGROUND = + (UIManager.getColor("Table.focusCellBackground") != null) ? UIManager.getColor("Table.focusCellBackground") : Color.WHITE; + /** The border color used for table cells. */ + static final Color GRID_BORDER = + (UIManager.getColor("Table.gridColor") != null) ? UIManager.getColor("Table.gridColor") : Color.BLACK; + + /** The default component used for the inline editor. */ + static final JTextField DEFAULT_EDITOR = new JTextField() {{ + setFont(Misc.getScaledFont(BrowserMenuBar.getInstance().getScriptFont())); + setBorder(new LineBorder(GRID_BORDER, 1)); + setBackground(GRID_BACKGROUND); + }}; + + /** Called when the specified editor value should be applied to the {@code InlineEditable} object. */ boolean update(Object value); + + /** + * Returns the editing component that is used for editing a value within the table cell. + * This method is called whenever the editor is activated. + *

    Note: For performance reasons the returned component should only be created once and reused afterwards. + */ + default JComponent getEditor() { return DEFAULT_EDITOR; } + + /** This method is called to return the edited data in a format supported by the {@code InlineEditable} object. */ + default Object getEditorValue() { return DEFAULT_EDITOR.getText(); } + + /** This method is called to initialize the editor with the specified data. */ + default void setEditorValue(Object data) { DEFAULT_EDITOR.setText((data != null) ? data.toString() : ""); } } diff --git a/src/org/infinity/datatype/IsNumeric.java b/src/org/infinity/datatype/IsNumeric.java index d50251f40..366d1d5e0 100644 --- a/src/org/infinity/datatype/IsNumeric.java +++ b/src/org/infinity/datatype/IsNumeric.java @@ -6,6 +6,9 @@ public interface IsNumeric { + /** Returns the current value as {@code long}. */ long getLongValue(); + + /** Returns the current value as {@code int}. */ int getValue(); } diff --git a/src/org/infinity/datatype/IwdRef.java b/src/org/infinity/datatype/IwdRef.java index a1c42bc6f..83b28ad51 100644 --- a/src/org/infinity/datatype/IwdRef.java +++ b/src/org/infinity/datatype/IwdRef.java @@ -25,12 +25,14 @@ public IwdRef(ByteBuffer buffer, int offset, String name, String idsFile) public long getValue(String ref) { if (ref != null && !ref.isEmpty()) { - ref = ref.toUpperCase(Locale.ENGLISH); - List list = getResourceList(); - for (final RefEntry entry: list) { - if (entry.getResourceName().equals(ref)) { - return entry.getValue(); - } + RefEntry entry = getBitmap() + .values() + .parallelStream() + .filter(re -> re.getResourceName().equalsIgnoreCase(ref)) + .findAny() + .orElse(null); + if (entry != null) { + return entry.getValue(); } } return -1L; @@ -47,11 +49,9 @@ public String getValueRef() public String getValueRef(long id) { - List list = getResourceList(); - for (final RefEntry entry: list) { - if (entry.getValue() == id) { - return entry.getResourceName(); - } + RefEntry result = getBitmap().values().parallelStream().filter(re -> re.getValue() == id).findAny().orElse(null); + if (result != null) { + return result.getResourceName(); } return NONE; } diff --git a/src/org/infinity/datatype/KitIdsBitmap.java b/src/org/infinity/datatype/KitIdsBitmap.java index cfc7e74b3..6920da8e5 100644 --- a/src/org/infinity/datatype/KitIdsBitmap.java +++ b/src/org/infinity/datatype/KitIdsBitmap.java @@ -18,6 +18,7 @@ public class KitIdsBitmap extends IdsBitmap public KitIdsBitmap(ByteBuffer buffer, int offset, String name) { super(buffer, offset, 4, name, "KIT.IDS"); + setShowAsHex(true); // adding "No Kit" value if needed addIdsMapEntry(new IdsMapEntry(0L, "NO_KIT")); @@ -35,6 +36,12 @@ public void write(OutputStream os) throws IOException //--------------------- End Interface Writeable --------------------- + @Override + protected String getHexValue(long value) + { + return String.format("0x%04X", value); + } + /** Swaps position of the two lower words. */ private static long swapWords(long value) { diff --git a/src/org/infinity/datatype/MultiNumber.java b/src/org/infinity/datatype/MultiNumber.java index eb6b4022d..d8cd83792 100644 --- a/src/org/infinity/datatype/MultiNumber.java +++ b/src/org/infinity/datatype/MultiNumber.java @@ -6,27 +6,29 @@ import java.awt.Color; import java.awt.Dimension; +import java.awt.Font; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; +import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; import javax.swing.BorderFactory; -import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.ListSelectionModel; +import javax.swing.event.TableModelEvent; +import javax.swing.event.TableModelListener; import javax.swing.table.AbstractTableModel; import org.infinity.gui.BrowserMenuBar; import org.infinity.gui.StructViewer; import org.infinity.gui.ViewerUtil; -import org.infinity.icon.Icons; import org.infinity.resource.AbstractStruct; import org.infinity.util.Misc; @@ -104,19 +106,31 @@ public JComponent edit(ActionListener container) tValues = new JTable(mValues); tValues.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); tValues.setFont(Misc.getScaledFont(BrowserMenuBar.getInstance().getScriptFont())); + tValues.getTableHeader().setFont(tValues.getTableHeader().getFont().deriveFont(Font.BOLD)); tValues.setRowHeight(tValues.getFontMetrics(tValues.getFont()).getHeight() + 1); tValues.setBorder(BorderFactory.createLineBorder(Color.GRAY)); tValues.getTableHeader().setBorder(BorderFactory.createLineBorder(Color.GRAY)); tValues.getTableHeader().setReorderingAllowed(false); tValues.getTableHeader().setResizingAllowed(true); + tValues.setRowHeight(tValues.getRowHeight() * 5 / 4); + tValues.setIntercellSpacing(new Dimension(4, 0)); tValues.setPreferredScrollableViewportSize(tValues.getPreferredSize()); + tValues.setCellSelectionEnabled(true); + tValues.getModel().addTableModelListener(new TableModelListener() { + @Override + public void tableChanged(TableModelEvent e) + { + // update field value automatically + if (e.getType() == TableModelEvent.UPDATE) { + ActionEvent ae = new ActionEvent(tValues, 0, StructViewer.UPDATE_VALUE); + container.actionPerformed(ae); + } + } + }); + JScrollPane scroll = new JScrollPane(tValues); scroll.setBorder(BorderFactory.createEmptyBorder()); - JButton bUpdate = new JButton("Update value", Icons.getIcon(Icons.ICON_REFRESH_16)); - bUpdate.addActionListener(container); - bUpdate.setActionCommand(StructViewer.UPDATE_VALUE); - JPanel panel = new JPanel(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); @@ -124,17 +138,14 @@ public JComponent edit(ActionListener container) GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); panel.add(scroll, gbc); - gbc = ViewerUtil.setGBC(gbc, 1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, - GridBagConstraints.NONE, new Insets(0, 8, 0, 0), 0, 0); - panel.add(bUpdate, gbc); - Dimension dim = Misc.getScaledDimension(DIM_MEDIUM); panel.setPreferredSize(dim); // making "Attribute" column wider - int tableWidth = dim.width - bUpdate.getPreferredSize().width - 8; - tValues.getColumnModel().getColumn(0).setPreferredWidth(tableWidth * 3 / 4); - tValues.getColumnModel().getColumn(1).setPreferredWidth(tableWidth * 1 / 4); + tValues.getColumnModel().getColumn(0).setPreferredWidth(dim.width * 3 / 4); + tValues.getColumnModel().getColumn(1).setPreferredWidth(dim.width * 1 / 4); + + tValues.changeSelection(0, 1, false, false); return panel; } @@ -148,10 +159,13 @@ public void select() @Override public boolean updateValue(AbstractStruct struct) { + long oldValue = getLongValue(); setValueImpl(mValues.getValue()); // notifying listeners - fireValueUpdated(new UpdateEvent(this, struct)); + if (getLongValue() != oldValue) { + fireValueUpdated(new UpdateEvent(this, struct)); + } return true; } @@ -209,6 +223,25 @@ public String toString() return sb.toString(); } + @Override + public int hashCode() + { + int hash = super.hashCode(); + hash = 31 * hash + Integer.hashCode(value); + return hash; + } + + @Override + public boolean equals(Object o) + { + if (!super.equals(o) || !(o instanceof MultiNumber)) { + return false; + } + MultiNumber other = (MultiNumber)o; + boolean retVal = (value == other.value); + return retVal; + } + /** Returns number of bits per value. */ public int getBits() { diff --git a/src/org/infinity/datatype/PriTypeBitmap.java b/src/org/infinity/datatype/PriTypeBitmap.java index 81c1ecd77..3619834c1 100644 --- a/src/org/infinity/datatype/PriTypeBitmap.java +++ b/src/org/infinity/datatype/PriTypeBitmap.java @@ -6,13 +6,13 @@ import java.nio.ByteBuffer; import java.util.Locale; +import java.util.TreeMap; import org.infinity.resource.Profile; import org.infinity.resource.ResourceFactory; import org.infinity.util.IdsMap; import org.infinity.util.IdsMapCache; import org.infinity.util.IdsMapEntry; -import org.infinity.util.LongIntegerHashMap; import org.infinity.util.Table2da; import org.infinity.util.Table2daCache; @@ -23,7 +23,7 @@ public class PriTypeBitmap extends HashBitmap private static final String[] s_school = {"None", "Abjurer", "Conjurer", "Diviner", "Enchanter", "Illusionist", "Invoker", "Necromancer", "Transmuter", "Generalist"}; - private static final LongIntegerHashMap typeMap = new LongIntegerHashMap(); + private static final TreeMap typeMap = new TreeMap<>(); static { if (ResourceFactory.resourceExists("MSCHOOL.2DA")) { @@ -47,11 +47,11 @@ public static String getTableName() public static String[] getTypeArray() { - final LongIntegerHashMap map = getTypeTable(); + final TreeMap map = getTypeTable(); return map.values().toArray(new String[map.size()]); } - private static synchronized LongIntegerHashMap getTypeTable() + private static synchronized TreeMap getTypeTable() { if (typeMap.isEmpty()) { if (ResourceFactory.resourceExists(TableName) && TableName.endsWith(".2DA")) { diff --git a/src/org/infinity/datatype/ResourceBitmap.java b/src/org/infinity/datatype/ResourceBitmap.java index 71d8f2dc7..e743134ad 100644 --- a/src/org/infinity/datatype/ResourceBitmap.java +++ b/src/org/infinity/datatype/ResourceBitmap.java @@ -4,29 +4,17 @@ package org.infinity.datatype; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.io.IOException; -import java.io.OutputStream; +import java.beans.PropertyChangeEvent; import java.nio.ByteBuffer; import java.nio.file.Path; -import java.util.ArrayList; -import java.util.Collections; import java.util.List; +import java.util.TreeMap; +import java.util.function.BiFunction; import javax.swing.JButton; -import javax.swing.JComponent; -import javax.swing.JPanel; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; -import org.infinity.gui.StructViewer; -import org.infinity.gui.TextListPanel; import org.infinity.gui.ViewFrame; import org.infinity.icon.Icons; import org.infinity.resource.AbstractStruct; @@ -47,245 +35,106 @@ *

  • Value meaning: index of the resource in the predefined list
  • * */ -public class ResourceBitmap extends Datatype - implements Editable, IsNumeric, IsReference, ActionListener, ListSelectionListener +public class ResourceBitmap extends AbstractBitmap implements IsReference, ActionListener { /** Print resource reference together with search name in parentheses and value after hyphen. */ - public static final String FMT_REF_NAME_VALUE = "%1$s (%2$s) - %3$s"; + public static final String FMT_REF_NAME_VALUE = "%s (%s) - %s"; /** Print search name together with resource reference in parentheses and value after hyphen. */ public static final String FMT_NAME_REF_VALUE = "%2$s (%1$s) - %3$s"; /** Print resource reference together with its search name after hyphen. */ - public static final String FMT_REF_HYPHEN_NAME = "%1$s - %2$s"; + public static final String FMT_REF_HYPHEN_NAME = "%s - %s"; /** Print search name together with its resource reference after hyphen. */ public static final String FMT_NAME_HYPHEN_REF = "%2$s - %1$s"; /** Print resource reference together with value after hyphen. */ public static final String FMT_REF_HYPHEN_VALUE = "%1$s - %3$s"; /** Print resource reference together with its search name. */ - public static final String FMT_REF_NAME = "%1$s (%2$s)"; + public static final String FMT_REF_NAME = "%s (%s)"; /** Print search name together with its resource reference. */ public static final String FMT_NAME_REF = "%2$s (%1$s)"; /** Print resource reference together with value in parentheses. */ public static final String FMT_REF_VALUE = "%1$s (%3$s)"; /** Print resource reference only. */ - public static final String FMT_REF_ONLY = "%1$s"; + public static final String FMT_REF_ONLY = "%s"; /** Print only the search name of the resource. */ public static final String FMT_NAME_ONLY = "%2$s"; /** Print resource value only. */ public static final String FMT_VALUE_ONLY = "%3$s"; - private final List resources; - private final String defaultLabel; - private final String formatString; - private JButton bView; - private TextListPanel list; - private long value; - - public ResourceBitmap(ByteBuffer buffer, int offset, int length, String name, - List resources, String defLabel, String fmt) - { - super(offset, length, name); - this.formatString = (fmt != null) ? fmt : FMT_REF_VALUE; - this.defaultLabel = (defLabel != null) ? defLabel : "Unknown"; - - this.resources = new ArrayList<>((resources != null) ? resources.size() : 10); - if (resources != null) { - for (final RefEntry entry: resources) { - entry.setFormatString(this.formatString); - this.resources.add(entry); - } + private final BiFunction formatterResourceBitmap = (value, item) -> { + String number; + if (isShowAsHex()) { + number = getHexValue(value.longValue()); + } else { + number = value.toString(); } - Collections.sort(this.resources); - - read(buffer, offset); - } -//--------------------- Begin Interface ActionListener --------------------- - - @Override - public void actionPerformed(ActionEvent event) - { - if (event.getSource() == bView) { - final RefEntry selected = list.getSelectedValue(); - if (selected != null) { - final ResourceEntry entry = selected.getResourceEntry(); - new ViewFrame(list.getTopLevelAncestor(), entry == null ? null : ResourceFactory.getResource(entry)); - } + String resName, searchString; + if (item != null) { + resName = item.getResourceName(); + searchString = item.getSearchString(); + } else { + resName = getDefaultLabel(); + searchString = ""; } - } -//--------------------- End Interface ActionListener --------------------- + return String.format(getFormatString(), resName, searchString, number); + }; -//--------------------- Begin Interface ListSelectionListener --------------------- + private final String defaultLabel; + private final String formatString; + private final JButton bView; - @Override - public void valueChanged(ListSelectionEvent e) + public ResourceBitmap(ByteBuffer buffer, int offset, int length, String name, List resources, + String defLabel, String fmt) { - final RefEntry selected = list.getSelectedValue(); - bView.setEnabled(selected != null && selected.isResource()); + this(buffer, offset, length, name, resources, defLabel, fmt, false); } -//--------------------- End Interface ListSelectionListener --------------------- - -//--------------------- Begin Interface Editable --------------------- - - @Override - public JComponent edit(final ActionListener container) + public ResourceBitmap(ByteBuffer buffer, int offset, int length, String name, List resources, + String defLabel, String fmt, boolean signed) { - list = new TextListPanel<>(resources, false); - list.addMouseListener(new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent event) - { - if (event.getClickCount() == 2) - container.actionPerformed(new ActionEvent(this, 0, StructViewer.UPDATE_VALUE)); - } - }); + super(buffer, offset, length, name, createMap(resources), null, true); + this.formatString = (fmt != null) ? fmt : FMT_REF_VALUE; + this.defaultLabel = (defLabel != null) ? defLabel : "Unknown"; + this.setSortByName(true); + this.setFormatter(formatterResourceBitmap); - RefEntry curEntry = getRefEntry(value); + RefEntry curEntry = getDataOf(getLongValue()); if (curEntry == null) { - curEntry = getRefEntry(0L); + curEntry = getDataOf(0L); } - if (curEntry == null && resources.size() > 0) { + if (curEntry == null && resources != null && resources.size() > 0) { curEntry = resources.get(0); } - if (curEntry != null) { - list.setSelectedValue(curEntry, true); - } - - JButton bUpdate = new JButton("Update value", Icons.getIcon(Icons.ICON_REFRESH_16)); - bUpdate.addActionListener(container); - bUpdate.setActionCommand(StructViewer.UPDATE_VALUE); - bView = new JButton("View/Edit", Icons.getIcon(Icons.ICON_ZOOM_16)); - bView.addActionListener(this); - bView.setEnabled(curEntry != null && curEntry.isResource()); - list.addListSelectionListener(this); - - GridBagLayout gbl = new GridBagLayout(); - GridBagConstraints gbc = new GridBagConstraints(); - JPanel panel = new JPanel(gbl); - - gbc.weightx = 1.0; - gbc.weighty = 1.0; - gbc.fill = GridBagConstraints.BOTH; - gbc.gridheight = 2; - gbl.setConstraints(list, gbc); - panel.add(list); - - gbc.gridheight = 1; - gbc.weightx = 0.0; - gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.insets = new Insets(3, 6, 3, 0); - gbc.anchor = GridBagConstraints.SOUTH; - gbc.gridwidth = GridBagConstraints.REMAINDER; - gbl.setConstraints(bUpdate, gbc); - panel.add(bUpdate); - - gbc.gridx = 1; - gbc.gridy = 1; - gbc.anchor = GridBagConstraints.NORTH; - gbl.setConstraints(bView, gbc); - panel.add(bView); - - panel.setMinimumSize(Misc.getScaledDimension(DIM_MEDIUM)); - return panel; + this.bView = new JButton("View/Edit", Icons.getIcon(Icons.ICON_ZOOM_16)); + this.bView.addActionListener(this); + this.bView.setEnabled(curEntry != null && curEntry.isResource()); + addButtons(this.bView); } - @Override - public void select() - { - list.ensureIndexIsVisible(list.getSelectedIndex()); - } - - @Override - public boolean updateValue(AbstractStruct struct) - { - final RefEntry selected = list.getSelectedValue(); - if (selected == null) { - return false; - } - - setValue(selected.getValue()); - // notifying listeners - fireValueUpdated(new UpdateEvent(this, struct)); - - return true; - } - -//--------------------- End Interface Editable --------------------- - -//--------------------- Begin Interface Writeable --------------------- + //--------------------- Begin Interface ActionListener --------------------- @Override - public void write(OutputStream os) throws IOException + public void actionPerformed(ActionEvent e) { - writeLong(os, value); - } - -//--------------------- End Interface Writeable --------------------- - -//--------------------- Begin Interface Readable --------------------- - - @Override - public int read(ByteBuffer buffer, int offset) - { - buffer.position(offset); - switch (getSize()) { - case 1: - value = buffer.get() & 0xff; - break; - case 2: - value = buffer.getShort() & 0xffff; - break; - case 4: - value = buffer.getInt() & 0xffffffff; - break; - default: - throw new IllegalArgumentException(); - } - - return offset + getSize(); - } - -//--------------------- End Interface Readable --------------------- - - @Override - public String toString() - { - String resName, searchString; - RefEntry ref = getRefEntry(value); - if (ref != null) { - resName = ref.getResourceName(); - searchString = ref.getSearchString(); - } else { - resName = defaultLabel; - searchString = ""; + if (e.getSource() == bView) { + Long value = getSelectedValue(); + final RefEntry selected = (value != null) ? getDataOf(value.longValue()) : null; + if (selected != null) { + final ResourceEntry entry = selected.getResourceEntry(); + new ViewFrame(getUiControl().getTopLevelAncestor(), ResourceFactory.getResource(entry)); + } } - - return String.format(formatString, resName, searchString, Long.toString(value)); } - //--------------------- Begin Interface IsNumeric --------------------- - - @Override - public int getValue() - { - return (int)value; - } - - @Override - public long getLongValue() - { - return value; - } - -//--------------------- End Interface IsNumeric --------------------- + //--------------------- End Interface ActionListener --------------------- //--------------------- Begin Interface IsReference --------------------- @Override public String getResourceName() { - RefEntry entry = getRefEntry(value); + RefEntry entry = getDataOf(getLongValue()); if (entry != null) { return entry.getResourceName(); } else { @@ -295,33 +144,35 @@ public String getResourceName() //--------------------- End Interface IsReference --------------------- - public List getResourceList() + @Override + protected void listItemChanged() { - return resources; + Long value = getSelectedValue(); + RefEntry selected = (value != null) ? getDataOf(value.longValue()) : null; + bView.setEnabled(selected != null && selected.isResource()); } - public String getFormatString() + protected String getFormatString() { return formatString; } - private RefEntry getRefEntry(long value) + protected String getDefaultLabel() { - for (final RefEntry entry : resources) { - if (entry.getValue() == value) { - return entry; - } - } - return null; + return defaultLabel; } - private void setValue(long newValue) +private static TreeMap createMap(List resources) { - final long oldValue = value; - value = newValue; - if (oldValue != newValue) { - firePropertyChange(oldValue, newValue); + TreeMap retVal = new TreeMap<>(); + if (resources != null) { + for (final RefEntry entry : resources) { + if (entry != null) { + retVal.put(Long.valueOf(entry.getValue()), entry); + } + } } + return retVal; } //-------------------------- INNER CLASSES -------------------------- @@ -336,8 +187,6 @@ public static final class RefEntry implements Comparable private final ResourceEntry entry; /** Resource-dependent search string. */ private final String searchString; - /** Format string for textual representation. */ - private String fmt; /** Cached textual output for {@link #toString()} method. */ private String desc; @@ -362,8 +211,7 @@ public RefEntry(long value, String ref, String search, List searchDirs) this.searchString = (search != null) ? search : ""; this.name = ref; } - this.fmt = FMT_REF_VALUE; - this.desc = String.format(fmt, getResourceName(), getSearchString(), Long.toString(value)); + this.desc = String.format(FMT_REF_VALUE, getResourceName(), getSearchString(), Long.toString(value)); } @Override @@ -372,6 +220,18 @@ public String toString() return desc; } + @Override + public int hashCode() + { + int hash = 7; + hash = 31 * hash + Long.hashCode(value); + hash = 31 * hash + ((name == null) ? 0 : name.hashCode()); + hash = 31 * hash + ((entry == null) ? 0 : entry.hashCode()); + hash = 31 * hash + ((searchString == null) ? 0 : searchString.hashCode()); + hash = 31 * hash + ((desc == null) ? 0 : desc.hashCode()); + return hash; + } + @Override public boolean equals(Object o) { @@ -403,17 +263,5 @@ public String getSearchString() { return searchString; } - - public String getFormatString() { return fmt; } - - public void setFormatString(String fmt) - { - if (fmt != null) { - this.fmt = fmt; - } else { - this.fmt = FMT_REF_VALUE; - } - this.desc = String.format(fmt, getResourceName(), getSearchString(), Long.toString(value)); - } } } diff --git a/src/org/infinity/datatype/ResourceRef.java b/src/org/infinity/datatype/ResourceRef.java index 089283ab7..30d8291f5 100644 --- a/src/org/infinity/datatype/ResourceRef.java +++ b/src/org/infinity/datatype/ResourceRef.java @@ -94,7 +94,6 @@ private ResourceRef(ByteBuffer h_buffer, int offset, int length, String name, St read(h_buffer, offset); } - // @Override public void actionPerformed(ActionEvent event) { @@ -105,9 +104,7 @@ public void actionPerformed(ActionEvent event) } } } - // - // @Override public JComponent edit(final ActionListener container) { @@ -212,12 +209,15 @@ public void select() @Override public boolean updateValue(AbstractStruct struct) { + String oldString = getText(); final ResourceRefEntry selected = list.getSelectedValue(); if (selected == NONE) { setValue(NONE.name);//FIXME: use null instead of this // notifying listeners - fireValueUpdated(new UpdateEvent(this, struct)); + if (!getText().equals(oldString)) { + fireValueUpdated(new UpdateEvent(this, struct)); + } return true; } @@ -240,21 +240,19 @@ public boolean updateValue(AbstractStruct struct) } // notifying listeners - fireValueUpdated(new UpdateEvent(this, struct)); + if (!getText().equals(oldString)) { + fireValueUpdated(new UpdateEvent(this, struct)); + } return true; } - // - // @Override public void valueChanged(ListSelectionEvent e) { bView.setEnabled(isEditable(list.getSelectedValue())); } - // - // @Override public void write(OutputStream os) throws IOException { @@ -271,9 +269,7 @@ public void write(OutputStream os) throws IOException StreamUtils.writeString(os, resname, getSize()); } } - // - // @Override public int read(ByteBuffer buffer, int offset) { @@ -294,7 +290,6 @@ public int read(ByteBuffer buffer, int offset) return offset + getSize(); } - // @Override public String toString() @@ -307,15 +302,35 @@ public String toString() return getResourceName(); } - // + @Override + public int hashCode() + { + int hash = super.hashCode(); + hash = 31 * hash + ((type == null) ? 0 : type.hashCode()); + hash = 31 * hash + ((resname == null) ? 0 : resname.hashCode()); + return hash; + } + + @Override + public boolean equals(Object o) + { + if (!super.equals(o) || !(o instanceof ResourceRef)) { + return false; + } + ResourceRef other = (ResourceRef)o; + boolean retVal = (type == null && other.type == null) || + (type != null && type.equals(other.type)); + retVal &= (resname == null && other.resname == null) || + (resname != null && resname.equals(other.resname)); + return retVal; + } + @Override public String getText() { return resname; } - // - // @Override public String getResourceName() { @@ -324,7 +339,6 @@ public String getResourceName() } return resname; } - // public boolean isEmpty() { diff --git a/src/org/infinity/datatype/SecTypeBitmap.java b/src/org/infinity/datatype/SecTypeBitmap.java index 94a9451f7..4832a5c16 100644 --- a/src/org/infinity/datatype/SecTypeBitmap.java +++ b/src/org/infinity/datatype/SecTypeBitmap.java @@ -6,9 +6,9 @@ import java.nio.ByteBuffer; import java.util.Locale; +import java.util.TreeMap; import org.infinity.resource.ResourceFactory; -import org.infinity.util.LongIntegerHashMap; import org.infinity.util.Table2da; import org.infinity.util.Table2daCache; @@ -21,7 +21,7 @@ public class SecTypeBitmap extends HashBitmap "Divination attack", "Conjuration", "Combat protections", "Contingency", "Battleground", "Offensive damage", "Disabling", "Combination", "Non-combat"}; - private static final LongIntegerHashMap typeMap = new LongIntegerHashMap<>(); + private static final TreeMap typeMap = new TreeMap<>(); public SecTypeBitmap(ByteBuffer buffer, int offset, int length, String name) { @@ -35,11 +35,11 @@ public static String getTableName() public static String[] getTypeArray() { - final LongIntegerHashMap map = getTypeTable(); + final TreeMap map = getTypeTable(); return map.values().toArray(new String[map.size()]); } - private static synchronized LongIntegerHashMap getTypeTable() + private static synchronized TreeMap getTypeTable() { if (typeMap.isEmpty()) { if (ResourceFactory.resourceExists(TableName)) { diff --git a/src/org/infinity/datatype/Song2daBitmap.java b/src/org/infinity/datatype/Song2daBitmap.java index ed1afcef5..f250fe489 100644 --- a/src/org/infinity/datatype/Song2daBitmap.java +++ b/src/org/infinity/datatype/Song2daBitmap.java @@ -37,7 +37,7 @@ public Song2daBitmap(ByteBuffer buffer, int offset, int length) public Song2daBitmap(ByteBuffer buffer, int offset, int length, String name) { - super(buffer, offset, length, name, createSongList(), "Unknown", FormatString); + super(buffer, offset, length, name, createSongList(), "Unknown", FormatString, true); } public static synchronized List createSongList() @@ -79,8 +79,8 @@ private static List createSongList_SONGLIST(List searchDirs) Table2da table = Table2daCache.get(TableName); String defValue = table.getDefaultValue(); SongList.ensureCapacity(2 + table.getRowCount()); - SongList.add(new RefEntry(0xfffffffeL, "Special", "Continue area music")); - SongList.add(new RefEntry(0xffffffffL, "Special", "Continue outside music")); + SongList.add(new RefEntry(-2L, "Special", "Continue area music")); + SongList.add(new RefEntry(-1L, "Special", "Continue outside music")); for (int i = 0, size = table.getRowCount(); i < size; i++) { String name = table.get(i, 1); @@ -100,8 +100,8 @@ private static List createSongList_MUSIC(List searchDirs) if (SongList.isEmpty()) { IdsMap map = IdsMapCache.get(TableName); SongList.ensureCapacity(2 + map.size()); - SongList.add(new RefEntry(0xfffffffeL, "Continue area music")); - SongList.add(new RefEntry(0xffffffffL, "Continue outside music")); + SongList.add(new RefEntry(-2L, "Continue area music")); + SongList.add(new RefEntry(-1L, "Continue outside music")); for (final IdsMapEntry e: map.getAllValues()) { String name = e.getSymbol(); @@ -167,8 +167,8 @@ private static List createSongList_PST(List searchDirs) }; SongList.ensureCapacity(2 + map.length); - SongList.add(new RefEntry(0xfffffffeL, "Special", "Continue area music")); - SongList.add(new RefEntry(0xffffffffL, "Special", "Continue outside music")); + SongList.add(new RefEntry(-2L, "Special", "Continue area music")); + SongList.add(new RefEntry(-1L, "Special", "Continue outside music")); for (int i = 0; i < map.length; i+=2) { final String ref = map[i]; @@ -230,8 +230,8 @@ private static List createSongList_BG1(List searchDirs) /*38L,*/ "DEATH.MUS", }; - SongList.add(new RefEntry(0xfffffffeL, "Continue area music")); - SongList.add(new RefEntry(0xffffffffL, "Continue outside music")); + SongList.add(new RefEntry(-2L, "Continue area music")); + SongList.add(new RefEntry(-1L, "Continue outside music")); for (int i = 0; i < map.length; ++i) { SongList.add(new RefEntry(i, map[i], null, searchDirs)); diff --git a/src/org/infinity/datatype/SpellProtType.java b/src/org/infinity/datatype/SpellProtType.java index 3cc15bcf5..caf417a2a 100644 --- a/src/org/infinity/datatype/SpellProtType.java +++ b/src/org/infinity/datatype/SpellProtType.java @@ -7,6 +7,7 @@ import java.nio.ByteBuffer; import java.util.Arrays; import java.util.ListIterator; +import java.util.TreeMap; import org.infinity.resource.AbstractStruct; import org.infinity.resource.Profile; @@ -15,7 +16,6 @@ import org.infinity.util.IdsMap; import org.infinity.util.IdsMapCache; import org.infinity.util.IdsMapEntry; -import org.infinity.util.LongIntegerHashMap; import org.infinity.util.Table2da; import org.infinity.util.Table2daCache; @@ -64,7 +64,7 @@ public class SpellProtType extends Bitmap "ALIGN.IDS", "KIT.IDS" }; private static final String tableName = "SPLPROT.2DA"; - private static final LongIntegerHashMap statIds = new LongIntegerHashMap<>(); + private static final TreeMap statIds = new TreeMap<>(); private static String[] creType; static { @@ -303,9 +303,9 @@ private static String[] getExternalizedTypeTable() break; case 0x102: // circle size if (isBitwiseRelation(rel) && value != -1) { - label = String.format("Circle size %s %d [0x%x]", getRelation(rel), value, value); + label = String.format("Personal space %s %d [0x%x]", getRelation(rel), value, value); } else { - label = String.format("Circle size %s %d", getRelation(rel), value); + label = String.format("Personal space %s %d", getRelation(rel), value); } break; case 0x103: // use two rows of splprot.2da diff --git a/src/org/infinity/datatype/StringRef.java b/src/org/infinity/datatype/StringRef.java index f2237b059..9d06af6c4 100644 --- a/src/org/infinity/datatype/StringRef.java +++ b/src/org/infinity/datatype/StringRef.java @@ -261,10 +261,13 @@ public void select() @Override public boolean updateValue(AbstractStruct struct) { + long oldValue = getLongValue(); setValue(getValueFromEditor()); // notifying listeners - fireValueUpdated(new UpdateEvent(this, struct)); + if (getLongValue() != oldValue) { + fireValueUpdated(new UpdateEvent(this, struct)); + } return true; } @@ -309,6 +312,25 @@ public String toString(StringTable.Format fmt) return StringTable.getStringRef(value, fmt); } + @Override + public int hashCode() + { + int hash = super.hashCode(); + hash = 31 * hash + Integer.hashCode(value); + return hash; + } + + @Override + public boolean equals(Object o) + { + if (!super.equals(o) || !(o instanceof StringRef)) { + return false; + } + StringRef other = (StringRef)o; + boolean retVal = (value == other.value); + return retVal; + } + //--------------------- Begin Interface IsNumeric --------------------- @Override diff --git a/src/org/infinity/datatype/Summon2daBitmap.java b/src/org/infinity/datatype/Summon2daBitmap.java index 6c3715f29..9564d11ba 100644 --- a/src/org/infinity/datatype/Summon2daBitmap.java +++ b/src/org/infinity/datatype/Summon2daBitmap.java @@ -6,9 +6,9 @@ import java.nio.ByteBuffer; import java.util.Locale; +import java.util.TreeMap; import org.infinity.resource.ResourceFactory; -import org.infinity.util.LongIntegerHashMap; import org.infinity.util.Table2da; import org.infinity.util.Table2daCache; @@ -16,7 +16,7 @@ public class Summon2daBitmap extends HashBitmap { private static final String TableName = "SMTABLES.2DA"; - private static final LongIntegerHashMap summonMap = new LongIntegerHashMap<>(); + private static final TreeMap summonMap = new TreeMap<>(); public Summon2daBitmap(ByteBuffer buffer, int offset, int length, String name) { @@ -28,7 +28,7 @@ public static String getTableName() return TableName; } - private static synchronized LongIntegerHashMap getSummonTable() + private static synchronized TreeMap getSummonTable() { if (summonMap.isEmpty()) { if (ResourceFactory.resourceExists(TableName)) { diff --git a/src/org/infinity/datatype/TextBitmap.java b/src/org/infinity/datatype/TextBitmap.java index 5bbcd28d6..dc9f26bac 100644 --- a/src/org/infinity/datatype/TextBitmap.java +++ b/src/org/infinity/datatype/TextBitmap.java @@ -121,10 +121,13 @@ public boolean updateValue(AbstractStruct struct) if (index == -1) { return false; } + String oldString = getText(); setValue(ids[index]); // notifying listeners - fireValueUpdated(new UpdateEvent(this, struct)); + if (getText().equals(oldString)) { + fireValueUpdated(new UpdateEvent(this, struct)); + } return true; } @@ -173,6 +176,26 @@ public String toString() return text; } + @Override + public int hashCode() + { + int hash = super.hashCode(); + hash = 31 * hash + ((text == null) ? 0 : text.hashCode()); + return hash; + } + + @Override + public boolean equals(Object o) + { + if (!super.equals(o) || !(o instanceof TextBitmap)) { + return false; + } + TextBitmap other = (TextBitmap)o; + boolean retVal = (text == null && other.text == null) || + (text != null && text.equals(other.text)); + return retVal; + } + //--------------------- Begin Interface IsTextual --------------------- /** Returns the unprocessed textual symbol of fixed number of characters. */ diff --git a/src/org/infinity/datatype/TextEdit.java b/src/org/infinity/datatype/TextEdit.java index e32bef4c5..f8831eda3 100644 --- a/src/org/infinity/datatype/TextEdit.java +++ b/src/org/infinity/datatype/TextEdit.java @@ -126,10 +126,13 @@ public void select() @Override public boolean updateValue(AbstractStruct struct) { + String oldString = getText(); setValue(textArea.getText()); // notifying listeners - fireValueUpdated(new UpdateEvent(this, struct)); + if (!getText().equals(oldString)) { + fireValueUpdated(new UpdateEvent(this, struct)); + } return true; } @@ -179,6 +182,27 @@ public String toString() return getText(); } + @Override + public int hashCode() + { + int hash = super.hashCode(); + hash = 31 * hash + ((text == null) ? 0 : text.hashCode()); + return hash; + } + + @Override + public boolean equals(Object o) + { + if (!super.equals(o) || !(o instanceof TextEdit)) { + return false; + } + TextEdit other = (TextEdit)o; + boolean retVal = (text == null && other.text == null) || + (text != null && text.equals(other.text)); + return retVal; + } + + public ByteBuffer toBuffer() { if (text != null) { diff --git a/src/org/infinity/datatype/TextString.java b/src/org/infinity/datatype/TextString.java index e303baa6c..fa9ad5d4b 100644 --- a/src/org/infinity/datatype/TextString.java +++ b/src/org/infinity/datatype/TextString.java @@ -46,11 +46,19 @@ public TextString(ByteBuffer buffer, int offset, int length, String name) @Override public boolean update(Object value) { - String newstring = (String)value; - if (newstring.length() > getSize()) { + String newString = value.toString(); + if (newString.length() > getSize()) { return false; } - setValue(newstring); + + String oldString = getText(); + setValue(newString); + + // notifying listeners + if (!getText().equals(oldString)) { + fireValueUpdated(new UpdateEvent(this, getParent())); + } + return true; } @@ -109,6 +117,26 @@ public String toString() return getText(); } + @Override + public int hashCode() + { + int hash = super.hashCode(); + hash = 31 * hash + ((text == null) ? 0 : text.hashCode()); + return hash; + } + + @Override + public boolean equals(Object o) + { + if (!super.equals(o) || !(o instanceof TextString)) { + return false; + } + TextString other = (TextString)o; + boolean retVal = (text == null && other.text == null) || + (text != null && text.equals(other.text)); + return retVal; + } + private void setValue(String newValue) { final String oldValue = getText(); diff --git a/src/org/infinity/datatype/Unknown.java b/src/org/infinity/datatype/Unknown.java index 28c96a438..bfde32a97 100644 --- a/src/org/infinity/datatype/Unknown.java +++ b/src/org/infinity/datatype/Unknown.java @@ -117,10 +117,13 @@ public boolean updateValue(AbstractStruct struct) if (newData == null) { return false; } + ByteBuffer oldBuffer = getData(); setValue(newData); // notifying listeners - fireValueUpdated(new UpdateEvent(this, struct)); + if (getData().compareTo(oldBuffer) != 0) { + fireValueUpdated(new UpdateEvent(this, struct)); + } return true; } @@ -156,9 +159,11 @@ public int read(ByteBuffer buffer, int offset) @Override public ByteBuffer getData() { - buffer.position(0); - ByteBuffer bb = StreamUtils.getByteBuffer(buffer.remaining()); - buffer.put(bb); + ByteBuffer bb = ByteBuffer.allocate(buffer.capacity()); + buffer.rewind(); + bb.put(buffer); + buffer.rewind(); + bb.flip(); return bb; } @@ -184,6 +189,28 @@ public String toString() return ""; } + @Override + public int hashCode() + { + int hash = super.hashCode(); + hash = 31 * hash + ((buffer == null || buffer.array() == null) ? 0 : buffer.array().hashCode()); + return hash; + } + + @Override + public boolean equals(Object o) + { + if (!super.equals(o) || !(o instanceof Unknown)) { + return false; + } + Unknown other = (Unknown)o; + byte[] arr1 = (buffer != null && buffer.array() != null) ? buffer.array() : null; + byte[] arr2 = (other.buffer != null && other.buffer.array() != null) ? other.buffer.array() : null; + boolean retVal = (arr1 == null && arr2 == null) || + (arr1 != null && arr1.equals(arr2)); + return retVal; + } + protected void setValue(byte[] newValue) { buffer.position(0); diff --git a/src/org/infinity/datatype/UnsignDecNumber.java b/src/org/infinity/datatype/UnsignDecNumber.java index 712b0705a..00f4f2ee1 100644 --- a/src/org/infinity/datatype/UnsignDecNumber.java +++ b/src/org/infinity/datatype/UnsignDecNumber.java @@ -6,7 +6,7 @@ import java.nio.ByteBuffer; -public class UnsignDecNumber extends DecNumber implements InlineEditable +public class UnsignDecNumber extends DecNumber { public UnsignDecNumber(ByteBuffer buffer, int offset, int length, String name) { diff --git a/src/org/infinity/gui/BIFFEditor.java b/src/org/infinity/gui/BIFFEditor.java index efd6bce95..2f417f5c4 100644 --- a/src/org/infinity/gui/BIFFEditor.java +++ b/src/org/infinity/gui/BIFFEditor.java @@ -71,7 +71,6 @@ public BIFFEditor() new ChooseBIFFrame(this); } - // @Override public void actionPerformed(ActionEvent event) { @@ -95,9 +94,7 @@ else if (event.getSource() == cbformat) { bsave.setEnabled(!biftable.isEmpty()); } } - // - // @Override public void valueChanged(ListSelectionEvent event) { @@ -106,9 +103,7 @@ public void valueChanged(ListSelectionEvent event) btobif.setEnabled(overridetable.getSelectedValues().length != 0); } } - // - // @Override public void run() { @@ -201,7 +196,6 @@ public void run() ResourceFactory.getResourceTreeModel().sort(); blocker.setBlocked(false); } - // public void makeEditor(BIFFEntry bifentry, AbstractBIFFReader.Type format) { diff --git a/src/org/infinity/gui/BIFFEditorTable.java b/src/org/infinity/gui/BIFFEditorTable.java index 7b852cf04..17203644c 100644 --- a/src/org/infinity/gui/BIFFEditorTable.java +++ b/src/org/infinity/gui/BIFFEditorTable.java @@ -142,7 +142,6 @@ public void mouseClicked(MouseEvent event) add(new JScrollPane(table), BorderLayout.CENTER); } - // @Override public void actionPerformed(ActionEvent event) { @@ -153,7 +152,6 @@ else if (event.getSource() == bupdated) else if (event.getSource() == bnew) tablemodel.fireTableDataChanged(); } - // public void addEntry(ResourceEntry entry, State state) { diff --git a/src/org/infinity/gui/BookmarkEditor.java b/src/org/infinity/gui/BookmarkEditor.java index 8746fa85a..ed0587eb0 100644 --- a/src/org/infinity/gui/BookmarkEditor.java +++ b/src/org/infinity/gui/BookmarkEditor.java @@ -60,8 +60,8 @@ */ public class BookmarkEditor extends JDialog implements ActionListener, FocusListener, ListSelectionListener, ItemListener { - private final SimpleListModel modelEntries = new SimpleListModel(); - private final JList listEntries = new JList(modelEntries); + private final SimpleListModel modelEntries = new SimpleListModel<>(); + private final JList listEntries = new JList<>(modelEntries); private final JButton bUp = new JButton("Up"); private final JButton bDown = new JButton("Down"); private final JButton bRemove = new JButton("Remove"); @@ -73,12 +73,12 @@ public class BookmarkEditor extends JDialog implements ActionListener, FocusList private final JTextField tfName = new JTextField(); private final JTextField tfPath = createReadOnlyField(null, true); private final DefaultComboBoxModel cbPlatformModel = - new DefaultComboBoxModel(BrowserMenuBar.Bookmark.getSupportedOS()); + new DefaultComboBoxModel<>(BrowserMenuBar.Bookmark.getSupportedOS()); private final JComboBox cbPlatform = new JComboBox<>(cbPlatformModel); private final EnumMap> listBinPathModels = new EnumMap<>(Platform.OS.class); - private final JList listBinPaths = new JList(); + private final JList listBinPaths = new JList<>(); - private final List listBookmarks = new ArrayList(); + private final List listBookmarks = new ArrayList<>(); private boolean accepted; @@ -305,7 +305,7 @@ private void accept() // updating bookmark list listBookmarks.clear(); for (int i = 0, size = modelEntries.size(); i < size; i++) { - listBookmarks.add((BrowserMenuBar.Bookmark)modelEntries.get(i)); + listBookmarks.add(modelEntries.get(i)); } accepted = true; setVisible(false); @@ -534,7 +534,7 @@ public void focusLost(FocusEvent event) if (event.getSource() == tfName) { int idx = listEntries.getSelectedIndex(); if (idx >= 0) { - BrowserMenuBar.Bookmark bookmark = (BrowserMenuBar.Bookmark)modelEntries.get(idx); + BrowserMenuBar.Bookmark bookmark = modelEntries.get(idx); if (!tfName.getText().trim().isEmpty()) { // update name in selected entry bookmark.setName(tfName.getText().trim()); diff --git a/src/org/infinity/gui/BrowserMenuBar.java b/src/org/infinity/gui/BrowserMenuBar.java index 0f70afd0d..7340e4762 100644 --- a/src/org/infinity/gui/BrowserMenuBar.java +++ b/src/org/infinity/gui/BrowserMenuBar.java @@ -16,7 +16,6 @@ import java.awt.KeyEventDispatcher; import java.awt.Toolkit; import java.awt.event.ActionEvent; -import static java.awt.event.ActionEvent.ALT_MASK; import java.awt.event.ActionListener; import java.awt.event.InputEvent; import java.awt.event.ItemEvent; @@ -85,6 +84,7 @@ import org.infinity.resource.StructureFactory; import org.infinity.resource.Viewable; import org.infinity.resource.ViewableContainer; +import org.infinity.resource.cre.browser.CreatureBrowser; import org.infinity.resource.key.FileResourceEntry; import org.infinity.resource.key.Keyfile; import org.infinity.resource.key.ResourceEntry; @@ -98,18 +98,18 @@ import org.infinity.updater.Updater; import org.infinity.updater.UpdaterSettings; import org.infinity.util.CharsetDetector; +import org.infinity.util.DataString; import org.infinity.util.MassExporter; import org.infinity.util.Misc; -import org.infinity.util.ObjectString; -import org.infinity.util.Pair; import org.infinity.util.Platform; import org.infinity.util.StringTable; import org.infinity.util.io.FileEx; import org.infinity.util.io.FileManager; +import org.infinity.util.tuples.Couple; public final class BrowserMenuBar extends JMenuBar implements KeyEventDispatcher { - public static final String VERSION = "v2.1-20210123"; + public static final String VERSION = "v2.2-20210501"; public static final LookAndFeelInfo DEFAULT_LOOKFEEL = new LookAndFeelInfo("Metal", "javax.swing.plaf.metal.MetalLookAndFeel"); @@ -130,7 +130,6 @@ public final class BrowserMenuBar extends JMenuBar implements KeyEventDispatcher private final HelpMenu helpMenu; private final Preferences prefsGui, prefsProfiles; - // /** Determines, in which virtual folder show resources from Override folder. */ public enum OverrideMode { @@ -204,7 +203,6 @@ public enum ViewMode private ViewMode(String title) { this.title = title; } } - // public static BrowserMenuBar getInstance() { @@ -668,7 +666,6 @@ public void storePreferences() gameMenu.storePreferences(); } - // @Override public boolean dispatchKeyEvent(KeyEvent e) { @@ -728,7 +725,6 @@ private static Frame findActiveFrame() } return null; } - // // -------------------------- INNER CLASSES -------------------------- @@ -741,11 +737,11 @@ private static final class GameMenu extends JMenu implements ActionListener gameProperties, gameBookmarkAdd, gameBookmarkEdit, gameRecentClear; private final JMenu gameRecent = new JMenu("Recently opened games"); - private final List recentList = new ArrayList(); + private final List recentList = new ArrayList<>(); private final JPopupMenu.Separator gameRecentSeparator = new JPopupMenu.Separator(); private final JMenu gameBookmarks = new JMenu("Bookmarked games"); - private final List bookmarkList = new ArrayList(); + private final List bookmarkList = new ArrayList<>(); private final JPopupMenu.Separator gameBookmarkSeparator = new JPopupMenu.Separator(); private GameMenu() @@ -787,7 +783,7 @@ private GameMenu() String path = getPrefsProfiles().get(Bookmark.getBinaryPathKey(os, i), null); if (path != null) { if (binPaths == null) - binPaths = new EnumMap>(Platform.OS.class); + binPaths = new EnumMap<>(Platform.OS.class); List list = Bookmark.unpackBinPaths(os, path); binPaths.put(os, list); } @@ -1408,12 +1404,12 @@ private SearchMenu() advancedSearch = makeMenuItem("Advanced search...", KeyEvent.VK_A, Icons.getIcon(Icons.ICON_FIND_16), -1, this); - advancedSearch.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A, CTRL_MASK | ALT_MASK)); + advancedSearch.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A, CTRL_MASK | ActionEvent.ALT_MASK)); advancedSearch.setToolTipText("A powerful and highly flexible search for structured resources of all kinds."); menuAdvanced.add(advancedSearch); searchResource = makeMenuItem("Legacy extended search...", KeyEvent.VK_X, Icons.getIcon(Icons.ICON_FIND_16), -1, this); - searchResource.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X, CTRL_MASK | ALT_MASK)); + searchResource.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X, CTRL_MASK | ActionEvent.ALT_MASK)); searchResource.setToolTipText("The original \"Extended Search\"."); menuAdvanced.add(searchResource); @@ -1483,7 +1479,7 @@ private static final class ToolsMenu extends JMenu implements ActionListener { private static final String TOOLS_DEBUG_EXTRA_INFO = "DebugShowExtraInfo"; - private final JMenuItem toolInfinityAmp, toolCleanKeyfile, toolCheckAllDialog, toolCheckOverrideDialog; + private final JMenuItem toolInfinityAmp, toolCreatureBrowser, toolCleanKeyfile, toolCheckAllDialog, toolCheckOverrideDialog; private final JMenuItem toolCheckResRef, toolIDSBrowser, toolDropZone, toolCheckCREInv; private final JMenuItem toolCheckIDSRef, toolCheckIDSBCSRef, toolCheckScripts, toolCheckStructs; private final JMenuItem toolCheckStringUse, toolCheckStringIndex, toolCheckFileUse, toolMassExport; @@ -1498,6 +1494,10 @@ private ToolsMenu() super("Tools"); setMnemonic(KeyEvent.VK_T); + toolCreatureBrowser = makeMenuItem("Creature Animation Browser", KeyEvent.VK_A, + Icons.getIcon(Icons.ICON_CRE_VIEWER_24), -1, this); + add(toolCreatureBrowser); + toolInfinityAmp = makeMenuItem("InfinityAmp", KeyEvent.VK_I, Icons.getIcon(Icons.ICON_VOLUME_16), -1, this); add(toolInfinityAmp); @@ -1631,7 +1631,7 @@ private ToolsMenu() add(toolConsole); dumpDebugInfo = new JMenuItem("Print debug info to Console", Icons.getIcon(Icons.ICON_PROPERTIES_16)); dumpDebugInfo.setToolTipText("Output to console class of current top-level window, resource and selected field in the structure viewer"); - dumpDebugInfo.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_D, CTRL_MASK | ALT_MASK)); + dumpDebugInfo.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_D, CTRL_MASK | ActionEvent.ALT_MASK)); dumpDebugInfo.addActionListener(this); dumpDebugInfo.setEnabled(getPrefs().getBoolean(TOOLS_DEBUG_EXTRA_INFO, false)); dumpDebugInfo.setVisible(dumpDebugInfo.isEnabled()); @@ -1685,7 +1685,10 @@ private void storePreferences() @Override public void actionPerformed(ActionEvent event) { - if (event.getSource() == toolInfinityAmp) { + if (event.getSource() == toolCreatureBrowser) { + ChildFrame.show(CreatureBrowser.class, () -> new CreatureBrowser()); + } + else if (event.getSource() == toolInfinityAmp) { ChildFrame.show(InfinityAmp.class, () -> new InfinityAmp()); } else if (event.getSource() == toolIDSBrowser) { @@ -1722,9 +1725,9 @@ public void windowClosing(WindowEvent e) else if (event.getSource() == dumpDebugInfo) { dumpDebugInfo(); } - else if (event.getSource() == toolCleanKeyfile) + else if (event.getSource() == toolCleanKeyfile) { // cleanKeyfile(); - ; + } else if (event.getSource() == toolDropZone) { ChildFrame.show(BcsDropFrame.class, () -> new BcsDropFrame()); } @@ -1779,7 +1782,7 @@ private static final class OptionsMenu extends JMenu implements ActionListener, new Font(Font.MONOSPACED, Font.PLAIN, 12), new Font(Font.SERIF, Font.PLAIN, 12), new Font(Font.SANS_SERIF, Font.PLAIN, 12), new Font(Font.DIALOG, Font.PLAIN, 12), null}; private static final String DefaultCharset = "Auto"; - private static final List CharsetsUsed = new ArrayList(); + private static final List CharsetsUsed = new ArrayList<>(); /** BCS indentations to use when decompiling (indent, title). */ private static final String[][] BCSINDENT = { {" ", "2 Spaces"}, {" ", "4 Spaces"}, @@ -1916,7 +1919,7 @@ private static final class OptionsMenu extends JMenu implements ActionListener, private int optionFixedInternal; /** Stores available languages in BG(2)EE. */ - private final HashMap gameLanguage = new HashMap(); + private final HashMap gameLanguage = new HashMap<>(); private OptionsMenu() { @@ -2712,9 +2715,9 @@ private int getSelectedButtonIndex(AbstractButton[] items, int defaultIndex) } /** Extracts entries of Game/Language pairs from the given argument. */ - private List> extractGameLanguages(String definition) + private List> extractGameLanguages(String definition) { - List> list = new ArrayList>(); + List> list = new ArrayList<>(); if (definition != null && !definition.isEmpty()) { String[] entries = definition.split(";"); if (entries != null) { @@ -2724,22 +2727,18 @@ private List> extractGameLanguages(String definition) Profile.Game game = Profile.gameFromString(elements[0]); if (game != Profile.Game.Unknown) { String lang = elements[1].trim(); - Pair pair = null; + Couple pair = null; if (lang.equalsIgnoreCase(LANGUAGE_AUTODETECT)) { - pair = new Pair(); - pair.setFirst(game.toString()); - pair.setSecond(LANGUAGE_AUTODETECT); + pair = Couple.with(game.toString(), LANGUAGE_AUTODETECT); } else if (lang.matches("[a-z]{2}_[A-Z]{2}")) { - pair = new Pair(); - pair.setFirst(game.toString()); - pair.setSecond(lang); + pair = Couple.with(game.toString(), lang); } // check if game/language pair is already in the list if (pair != null) { - for (final Pair curPair: list) { - if (curPair.getFirst().equalsIgnoreCase(pair.getFirst())) { - curPair.setSecond(pair.getSecond()); + for (final Couple curPair: list) { + if (curPair.getValue0().equalsIgnoreCase(pair.getValue0())) { + curPair.setValue1(pair.getValue1()); pair = null; break; } @@ -2758,13 +2757,13 @@ private List> extractGameLanguages(String definition) } /** Creates a formatted string out of the Game/Language pairs included in the given list. */ - private String createGameLanguages(List> list) + private String createGameLanguages(List> list) { StringBuilder sb = new StringBuilder(); if (list != null) { - for (Iterator> iter = list.iterator(); iter.hasNext();) { - Pair pair = iter.next(); - sb.append(String.format("%s=%s", pair.getFirst(), pair.getSecond())); + for (final Iterator> iter = list.iterator(); iter.hasNext();) { + Couple pair = iter.next(); + sb.append(String.format("%s=%s", pair.getValue0(), pair.getValue1())); if (iter.hasNext()) { sb.append(';'); } @@ -2774,14 +2773,14 @@ private String createGameLanguages(List> list) } /** Adds or updates the Game/Language pair in the formatted "definition" string. */ - private String updateGameLanguages(String definition, Pair pair) + private String updateGameLanguages(String definition, Couple pair) { - List> list = extractGameLanguages(definition); - if (pair != null && pair.getFirst() != null && pair.getSecond() != null) { + List> list = extractGameLanguages(definition); + if (pair != null && pair.getValue0() != null && pair.getValue1() != null) { // attempt to update existing entry first - for (final Pair curPair: list) { - if (curPair.getFirst().equalsIgnoreCase(pair.getFirst())) { - curPair.setSecond(pair.getSecond()); + for (final Couple curPair: list) { + if (curPair.getValue0().equalsIgnoreCase(pair.getValue0())) { + curPair.setValue1(pair.getValue1()); pair = null; break; } @@ -2801,12 +2800,12 @@ private String updateGameLanguages(String definition, Pair pair) private String getGameLanguage(String definition, Profile.Game game) { if (game != null && game != Profile.Game.Unknown) { - List> list = extractGameLanguages(definition); - for (Iterator> iter = list.iterator(); iter.hasNext();) { - Pair pair = iter.next(); - Profile.Game curGame = Profile.gameFromString(pair.getFirst()); + List> list = extractGameLanguages(definition); + for (final Iterator> iter = list.iterator(); iter.hasNext();) { + Couple pair = iter.next(); + Profile.Game curGame = Profile.gameFromString(pair.getValue0()); if (curGame == game) { - return pair.getSecond(); + return pair.getValue1(); } } } @@ -2843,7 +2842,7 @@ private void switchGameLanguage(String newLanguage) if (Profile.updateGameLanguage(newLanguageCode)) { languageDefinition = updateGameLanguages(languageDefinition, - new Pair(Profile.getGame().toString(), newLanguage)); + Couple.with(Profile.getGame().toString(), newLanguage)); NearInfinity.getInstance().refreshGame(); success = true; } else { @@ -3300,44 +3299,44 @@ private void displayAbout() // title string final String versionString = "Near Infinity " + VERSION; // list of current links - final ObjectString[] currentLinks = { - new ObjectString("Active branch", "https://github.com/Argent77/NearInfinity/"), - new ObjectString("Main branch", "https://github.com/NearInfinityBrowser/NearInfinity/"), - new ObjectString("Wiki page", wikiUrl), - }; + final List> currentLinks = new ArrayList>() {{ + add(DataString.with("Active branch", "https://github.com/Argent77/NearInfinity/")); + add(DataString.with("Main branch", "https://github.com/NearInfinityBrowser/NearInfinity/")); + add(DataString.with("Wiki page", wikiUrl)); + }}; // original author final String originalVersion = "From Near Infinity 1.32.1 beta 24"; final String originalCopyright = "Copyright (\u00A9) 2001-2005 - Jon Olav Hauglid"; - final ObjectString originalLink = new ObjectString("Website", "http://www.idi.ntnu.no/~joh/ni/"); + final DataString originalLink = DataString.with("Website", "http://www.idi.ntnu.no/~joh/ni/"); // List of contributors (sorted alphabetically) - final String[] contributors = { - "Argent77", - "Bubb", - "devSin", - "Fredrik Lindgren (aka Wisp)", - "FredSRichardson", - "Mingun", - "Taimon", - "Valerio Bigiani (aka The Bigg)", - "winterheart", - }; + final List contributors = new ArrayList() {{ + add("Argent77"); + add("Bubb"); + add("devSin"); + add("Fredrik Lindgren (aka Wisp)"); + add("FredSRichardson"); + add("Mingun"); + add("Taimon"); + add("Valerio Bigiani (aka The Bigg)"); + add("winterheart"); + }}; // More contributors, in separate block - final String[] contributorsMisc = { - "Near Infinity logo/icon by Cuv and Troodon80", - }; + final List contributorsMisc = new ArrayList() {{ + add("Near Infinity logo/icon by Cuv and Troodon80"); + }}; // copyright message - final String[] copyNearInfinityText = { - "This program is free and may be distributed according to the terms of ", - "the GNU Lesser General Public License." - }; + final List copyNearInfinityText = new ArrayList() {{ + add("This program is free and may be distributed according to the terms of "); + add("the GNU Lesser General Public License."); + }}; // Third-party copyright messages - final String[] copyThirdPartyText = { - "Most icons (\u00A9) eclipse.org - Common Public License.", - "RSyntaxTextArea (\u00A9) Fifesoft - Berkeley Software Distribution License.", - "Monte Media Library by Werner Randelshofer - GNU Lesser General Public License.", - "JOrbis (\u00A9) JCraft Inc. - GNU Lesser General Public License.", - "JHexView by Sebastian Porst - GNU General Public License.", - }; + final List copyThirdPartyText = new ArrayList() {{ + add("Most icons (\u00A9) eclipse.org - Common Public License."); + add("RSyntaxTextArea (\u00A9) Fifesoft - Berkeley Software Distribution License."); + add("Monte Media Library by Werner Randelshofer - GNU Lesser General Public License."); + add("JOrbis (\u00A9) JCraft Inc. - GNU Lesser General Public License."); + add("JHexView by Sebastian Porst - GNU General Public License."); + }}; // Fixed elements final Font defaultfont = UIManager.getFont("Label.font"); @@ -3355,11 +3354,11 @@ private void displayAbout() { int row = 0; // current links - for (int i = 0; i < currentLinks.length; i++, row++) { + for (int i = 0; i < currentLinks.size(); i++, row++) { int top = (i > 0) ? 4 : 0; - JLabel lTitle = new JLabel(currentLinks[i].getString() + ":"); + JLabel lTitle = new JLabel(currentLinks.get(i).getString() + ":"); lTitle.setFont(font); - String link = currentLinks[i].getObject(); + String link = currentLinks.get(i).getData(); JLabel lLink = ViewerUtil.createUrlLabel(link); lLink.setFont(font); gbc = ViewerUtil.setGBC(gbc, 0, row, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, @@ -3388,7 +3387,7 @@ private void displayAbout() gbc = ViewerUtil.setGBC(gbc, 0, row, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(4, 0, 0, 0), 0, 0); pLinks.add(label, gbc); - String link = originalLink.getObject(); + String link = originalLink.getData(); label = ViewerUtil.createUrlLabel(link); label.setFont(font); gbc = ViewerUtil.setGBC(gbc, 1, row, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, @@ -3403,8 +3402,8 @@ private void displayAbout() // trying to limit line width to a certain maximum FontMetrics fm = getFontMetrics(font); double maxWidth = 0.0; - for (int i = 0; i < currentLinks.length; i++) { - String s = currentLinks[i].getString() + ": " + currentLinks[i].getObject(); + for (int i = 0; i < currentLinks.size(); i++) { + String s = currentLinks.get(i).getString() + ": " + currentLinks.get(i).getData(); maxWidth = Math.max(maxWidth, fm.getStringBounds(s, getGraphics()).getWidth()); } @@ -3419,15 +3418,15 @@ private void displayAbout() // adding names StringBuilder sb = new StringBuilder(); - for (int i = 0; i < contributors.length; i++) { + for (int i = 0; i < contributors.size(); i++) { if (i > 0) { - if (i+1 == contributors.length) { + if (i+1 == contributors.size()) { sb.append(" and "); } else { sb.append(", "); } } - String s = sb.toString() + contributors[i]; + String s = sb.toString() + contributors.get(i); if (fm.getStringBounds(s, getGraphics()).getWidth() > maxWidth) { label = new JLabel(sb.toString()); label.setFont(smallFont); @@ -3437,7 +3436,7 @@ private void displayAbout() row++; sb = new StringBuilder(); } - sb.append(contributors[i]); + sb.append(contributors.get(i)); } label = new JLabel(sb.toString()); label.setFont(smallFont); @@ -3447,8 +3446,8 @@ private void displayAbout() row++; // Adding misc. contributors - for (int i = 0; i < contributorsMisc.length; i++) { - label = new JLabel(contributorsMisc[i]); + for (int i = 0; i < contributorsMisc.size(); i++) { + label = new JLabel(contributorsMisc.get(i)); label.setFont(smallFont); gbc = ViewerUtil.setGBC(gbc, 0, row, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(i == 0 ? 4 : 0, 0, 0, 0), 0, 0); @@ -3468,8 +3467,8 @@ private void displayAbout() pLicense.add(label, gbc); row++; - for (int i = 0; i < copyNearInfinityText.length; i++) { - label = new JLabel(copyNearInfinityText[i]); + for (int i = 0; i < copyNearInfinityText.size(); i++) { + label = new JLabel(copyNearInfinityText.get(i)); label.setFont(smallFont); gbc = ViewerUtil.setGBC(gbc, 0, row, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); @@ -3489,8 +3488,8 @@ private void displayAbout() pMiscLicenses.add(label, gbc); row++; - for (int i = 0; i < copyThirdPartyText.length; i++) { - label = new JLabel(copyThirdPartyText[i]); + for (int i = 0; i < copyThirdPartyText.size(); i++) { + label = new JLabel(copyThirdPartyText.get(i)); label.setFont(smallFont); gbc = ViewerUtil.setGBC(gbc, 0, row, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); diff --git a/src/org/infinity/gui/ButtonPanel.java b/src/org/infinity/gui/ButtonPanel.java index 962892ad7..198844733 100644 --- a/src/org/infinity/gui/ButtonPanel.java +++ b/src/org/infinity/gui/ButtonPanel.java @@ -88,7 +88,7 @@ public enum Control { private static final int DefaultGapSize = 4; - private final List listControls = new ArrayList(); + private final List listControls = new ArrayList<>(); private int gapSize; diff --git a/src/org/infinity/gui/ButtonPopupMenu.java b/src/org/infinity/gui/ButtonPopupMenu.java index b54f591f4..aabe6e1bb 100644 --- a/src/org/infinity/gui/ButtonPopupMenu.java +++ b/src/org/infinity/gui/ButtonPopupMenu.java @@ -45,7 +45,7 @@ public int compare(JComponent item1, JComponent item2) private final ScrollPopupMenu menu = new ScrollPopupMenu(); private final PopupListener listener = new PopupListener(); private final PopupItemListener itemListener = new PopupItemListener(); - private List items = new ArrayList(); + private List items = new ArrayList<>(); private JMenuItem selected; private Align menuAlign; @@ -218,7 +218,7 @@ public void setMenuItems(JMenuItem[] menuItems) */ public void setMenuItems(JMenuItem[] menuItems, boolean sorted) { - List list = new ArrayList(); + List list = new ArrayList<>(); if (menuItems != null) { Collections.addAll(list, menuItems); } @@ -245,7 +245,7 @@ public void setMenuItems(List menuItems, boolean sorted) if (menuItems != null) { List preparedList; if (sorted) { - preparedList = new ArrayList(menuItems); + preparedList = new ArrayList<>(menuItems); Collections.sort(preparedList, menuItemComparator); } else { preparedList = menuItems; @@ -270,7 +270,7 @@ public List getItems() */ public List getMenuItems() { - List list = new ArrayList(items.size()); + List list = new ArrayList<>(items.size()); for (final JComponent c: items) { if (c instanceof JMenuItem) { list.add((JMenuItem)c); @@ -328,6 +328,7 @@ public void addSeparator() /** * Removes the menu item at the specified index from the popup menu. */ + @Override public void remove(int pos) { if (pos < 0 || pos >= items.size()) { @@ -341,6 +342,7 @@ public void remove(int pos) /** * Removes all items from the popup menu. */ + @Override public void removeAll() { for (int i = items.size() - 1; i >= 0; i--) { diff --git a/src/org/infinity/gui/ButtonPopupWindow.java b/src/org/infinity/gui/ButtonPopupWindow.java index 9ecb97292..5f01755db 100644 --- a/src/org/infinity/gui/ButtonPopupWindow.java +++ b/src/org/infinity/gui/ButtonPopupWindow.java @@ -56,7 +56,7 @@ public enum Align { } private final PopupWindow window = new PopupWindow(this); - private final List listeners = new ArrayList(); + private final List listeners = new ArrayList<>(); private PopupWindow ignoredWindow; // used to determine whether to hide the current window on lost focus private Align windowAlign; diff --git a/src/org/infinity/gui/ChildFrame.java b/src/org/infinity/gui/ChildFrame.java index 52819f3da..5af37dcd6 100644 --- a/src/org/infinity/gui/ChildFrame.java +++ b/src/org/infinity/gui/ChildFrame.java @@ -12,6 +12,7 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.function.Predicate; import java.util.function.Supplier; import javax.swing.AbstractAction; @@ -96,6 +97,35 @@ public static T getFirstFrame(Class frameClass) return null; } + /** + * Returns an iterator over the registered {@code ChildFrame} instances that are compatible with the + * specified class type. + * @param Class of the filtered instances + * @param frameClass Runtime class of the windows to filter + * @return An iterator over matching instances. Returns {@code null} if the parameter is {@code null}. + */ + public static Iterator getFrameIterator(Class frameClass) + { + if (frameClass != null) { + return windows.stream().filter(frameClass::isInstance).map(frameClass::cast).iterator(); + } + return null; + } + + /** + * Returns an iterator over the registered {@code ChildFrame} instances that are matching the given predicate. + * @param pred Predicate used to filter registered windows + * @return An iterator over matching {@code ChildFrame} instances + */ + public static Iterator getFrameIterator(Predicate pred) + { + if (pred != null) { + return windows.stream().filter(pred).iterator(); + } else { + return windows.iterator(); + } + } + /** * Shows first window of specified class. If window do not yet exists, create * it with {@code init} function and then shows. diff --git a/src/org/infinity/gui/ColorGrid.java b/src/org/infinity/gui/ColorGrid.java index a91be27fd..05b4ec13e 100644 --- a/src/org/infinity/gui/ColorGrid.java +++ b/src/org/infinity/gui/ColorGrid.java @@ -64,12 +64,12 @@ public enum Frame { SINGLE_LINE, DOUBLE_LINE } // Background pattern is visible for semi-transparent color entries private static final TexturePaint BackgroundPattern = createBackgroundPattern(); - private final List listColors = new ArrayList(); - private final List listActionListeners = new ArrayList(); - private final List listMouseOverListeners = new ArrayList(); - private final List listChangeListeners = new ArrayList(); + private final List listColors = new ArrayList<>(); + private final List listActionListeners = new ArrayList<>(); + private final List listMouseOverListeners = new ArrayList<>(); + private final List listChangeListeners = new ArrayList<>(); // stores selected color entry indices - private final List listSelection = new ArrayList(); + private final List listSelection = new ArrayList<>(); private Frame frame; // frame type private Color frameColor; // frame color diff --git a/src/org/infinity/gui/FixedFocusTraversalPolicy.java b/src/org/infinity/gui/FixedFocusTraversalPolicy.java index 1ef948435..856cc4b2f 100644 --- a/src/org/infinity/gui/FixedFocusTraversalPolicy.java +++ b/src/org/infinity/gui/FixedFocusTraversalPolicy.java @@ -14,7 +14,7 @@ */ public class FixedFocusTraversalPolicy extends FocusTraversalPolicy { - private final Vector order = new Vector(); + private final Vector order = new Vector<>(); private int defaultIndex; /** diff --git a/src/org/infinity/gui/FontChooser.java b/src/org/infinity/gui/FontChooser.java index 1dff7bb1f..4e0543165 100644 --- a/src/org/infinity/gui/FontChooser.java +++ b/src/org/infinity/gui/FontChooser.java @@ -246,7 +246,7 @@ public JList getFontSizeList() **/ public String getSelectedFontFamily() { - String fontName = (String) getFontFamilyList().getSelectedValue(); + String fontName = getFontFamilyList().getSelectedValue(); return fontName; } @@ -284,7 +284,7 @@ public int getSelectedFontSize() fontSize = Integer.parseInt(fontSizeString); break; } catch (NumberFormatException e) { - fontSizeString = (String) getFontSizeList().getSelectedValue(); + fontSizeString = getFontSizeList().getSelectedValue(); getFontSizeTextField().setText(fontSizeString); } } @@ -408,6 +408,7 @@ public int showDialog(Component parent) dialogResultValue = ERROR_OPTION; JDialog dialog = createDialog(parent); dialog.addWindowListener(new WindowAdapter() { + @Override public void windowClosing(WindowEvent e) { dialogResultValue = CANCEL_OPTION; @@ -432,6 +433,7 @@ protected class ListSelectionHandler implements ListSelectionListener this.textComponent = textComponent; } + @Override public void valueChanged(ListSelectionEvent e) { if (e.getValueIsAdjusting() == false) { @@ -459,11 +461,13 @@ public TextFieldFocusHandlerForTextSelection(JTextComponent textComponent) this.textComponent = textComponent; } + @Override public void focusGained(FocusEvent e) { textComponent.selectAll(); } + @Override public void focusLost(FocusEvent e) { textComponent.select(0, 0); @@ -480,6 +484,7 @@ public TextFieldKeyHandlerForListSelectionUpDown(JList list) this.targetList = list; } + @Override public void keyPressed(KeyEvent e) { int i = targetList.getSelectedIndex(); @@ -514,16 +519,19 @@ public ListSearchTextFieldDocumentHandler(JList targetList) this.targetList = targetList; } + @Override public void insertUpdate(DocumentEvent e) { update(e); } + @Override public void removeUpdate(DocumentEvent e) { update(e); } + @Override public void changedUpdate(DocumentEvent e) { update(e); @@ -564,6 +572,7 @@ public ListSelector(int index) this.index = index; } + @Override public void run() { targetList.setSelectedIndex(this.index); @@ -584,6 +593,7 @@ protected DialogOKAction(JDialog dialog) putValue(Action.NAME, (ACTION_NAME)); } + @Override public void actionPerformed(ActionEvent e) { dialogResultValue = OK_OPTION; @@ -604,6 +614,7 @@ protected DialogCancelAction(JDialog dialog) putValue(Action.NAME, (ACTION_NAME)); } + @Override public void actionPerformed(ActionEvent e) { dialogResultValue = CANCEL_OPTION; diff --git a/src/org/infinity/gui/GameProperties.java b/src/org/infinity/gui/GameProperties.java index 92a2dc839..526b37959 100644 --- a/src/org/infinity/gui/GameProperties.java +++ b/src/org/infinity/gui/GameProperties.java @@ -41,8 +41,8 @@ import org.infinity.icon.Icons; import org.infinity.resource.Profile; import org.infinity.resource.ResourceFactory; -import org.infinity.util.Pair; import org.infinity.util.io.FileEx; +import org.infinity.util.tuples.Couple; /** * Display verbose information about the currently selected game. @@ -161,7 +161,7 @@ private void init() setLayout(new BorderLayout()); GridBagConstraints gbc = new GridBagConstraints(); - List> listControls = new ArrayList>(); + List> listControls = new ArrayList<>(); JLabel l; JTextField tf; String s; @@ -173,25 +173,25 @@ private void init() } l = new JLabel("Game type:"); tf = createReadOnlyField(s, true); - listControls.add(new Pair(l, tf)); + listControls.add(Couple.with(l, tf)); // Entry: profile name s = Profile.getProperty(Profile.Key.GET_GAME_DESC); l = new JLabel("Profile name:"); tf = createReadOnlyField((s != null) ? s : "n/a", true); - listControls.add(new Pair(l, tf)); + listControls.add(Couple.with(l, tf)); // Entry: game folder s = (Profile.getGameRoot()).toString(); l = new JLabel("Game folder:"); tf = createReadOnlyField(s, true); - listControls.add(new Pair(l, tf)); + listControls.add(Couple.with(l, tf)); if (Profile.isEnhancedEdition()) { // Entry: home folder s = Profile.getHomeRoot().toString(); l = new JLabel("Home folder:"); tf = createReadOnlyField(s, true); - listControls.add(new Pair(l, tf)); + listControls.add(Couple.with(l, tf)); // Entry: detected DLC List dlcPaths = Profile.getProperty(Profile.Key.GET_GAME_DLC_FOLDERS_AVAILABLE); @@ -205,7 +205,7 @@ private void init() } l = new JLabel("DLC archives:"); tf = createReadOnlyField(sb.toString(), true); - listControls.add(new Pair(l, tf)); + listControls.add(Couple.with(l, tf)); } // Entry: available languages @@ -224,19 +224,19 @@ private void init() } l = new JLabel("Available languages:"); tf = createReadOnlyField(sb.toString(), true); - listControls.add(new Pair(l, tf)); + listControls.add(Couple.with(l, tf)); // Entry: language s = getLanguageName(Profile.getProperty(Profile.Key.GET_GAME_LANG_FOLDER_NAME)); l = new JLabel("Current language:"); tf = createReadOnlyField(s, true); - listControls.add(new Pair(l, tf)); + listControls.add(Couple.with(l, tf)); } // Entry: Use female TLK file l = new JLabel("Uses female TLK file:"); tf = createReadOnlyField(Boolean.toString((Profile.getProperty(Profile.Key.GET_GAME_DIALOGF_FILE) != null)), true); - listControls.add(new Pair(l, tf)); + listControls.add(Couple.with(l, tf)); // Entry: game's ini file l = new JLabel("Game's INI file:"); @@ -253,19 +253,19 @@ private void init() gbc = ViewerUtil.setGBC(gbc, 1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 4, 0, 0), 0, 0); pIni.add(bEdit, gbc); - listControls.add(new Pair(l, pIni)); + listControls.add(Couple.with(l, pIni)); // adding controls from listControls to dialog JPanel pFixed = new JPanel(new GridBagLayout()); int row = 0; - for (Iterator> iter = listControls.iterator(); iter.hasNext();) { - Pair pair = iter.next(); + for (final Iterator> iter = listControls.iterator(); iter.hasNext();) { + Couple pair = iter.next(); gbc = ViewerUtil.setGBC(gbc, 0, row, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(8, 0, 0, 0), 0, 0); - pFixed.add(pair.getFirst(), gbc); + pFixed.add(pair.getValue0(), gbc); gbc = ViewerUtil.setGBC(gbc, 1, row, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(8, 8, 0, 0), 0, 0); - pFixed.add(pair.getSecond(), gbc); + pFixed.add(pair.getValue1(), gbc); row++; } @@ -279,7 +279,7 @@ private void init() FlowLayout flow = new FlowLayout(FlowLayout.LEFT, 8, 4); JPanel pSupportList = new JPanel(flow); pSupportList.setBorder(BorderFactory.createLineBorder(Color.BLACK)); - List listTypes = new ArrayList(); + List listTypes = new ArrayList<>(); int maxWidth = 0, maxHeight = 0; // preparing entries for (Iterator iter = RES_TYPES.keySet().iterator(); iter.hasNext();) { diff --git a/src/org/infinity/gui/IdsBrowser.java b/src/org/infinity/gui/IdsBrowser.java index 6ab2ad096..a7f5bbe5b 100644 --- a/src/org/infinity/gui/IdsBrowser.java +++ b/src/org/infinity/gui/IdsBrowser.java @@ -60,7 +60,7 @@ public IdsBrowser() public void mouseClicked(MouseEvent e) { if (e.getClickCount() == 2) - insertString(((IdsMapEntry)list.getSelectedValue()).getSymbol()); + insertString(list.getSelectedValue().getSymbol()); } }); @@ -99,7 +99,7 @@ public void actionPerformed(ActionEvent event) if (event.getSource() == idsfiles) refreshList(); else if (event.getSource() == binsert) - insertString(((IdsMapEntry)list.getSelectedValue()).getSymbol()); + insertString(list.getSelectedValue().getSymbol()); } // --------------------- End Interface ActionListener --------------------- diff --git a/src/org/infinity/gui/InfinityAmp.java b/src/org/infinity/gui/InfinityAmp.java index 985444620..86ca3ca07 100644 --- a/src/org/infinity/gui/InfinityAmp.java +++ b/src/org/infinity/gui/InfinityAmp.java @@ -44,8 +44,8 @@ public final class InfinityAmp extends ChildFrame implements ActionListener, ListSelectionListener, Runnable, Closeable { - private final SimpleListModel allMusModel = new SimpleListModel(); - private final SimpleListModel selectedMusModel = new SimpleListModel(); + private final SimpleListModel allMusModel = new SimpleListModel<>(); + private final SimpleListModel selectedMusModel = new SimpleListModel<>(); private final JButton bPlay = new JButton(Icons.getIcon(Icons.ICON_PLAY_16)); private final JButton bStop = new JButton(Icons.getIcon(Icons.ICON_STOP_16)); private final JButton bAdd = new JButton(Icons.getIcon(Icons.ICON_FORWARD_16)); @@ -57,7 +57,7 @@ public final class InfinityAmp extends ChildFrame private final JList selectedMusList; private final JTextField tfNowPlaying = new JTextField(10); private final AudioPlayer player = new AudioPlayer(); - private List entryList = new ArrayList(); + private List entryList = new ArrayList<>(); private boolean keepPlaying = true; public InfinityAmp() diff --git a/src/org/infinity/gui/InfinityTextArea.java b/src/org/infinity/gui/InfinityTextArea.java index 7c9d2770c..90263022d 100644 --- a/src/org/infinity/gui/InfinityTextArea.java +++ b/src/org/infinity/gui/InfinityTextArea.java @@ -104,7 +104,7 @@ public enum Scheme { /** BCS color scheme based on WeiDU Highlighter for Notepad++ */ public static final String SchemeBCS = "org/infinity/resource/text/modes/ThemeBCSLight.xml"; - private static EnumMap SchemeMap = new EnumMap(Scheme.class); + private static EnumMap SchemeMap = new EnumMap<>(Scheme.class); static { // adding custom code folding definitions @@ -366,28 +366,15 @@ public static void applyExtendedSettings(RSyntaxTextArea edit, Language language } } - InputStream is = null; - try { - is = ClassLoader.getSystemResourceAsStream(schemePath); - if (is != null) { - try { - Theme theme = Theme.load(is); - if (theme != null) { - theme.apply(edit); - } - } catch (IOException e) { - e.printStackTrace(); - } - } - } finally { - if (is != null) { - try { - is.close(); - } catch (IOException e) { - e.printStackTrace(); - } - is = null; + try (InputStream is = ClassLoader.getSystemResourceAsStream(schemePath)) { + Theme theme = Theme.load(is); + if (theme != null) { + theme.apply(edit); } + } catch (NullPointerException e) { + // ignore + } catch (IOException e) { + e.printStackTrace(); } diff --git a/src/org/infinity/gui/LinkButton.java b/src/org/infinity/gui/LinkButton.java index 858180b18..42186b9c2 100644 --- a/src/org/infinity/gui/LinkButton.java +++ b/src/org/infinity/gui/LinkButton.java @@ -208,7 +208,6 @@ public String getUrl() } } - // @Override public void actionPerformed(ActionEvent e) { @@ -229,9 +228,7 @@ public void actionPerformed(ActionEvent e) } } } - // - // @Override public void mouseClicked(MouseEvent e) { @@ -272,7 +269,6 @@ public void mouseEntered(MouseEvent e) public void mouseExited(MouseEvent e) { } - // public void removeActionListener(ActionListener listener) { diff --git a/src/org/infinity/gui/NewResSettings.java b/src/org/infinity/gui/NewResSettings.java index 06599d985..72d5fcdcd 100644 --- a/src/org/infinity/gui/NewResSettings.java +++ b/src/org/infinity/gui/NewResSettings.java @@ -32,20 +32,20 @@ public final class NewResSettings extends NewAbstractSettings implements KeyList private enum GameType { UNKNOWN, BG2, IWD, IWD2, IWDEE } private static final EnumMap> STRREF_ITEM = - new EnumMap>(GameType.class); + new EnumMap<>(GameType.class); static { Vector list; // creating maps for unknown, BG2, IWD and IWD2 // initializing 'unknown' items - STRREF_ITEM.put(GameType.UNKNOWN, (list = new Vector())); + STRREF_ITEM.put(GameType.UNKNOWN, (list = new Vector<>())); list.add(new StrrefItem(-1, "User-defined biography")); // initializing BG2 items - STRREF_ITEM.put(GameType.BG2, (list = new Vector())); + STRREF_ITEM.put(GameType.BG2, (list = new Vector<>())); list.add(new StrrefItem(-1, "User-defined biography")); list.add(new StrrefItem(33347, "Biography of the protagonist")); list.add(new StrrefItem(15882, "Biography of a generic NPC")); // initializing IWD items - STRREF_ITEM.put(GameType.IWD, (list = new Vector())); + STRREF_ITEM.put(GameType.IWD, (list = new Vector<>())); list.add(new StrrefItem(-1, "User-defined biography")); list.add(new StrrefItem(19423, "Biography of a fighter")); list.add(new StrrefItem(19429, "Biography of a ranger")); @@ -56,7 +56,7 @@ private enum GameType { UNKNOWN, BG2, IWD, IWD2, IWDEE } list.add(new StrrefItem(19428, "Biography of a thief")); list.add(new StrrefItem(19425, "Biography of a bard")); // initializing IWD2 items - STRREF_ITEM.put(GameType.IWD2, (list = new Vector())); + STRREF_ITEM.put(GameType.IWD2, (list = new Vector<>())); list.add(new StrrefItem(-1, "User-defined biography")); list.add(new StrrefItem(27862, "Biography of a barbarian")); list.add(new StrrefItem(19425, "Biography of a bard")); @@ -70,7 +70,7 @@ private enum GameType { UNKNOWN, BG2, IWD, IWD2, IWDEE } list.add(new StrrefItem(27863, "Biography of a sorcerer")); list.add(new StrrefItem(19430, "Biography of a wizard")); // initializing IWDEE items - STRREF_ITEM.put(GameType.IWDEE, (list = new Vector())); + STRREF_ITEM.put(GameType.IWDEE, (list = new Vector<>())); list.add(new StrrefItem(-1, "User-defined biography")); list.add(new StrrefItem(19423, "Biography of a fighter")); list.add(new StrrefItem(19429, "Biography of a ranger")); diff --git a/src/org/infinity/gui/OpenResourceDialog.java b/src/org/infinity/gui/OpenResourceDialog.java index 68ebcaa76..3b532680f 100644 --- a/src/org/infinity/gui/OpenResourceDialog.java +++ b/src/org/infinity/gui/OpenResourceDialog.java @@ -46,7 +46,7 @@ import org.infinity.resource.Profile; import org.infinity.resource.ResourceFactory; import org.infinity.resource.key.ResourceEntry; -import org.infinity.util.ObjectString; +import org.infinity.util.DataString; import org.infinity.util.SimpleListModel; /** @@ -59,10 +59,10 @@ public class OpenResourceDialog extends JDialog private final List> resources = new ArrayList<>(); private ResourceEntry[] result; - private ObjectString[] extensions; + private List> extensions; private JList list; private SimpleListModel listModel; - private JComboBox cbType; + private JComboBox> cbType; private JTextField tfSearch; private PlainDocument searchDoc; private JButton bOpen, bCancel; @@ -190,10 +190,10 @@ protected void setExtensions(String[] extList) { if (extList != null && extList.length > 0) { int extra = (extList.length > 1) ? 1 : 0; - extensions = new ObjectString[extList.length + extra]; + extensions = new ArrayList<>();// ObjectString[extList.length + extra]; for (int i = 0; i < extList.length; i++) { final String s = extList[i].trim().toUpperCase(Locale.ENGLISH); - extensions[i + extra] = new ObjectString(s + " resources", s, ObjectString.FMT_STRING_ONLY); + extensions.add(DataString.with(s + " resources", s, DataString.FMT_STRING_ONLY)); } // adding an extra entry which combines all listed extensions @@ -206,10 +206,11 @@ protected void setExtensions(String[] extList) final String s = extList[i].trim().toUpperCase(Locale.ENGLISH); sb.append(s); } - extensions[0] = new ObjectString("Supported resources", sb.toString(), ObjectString.FMT_STRING_ONLY); + extensions.add(0, DataString.with("Supported resources", sb.toString(), DataString.FMT_STRING_ONLY)); } } else { - extensions = new ObjectString[]{new ObjectString("All resources", "", ObjectString.FMT_STRING_ONLY)}; + extensions = new ArrayList<>(); + extensions.add(DataString.with("All resources", "", DataString.FMT_STRING_ONLY)); } updateResources(); @@ -218,7 +219,7 @@ protected void setExtensions(String[] extList) /** Returns a list of resource types defined for this dialog. */ protected String[] getExtensions() { - final String str = extensions[0].getObject().toString(); + final String str = extensions.get(0).getData(); if (!str.isEmpty()) { return str.split(";"); } @@ -273,8 +274,8 @@ private void cancel() private void updateResources() { resources.clear(); - for (final ObjectString extension : extensions) { - final String data = extension.getObject(); + for (final DataString extension : extensions) { + final String data = extension.getData(); final String[] types; if (data.isEmpty()) { types = Profile.getAvailableResourceTypes(); @@ -294,10 +295,10 @@ private void updateResources() /** Initializes type combobox. */ private void updateGui() { - DefaultComboBoxModel model = (DefaultComboBoxModel)cbType.getModel(); + DefaultComboBoxModel> model = (DefaultComboBoxModel>)cbType.getModel(); model.removeAllElements(); if (extensions != null) { - for (final ObjectString os: extensions) { + for (final DataString os: extensions) { model.addElement(os); } } diff --git a/src/org/infinity/gui/QuickSearch.java b/src/org/infinity/gui/QuickSearch.java index 5e51d3fb0..ef809105b 100644 --- a/src/org/infinity/gui/QuickSearch.java +++ b/src/org/infinity/gui/QuickSearch.java @@ -75,7 +75,7 @@ public QuickSearch(ButtonPopupWindow parent, ResourceTree tree) } this.parent = parent; this.tree = tree; - this.resourceTree = new MapTree>(Character.valueOf('\0'), null); + this.resourceTree = new MapTree<>(Character.valueOf('\0'), null); this.command = Command.IDLE; new Thread(this).start(); // updating list of matching resources is done in the background init(); @@ -256,7 +256,7 @@ private void generateRootNode() if (list != null) { list.clear(); } else { - list = new Vector(); + list = new Vector<>(); resourceTree.setValue(list); } @@ -274,7 +274,7 @@ private void generateRootNode() private SortedSet generateResourceList(ResourceTreeFolder folder, SortedSet set) { if (set == null) { - set = new TreeSet(); + set = new TreeSet<>(); } if (folder != null) { @@ -309,8 +309,7 @@ private MapTree> generateNode(MapTree()); } } else { - retVal = new MapTree>(Character.valueOf(ch), - new Vector()); + retVal = new MapTree<>(Character.valueOf(ch), new Vector()); } node.addChild(retVal); diff --git a/src/org/infinity/gui/RenderCanvas.java b/src/org/infinity/gui/RenderCanvas.java index 3185d8950..205cc0239 100644 --- a/src/org/infinity/gui/RenderCanvas.java +++ b/src/org/infinity/gui/RenderCanvas.java @@ -4,6 +4,8 @@ package org.infinity.gui; +import java.awt.AlphaComposite; +import java.awt.Composite; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; @@ -29,6 +31,7 @@ public class RenderCanvas extends JComponent implements SwingConstants private boolean isScaling, isAutoScale; private int scaledWidth, scaledHeight; private int verticalAlignment, horizontalAlignment; + private Composite composite; public RenderCanvas() { @@ -62,6 +65,7 @@ public RenderCanvas(Image image, boolean scaled, Object interpolationType, this.verticalAlignment = this.horizontalAlignment = CENTER; setInterpolationType(interpolationType); setScalingEnabled(scaled); + setComposite(null); setHorizontalAlignment(horizontalAlign); setVerticalAlignment(verticalAlign); setImage(image); @@ -294,6 +298,23 @@ public void setInterpolationType(Object interpolationType) } } + /** + * Returns the {@link Composite} object used to draw the image on the canvas. + */ + public Composite getComposite() + { + return composite; + } + + /** + * Sets the {@link Composite} object that is used to draw the image on the canvas. + * @param c the {@code Composite} object. Specify {@code null} to use a default composite object. + */ + public void setComposite(Composite c) + { + composite = (c != null) ? c : AlphaComposite.SrcOver; + } + protected void update() { @@ -345,6 +366,8 @@ protected void paintCanvas(Graphics g) { if (currentImage != null && currentImage.getWidth(null) > 0 && currentImage.getHeight(null) > 0) { Graphics2D g2 = (Graphics2D)g; + Composite oldComposite = g2.getComposite(); + g2.setComposite(getComposite()); Rectangle rect = getCanvasSize(); if (isScaling) { g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, interpolationType); @@ -352,6 +375,9 @@ protected void paintCanvas(Graphics g) } else { g2.drawImage(currentImage, rect.x, rect.y, null); } + if (oldComposite != null) { + g2.setComposite(oldComposite); + } } } diff --git a/src/org/infinity/gui/ResourceChooser.java b/src/org/infinity/gui/ResourceChooser.java index 32a3bdeb9..2b72bd30b 100644 --- a/src/org/infinity/gui/ResourceChooser.java +++ b/src/org/infinity/gui/ResourceChooser.java @@ -214,8 +214,7 @@ private void init(String initialExtension) private void resetResourceList(String ext) { - final List resources = (ext != null) ? ResourceFactory.getResources(ext) - : new ArrayList(); + final List resources = (ext != null) ? ResourceFactory.getResources(ext) : new ArrayList<>(); if (lpResources != null) { // switching type in existing list panel RootPaneContainer rpc = (RootPaneContainer)SwingUtilities.getAncestorOfClass(RootPaneContainer.class, this); diff --git a/src/org/infinity/gui/StringEditor.java b/src/org/infinity/gui/StringEditor.java index feade47bd..728b6f426 100644 --- a/src/org/infinity/gui/StringEditor.java +++ b/src/org/infinity/gui/StringEditor.java @@ -47,9 +47,8 @@ import javax.swing.filechooser.FileNameExtensionFilter; import org.infinity.NearInfinity; -import org.infinity.datatype.DecNumber; import org.infinity.datatype.Editable; -import org.infinity.datatype.Flag; +import org.infinity.datatype.IsNumeric; import org.infinity.datatype.ResourceRef; import org.infinity.icon.Icons; import org.infinity.resource.Profile; @@ -615,14 +614,14 @@ private void updateTableItem(int row) if (cellName != null && cellValue != null && entry != null) { String name = cellName.toString(); if (StringEditor.TLK_FLAGS.equals(name)) { - entry.setFlags((short)((Flag)cellValue).getValue()); + entry.setFlags((short)((IsNumeric)cellValue).getValue()); } else if (StringEditor.TLK_SOUND.equals(name)) { ResourceRef ref = (ResourceRef)cellValue; entry.setSoundRef(ref.isEmpty() ? "" : ref.getText()); } else if (StringEditor.TLK_VOLUME.equals(name)) { - entry.setVolume(((DecNumber)cellValue).getValue()); + entry.setVolume(((IsNumeric)cellValue).getValue()); } else if (StringEditor.TLK_PITCH.equals(name)) { - entry.setPitch(((DecNumber)cellValue).getValue()); + entry.setPitch(((IsNumeric)cellValue).getValue()); } updateModifiedUI(getSelectedDialogType()); } diff --git a/src/org/infinity/gui/StructCellEditor.java b/src/org/infinity/gui/StructCellEditor.java new file mode 100644 index 000000000..e9ead60a3 --- /dev/null +++ b/src/org/infinity/gui/StructCellEditor.java @@ -0,0 +1,258 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.gui; + +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.event.MouseEvent; +import java.util.EventObject; + +import javax.swing.AbstractCellEditor; +import javax.swing.JComponent; +import javax.swing.JTable; +import javax.swing.table.TableCellEditor; +import javax.swing.table.TableCellRenderer; + +import org.infinity.datatype.InlineEditable; + +/** + * A specialized cell renderer for the {@code StructViewer} table. + */ +public class StructCellEditor extends AbstractCellEditor implements TableCellEditor +{ + /** The delegate class which handles all methods sent from the {@code CellEditor} */ + protected final StructEditorDelegate delegate = new StructEditorDelegate(); + + /** The Swing component being edited. */ + protected InlineEditable editorComponent; + + /** + * An integer specifying the number of clicks needed to start editing. + * Even if {@codeclickCountToStart} is defined as zero, it will not initiate until a click occurs. + */ + protected int clickCountToStart; + + public StructCellEditor() + { + // use double-click to enable editor + clickCountToStart = 2; + } + + /** + * Returns a reference to the editor component. + * @return the editor Component + */ + public Component getComponent() + { + return (editorComponent != null) ? editorComponent.getEditor() : InlineEditable.DEFAULT_EDITOR; + } + + /** + * Specifies the number of clicks needed to start editing. + * @param count an int specifying the number of clicks needed to start editing + * @see #getClickCountToStart + */ + public void setClickCountToStart(int count) + { + clickCountToStart = count; + } + + /** + * Returns the number of clicks needed to start editing. + * @return the number of clicks needed to start editing + */ + public int getClickCountToStart() + { + return clickCountToStart; + } + + /** + * Forwards the message from the {@code CellEditor} to the {@code delegate}. + * @see StructEditorDelegate#getCellEditorValue + */ + @Override + public Object getCellEditorValue() + { + return delegate.getCellEditorValue(); + } + + /** + * Forwards the message from the {@code CellEditor} to the {@code delegate}. + * @see StructEditorDelegate#isCellEditable(EventObject) + */ + @Override + public boolean isCellEditable(EventObject anEvent) + { + return delegate.isCellEditable(anEvent); + } + + /** + * Forwards the message from the {@code CellEditor} to the {@code delegate}. + * @see StructEditorDelegate#shouldSelectCell(EventObject) + */ + @Override + public boolean shouldSelectCell(EventObject anEvent) + { + return delegate.shouldSelectCell(anEvent); + } + + /** + * Forwards the message from the {@code CellEditor} to the {@code delegate}. + * @see StructEditorDelegate#stopCellEditing + */ + @Override + public boolean stopCellEditing() + { + return delegate.stopCellEditing(); + } + + /** + * Forwards the message from the {@code CellEditor} to the {@code delegate}. + * @see StructEditorDelegate#cancelCellEditing + */ + @Override + public void cancelCellEditing() + { + delegate.cancelCellEditing(); + } + + @Override + public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) + { + if (value instanceof InlineEditable) { + editorComponent = (InlineEditable)value; + } + JComponent comp = (editorComponent != null) ? editorComponent.getEditor() : InlineEditable.DEFAULT_EDITOR; + + delegate.setValue(value); + + if (!comp.isOpaque()) { +// if (comp instanceof JCheckBox) { + //in order to avoid a "flashing" effect when clicking a checkbox + //in a table, it is important for the editor to have as a border + //the same border that the renderer has, and have as the background + //the same color as the renderer has. This is primarily only + //needed for JCheckBox since this editor doesn't fill all the + //visual space of the table cell, unlike a text field. + TableCellRenderer renderer = table.getCellRenderer(row, column); + Component c = renderer.getTableCellRendererComponent(table, value, isSelected, true, row, column); + if (c != null) { + comp.setOpaque(true); + comp.setBackground(c.getBackground()); + if (c instanceof JComponent) { + comp.setBorder(((JComponent)c).getBorder()); + } + } else { + comp.setOpaque(false); + } + } + + return comp; + } + +//-------------------------- INNER CLASSES -------------------------- + + protected class StructEditorDelegate implements ActionListener, ItemListener + { + /** + * Returns the value of this cell. + * @return the value of this cell + */ + public Object getCellEditorValue() + { + return editorComponent.getEditorValue(); + } + + /** + * Sets the value of this cell. + * @param value the new value of this cell + */ + public void setValue(Object value) + { + editorComponent.setEditorValue(value); + } + + /** + * Returns {@code true} if {@code anEvent} is not a {@code MouseEvent}. + * Otherwise, it returns {@code true} if the necessary number of clicks have occurred, + * and returns {@code false} otherwise. + * @param anEvent the event + * @return {@code true} if cell is ready for editing, {@code false} otherwise + * @see #setClickCountToStart + * @see #shouldSelectCell + */ + public boolean isCellEditable(EventObject anEvent) + { + if (anEvent instanceof MouseEvent) { + return ((MouseEvent)anEvent).getClickCount() >= clickCountToStart; + } + return true; + } + + /** + * Returns true to indicate that the editing cell may be selected. + * @param anEvent the event + * @return true + * @see #isCellEditable + */ + public boolean shouldSelectCell(EventObject anEvent) + { + return true; + } + + /** + * Returns true to indicate that editing has begun. + * @param anEvent the event + */ + public boolean startCellEditing(EventObject anEvent) + { + return true; + } + + /** + * Stops editing and returns true to indicate that editing has stopped. + * This method calls fireEditingStopped. + * @return true + */ + public boolean stopCellEditing() + { + fireEditingStopped(); + return true; + } + + /** + * Cancels editing. This method calls fireEditingCanceled. + */ + public void cancelCellEditing() + { + fireEditingCanceled(); + } + + /** + * When an action is performed, editing is ended. + * @param e the action event + * @see #stopCellEditing + */ + @Override + public void actionPerformed(ActionEvent e) + { + StructCellEditor.this.stopCellEditing(); + } + + /** + * When an item's state changes, editing is ended. + * @param e the action event + * @see #stopCellEditing + */ + @Override + public void itemStateChanged(ItemEvent e) + { + StructCellEditor.this.stopCellEditing(); + } + } +} diff --git a/src/org/infinity/gui/StructViewer.java b/src/org/infinity/gui/StructViewer.java index 70fdb85c0..197e25638 100644 --- a/src/org/infinity/gui/StructViewer.java +++ b/src/org/infinity/gui/StructViewer.java @@ -101,10 +101,10 @@ 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; import org.infinity.util.io.ByteBufferOutputStream; import org.infinity.util.io.StreamUtils; +import org.infinity.util.tuples.Couple; public final class StructViewer extends JPanel implements ListSelectionListener, ActionListener, ItemListener, ChangeListener, TableModelListener, @@ -175,16 +175,16 @@ public final class StructViewer extends JPanel implements ListSelectionListener, private final ButtonPanel buttonPanel = new ButtonPanel(); private final JPopupMenu popupmenu = new JPopupMenu(); private final InfinityTextArea tatext = new InfinityTextArea(true); - private final StructTable table = new StructTable(); private final HashMap entryMap = new HashMap<>(); private final HashMap viewMap = new HashMap<>(); + private final StructTable table; private JMenuItem miFindAttribute, miFindReferences, miFindStateReferences, miFindRefToItem; private Editable editable; private JTabbedPane tabbedPane; private JSplitPane splitv; private boolean splitterSet; private int oldSplitterHeight; - private Pair storedSelection; + private Couple storedSelection; private static JMenuItem createMenuItem(String cmd, String text, Icon icon, ActionListener l) { @@ -220,7 +220,8 @@ private static JMenu createResrefMenu(String cmd, String text, String[] types, I public StructViewer(AbstractStruct struct) { this.struct = struct; - struct.addTableModelListener(this); + this.struct.addTableModelListener(this); + this.table = new StructTable(this.struct); table.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3)); table.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); table.getSelectionModel().addListSelectionListener(this); @@ -274,6 +275,7 @@ else if (fieldColors.containsKey(field.getClass())) // consider only referenced return this; } }); + table.setDefaultEditor(Object.class, new StructCellEditor()); popupmenu.add(miCopyValue); popupmenu.add(miPasteValue); @@ -799,7 +801,7 @@ public void valueChanged(ListSelectionEvent event) selected instanceof TextBitmap) && !(selected instanceof AbstractCode)); miReset.setEnabled(isDataType && isReadable && - getCachedStructEntry(((Datatype)selected).getOffset()) instanceof Readable && + getCachedStructEntry(((Datatype)selected).getOffset()) != null && !(selected instanceof AbstractCode)); miAddToAdvSearch.setEnabled(!(selected instanceof AbstractStruct || selected instanceof Unknown)); miGotoOffset.setEnabled(selected instanceof SectionOffset|| selected instanceof SectionCount); @@ -920,7 +922,7 @@ public void storeCurrentSelection() min = Math.min(min, idx); max = Math.max(max, idx); } - storedSelection = new Pair(min, max); + storedSelection = Couple.with(min, max); } } } @@ -929,7 +931,7 @@ public void storeCurrentSelection() public void restoreCurrentSelection() { if (storedSelection != null) { - table.setRowSelectionInterval(storedSelection.getFirst(), storedSelection.getSecond()); + table.setRowSelectionInterval(storedSelection.getValue0(), storedSelection.getValue1()); storedSelection = null; } } @@ -1249,7 +1251,7 @@ private void convertAttribute(int index, JMenuItem menuitem) newentry = new TextString(bb, 0, entry.getSize(), entry.getName()); } else if (menuitem == miReset) { newentry = removeCachedStructEntry(entry.getOffset()); - if (newentry == null || !(newentry instanceof Readable)) { + if (newentry == null) { newentry = entry; } else { ((Readable)newentry).read(bb, 0); @@ -1543,10 +1545,13 @@ private void maybeShowPopup(MouseEvent e) } } - private final class StructTable extends JTable implements Printable + private static final class StructTable extends JTable implements Printable { - private StructTable() + private final AbstractStruct struct; + + private StructTable(AbstractStruct struct) { + this.struct = struct; } @Override diff --git a/src/org/infinity/gui/TileGrid.java b/src/org/infinity/gui/TileGrid.java index 9910985e9..43eab863b 100644 --- a/src/org/infinity/gui/TileGrid.java +++ b/src/org/infinity/gui/TileGrid.java @@ -229,7 +229,7 @@ public Image getImage(int index) throws IndexOutOfBoundsException */ public List getImageList() { - return new ArrayList(imageList); + return new ArrayList<>(imageList); } /** @@ -608,8 +608,8 @@ private void init(int rows, int cols, int tw, int th) this.tileWidth = Math.max(tw, 1); this.tileHeight = Math.max(th, 1); this.bgColor = null; - this.imageList = new ArrayList(1); - this.tileList = new ArrayList(); + this.imageList = new ArrayList<>(1); + this.tileList = new ArrayList<>(); this.tileBorder = null; this.bShowGrid = false; this.bShowIcons = true; diff --git a/src/org/infinity/gui/ViewerUtil.java b/src/org/infinity/gui/ViewerUtil.java index b25b0dfa3..d9b6cf459 100644 --- a/src/org/infinity/gui/ViewerUtil.java +++ b/src/org/infinity/gui/ViewerUtil.java @@ -55,10 +55,10 @@ import org.infinity.resource.StructEntry; import org.infinity.resource.Viewable; import org.infinity.resource.graphics.BamDecoder; +import org.infinity.resource.graphics.BamDecoder.BamControl; import org.infinity.resource.graphics.BamResource; import org.infinity.resource.graphics.GraphicsResource; import org.infinity.resource.graphics.MosResource; -import org.infinity.resource.graphics.BamDecoder.BamControl; import org.infinity.resource.key.ResourceEntry; import org.infinity.util.Misc; import org.infinity.util.SimpleListModel; @@ -308,7 +308,23 @@ public static JPanel makeListPanel(String title, AbstractStruct struct, return new StructListPanel(title, struct, listClass, attrName, renderer, listener); } + /** + * Creates a panel with a text area control and a title with the {@code StructEntry} name. + * @param entry the {@code StructEntry} instance used to derive data and title from. + * @return a {@code JPanel} instance. + */ public static JPanel makeTextAreaPanel(StructEntry entry) + { + return makeTextAreaPanel(entry, true); + } + + /** + * Creates a panel with a text area control and an optional title with the {@code StructEntry} name. + * @param entry the {@code StructEntry} instance used to derive data and title from. + * @param showTitle whether to show the entry title. + * @return a {@code JPanel} instance. + */ + public static JPanel makeTextAreaPanel(StructEntry entry, boolean showTitle) { String text; if (entry instanceof StringRef) { @@ -329,7 +345,9 @@ public static JPanel makeTextAreaPanel(StructEntry entry) scroll.setLineNumbersEnabled(false); ta.setMargin(new Insets(3, 3, 3, 3)); JPanel panel = new JPanel(new BorderLayout()); - panel.add(new JLabel(entry.getName()), BorderLayout.NORTH); + if (showTitle) { + panel.add(new JLabel(entry.getName()), BorderLayout.NORTH); + } panel.add(scroll, BorderLayout.CENTER); panel.setPreferredSize(new Dimension(5, 5)); return panel; @@ -387,7 +405,7 @@ public static final class StructListPanel extends JPanel implements TableModelLi private final AbstractStruct struct; private final Class listClass; private final JList list; - private final SimpleListModel listModel = new SimpleListModel(); + private final SimpleListModel listModel = new SimpleListModel<>(); private final JButton bOpen = new JButton("View/Edit", Icons.getIcon(Icons.ICON_ZOOM_16)); private StructListPanel(String title, AbstractStruct struct, diff --git a/src/org/infinity/gui/WindowBlocker.java b/src/org/infinity/gui/WindowBlocker.java index afea324ce..eef3bd38c 100644 --- a/src/org/infinity/gui/WindowBlocker.java +++ b/src/org/infinity/gui/WindowBlocker.java @@ -61,7 +61,9 @@ public WindowBlocker(RootPaneContainer window) public void setBlocked(boolean blocked) { if (glassPane == null) return; - glassPane.setVisible(blocked); + if (blocked != glassPane.isVisible()) { + glassPane.setVisible(blocked); + } } } diff --git a/src/org/infinity/gui/converter/BamFilterBase.java b/src/org/infinity/gui/converter/BamFilterBase.java index 966985966..9ec457448 100644 --- a/src/org/infinity/gui/converter/BamFilterBase.java +++ b/src/org/infinity/gui/converter/BamFilterBase.java @@ -31,7 +31,7 @@ public enum Type { private final ConvertToBam converter; private final String name, description; private final Type type; - private final List listChangeListeners = new ArrayList(); + private final List listChangeListeners = new ArrayList<>(); private final JPanel controls; protected BamFilterBase(ConvertToBam converter, String name, String desc, Type type) diff --git a/src/org/infinity/gui/converter/BamFilterBaseColor.java b/src/org/infinity/gui/converter/BamFilterBaseColor.java index 1ded3c02b..621e413d2 100644 --- a/src/org/infinity/gui/converter/BamFilterBaseColor.java +++ b/src/org/infinity/gui/converter/BamFilterBaseColor.java @@ -23,9 +23,9 @@ import javax.swing.event.ChangeListener; import org.infinity.gui.ColorGrid; -import org.infinity.gui.ViewerUtil; import org.infinity.gui.ColorGrid.MouseOverEvent; import org.infinity.gui.ColorGrid.MouseOverListener; +import org.infinity.gui.ViewerUtil; import org.infinity.util.Misc; /** @@ -105,7 +105,7 @@ public static class ExcludeColorsPanel extends JPanel } } - private final List listChangeListeners = new ArrayList(); + private final List listChangeListeners = new ArrayList<>(); private ColorGrid cgPalette; private JLabel lInfoIndex, lInfoRGB, lInfoHexRGB; diff --git a/src/org/infinity/gui/converter/BamFilterColorLab.java b/src/org/infinity/gui/converter/BamFilterColorLab.java new file mode 100644 index 000000000..6e35b2cd4 --- /dev/null +++ b/src/org/infinity/gui/converter/BamFilterColorLab.java @@ -0,0 +1,462 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2005 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.gui.converter; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.image.BufferedImage; +import java.awt.image.DataBuffer; +import java.awt.image.DataBufferByte; +import java.awt.image.DataBufferInt; +import java.awt.image.IndexColorModel; + +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSlider; +import javax.swing.JSpinner; +import javax.swing.SpinnerNumberModel; +import javax.swing.SwingConstants; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import org.infinity.gui.ButtonPopupWindow; +import org.infinity.gui.ViewerUtil; +import org.infinity.icon.Icons; +import org.infinity.resource.graphics.ColorConvert; +import org.infinity.resource.graphics.PseudoBamDecoder.PseudoBamFrameEntry; +import org.infinity.util.tuples.Triple; + +/** + * ColorFilter: adjust CIELAB values L, a and b. + */ +public class BamFilterColorLab extends BamFilterBaseColor + implements ChangeListener, ActionListener +{ + private static final String FilterName = "CIELAB (L/a/b)"; + private static final String FilterDesc = "This filter provides controls for adjusting values in the CIELAB color space"; + + private static final int LAB_L_MIN = -127; + private static final int LAB_L_MAX = 127; + private static final int LAB_A_MIN = -255; + private static final int LAB_A_MAX = 255; + private static final int LAB_B_MIN = -255; + private static final int LAB_B_MAX = 255; + + private JSlider sliderL, sliderA, sliderB; + private JSpinner spinnerL, spinnerA, spinnerB; + private ButtonPopupWindow bpwExclude; + private BamFilterBaseColor.ExcludeColorsPanel pExcludeColors; + + public static String getFilterName() { return FilterName; } + public static String getFilterDesc() { return FilterDesc; } + + public BamFilterColorLab(ConvertToBam parent) + { + super(parent, FilterName, FilterDesc); + } + + @Override + public BufferedImage process(BufferedImage frame) throws Exception + { + return applyEffect(frame); + } + + @Override + public PseudoBamFrameEntry updatePreview(PseudoBamFrameEntry entry) + { + if (entry != null) { + entry.setFrame(applyEffect(entry.getFrame())); + } + return entry; + } + + @Override + public void updateControls() + { + bpwExclude.setEnabled(getConverter().isBamV1Selected()); + } + + @Override + public String getConfiguration() + { + StringBuilder sb = new StringBuilder(); + sb.append(sliderL.getValue()).append(';'); + sb.append(sliderA.getValue()).append(';'); + sb.append(sliderB.getValue()).append(';'); + sb.append(encodeColorList(pExcludeColors.getSelectedIndices())); + return sb.toString(); + } + + @Override + public boolean setConfiguration(String config) + { + if (config != null) { + config = config.trim(); + if (!config.isEmpty()) { + String[] params = config.trim().split(";"); + Integer lValue = Integer.MIN_VALUE; + Integer aValue = Integer.MIN_VALUE; + Integer bValue = Integer.MIN_VALUE; + int[] indices = null; + + // parsing configuration data + if (params.length > 0) { // set L value + lValue = decodeNumber(params[0], sliderL.getMinimum(), sliderL.getMaximum(), Integer.MIN_VALUE); + if (lValue == Integer.MIN_VALUE) { + return false; + } + } + if (params.length > 1) { // set A value + aValue = decodeNumber(params[1], sliderA.getMinimum(), sliderA.getMaximum(), Integer.MIN_VALUE); + if (aValue == Integer.MIN_VALUE) { + return false; + } + } + if (params.length > 2) { // set B value + bValue = decodeNumber(params[2], sliderB.getMinimum(), sliderB.getMaximum(), Integer.MIN_VALUE); + if (bValue == Integer.MIN_VALUE) { + return false; + } + } + if (params.length > 3) { + indices = decodeColorList(params[3]); + if (indices == null) { + return false; + } + } + + // applying configuration data + if (lValue != Integer.MIN_VALUE) { + sliderL.setValue(lValue); + } + if (aValue != Integer.MIN_VALUE) { + sliderA.setValue(aValue); + } + if (bValue != Integer.MIN_VALUE) { + sliderB.setValue(bValue); + } + if (indices != null) { + pExcludeColors.setSelectedIndices(indices); + } + } + return true; + } + return false; + } + + @Override + protected JPanel loadControls() + { + GridBagConstraints c = new GridBagConstraints(); + + JLabel l1 = new JLabel("Exclude colors:"); + pExcludeColors = new BamFilterBaseColor.ExcludeColorsPanel( + getConverter().getPaletteDialog().getPalette(getConverter().getPaletteDialog().getPaletteType())); + pExcludeColors.addChangeListener(this); + bpwExclude = new ButtonPopupWindow("Palette", Icons.getIcon(Icons.ICON_ARROW_DOWN_15), pExcludeColors); + bpwExclude.setIconTextGap(8); + bpwExclude.addActionListener(this); + bpwExclude.setEnabled(getConverter().isBamV1Selected()); + JPanel pExclude = new JPanel(new GridBagLayout()); + ViewerUtil.setGBC(c, 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0); + pExclude.add(l1, c); + ViewerUtil.setGBC(c, 1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.NONE, new Insets(0, 8, 0, 0), 0, 0); + pExclude.add(bpwExclude, c); + ViewerUtil.setGBC(c, 2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.NONE, new Insets(0, 4, 0, 0), 0, 0); + pExclude.add(new JPanel(), c); + + JLabel lh = new JLabel("L:"); + JLabel ls = new JLabel("a:"); + JLabel ll = new JLabel("b:"); + sliderL = new JSlider(SwingConstants.HORIZONTAL, LAB_L_MIN, LAB_L_MAX, 0); + sliderL.addChangeListener(this); + sliderA = new JSlider(SwingConstants.HORIZONTAL, LAB_A_MIN, LAB_A_MAX, 0); + sliderA.addChangeListener(this); + sliderB = new JSlider(SwingConstants.HORIZONTAL, LAB_B_MIN, LAB_B_MAX, 0); + sliderB.addChangeListener(this); + spinnerL = new JSpinner(new SpinnerNumberModel(sliderL.getValue(), + sliderL.getMinimum(), + sliderL.getMaximum(), 1)); + spinnerL.addChangeListener(this); + spinnerA = new JSpinner(new SpinnerNumberModel(sliderA.getValue(), + sliderA.getMinimum(), + sliderA.getMaximum(), 1)); + spinnerA.addChangeListener(this); + spinnerB = new JSpinner(new SpinnerNumberModel(sliderB.getValue(), + sliderB.getMinimum() , + sliderB.getMaximum(), 1)); + spinnerB.addChangeListener(this); + + JPanel p = new JPanel(new GridBagLayout()); + ViewerUtil.setGBC(c, 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0); + p.add(lh, c); + ViewerUtil.setGBC(c, 1, 0, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(0, 4, 0, 0), 0, 0); + p.add(sliderL, c); + ViewerUtil.setGBC(c, 2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(0, 4, 0, 0), 0, 0); + p.add(spinnerL, c); + + ViewerUtil.setGBC(c, 0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.NONE, new Insets(4, 0, 0, 0), 0, 0); + p.add(ls, c); + ViewerUtil.setGBC(c, 1, 1, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(4, 4, 0, 0), 0, 0); + p.add(sliderA, c); + ViewerUtil.setGBC(c, 2, 1, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(4, 4, 0, 0), 0, 0); + p.add(spinnerA, c); + + ViewerUtil.setGBC(c, 0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.NONE, new Insets(4, 0, 0, 0), 0, 0); + p.add(ll, c); + ViewerUtil.setGBC(c, 1, 2, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(4, 4, 0, 0), 0, 0); + p.add(sliderB, c); + ViewerUtil.setGBC(c, 2, 2, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(4, 4, 0, 0), 0, 0); + p.add(spinnerB, c); + ViewerUtil.setGBC(c, 0, 3, 3, 1, 0.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.NONE, new Insets(8, 0, 0, 0), 0, 0); + p.add(pExclude, c); + + JPanel panel = new JPanel(new GridBagLayout()); + ViewerUtil.setGBC(c, 0, 0, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, + GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); + panel.add(p, c); + + return panel; + } + +//--------------------- Begin Interface ChangeListener --------------------- + + @Override + public void stateChanged(ChangeEvent event) + { + if (event.getSource() == pExcludeColors) { + fireChangeListener(); + } else if (event.getSource() == sliderL) { + spinnerL.setValue(Integer.valueOf(sliderL.getValue())); + if (sliderL.getModel().getValueIsAdjusting() == false) { + fireChangeListener(); + } + } else if (event.getSource() == sliderA) { + spinnerA.setValue(Integer.valueOf(sliderA.getValue())); + if (sliderA.getModel().getValueIsAdjusting() == false) { + fireChangeListener(); + } + } else if (event.getSource() == sliderB) { + spinnerB.setValue(Integer.valueOf(sliderB.getValue())); + if (sliderB.getModel().getValueIsAdjusting() == false) { + fireChangeListener(); + } + } else if (event.getSource() == spinnerL) { + sliderL.setValue(((Integer)spinnerL.getValue()).intValue()); + } else if (event.getSource() == spinnerA) { + sliderA.setValue(((Integer)spinnerA.getValue()).intValue()); + } else if (event.getSource() == spinnerB) { + sliderB.setValue(((Integer)spinnerB.getValue()).intValue()); + } + } + +//--------------------- End Interface ChangeListener --------------------- + +//--------------------- Begin Interface ActionListener --------------------- + + @Override + public void actionPerformed(ActionEvent event) + { + if (event.getSource() == bpwExclude) { + pExcludeColors.updatePalette(getConverter().getPaletteDialog().getPalette( + getConverter().getPaletteDialog().getPaletteType())); + } + } + +//--------------------- End Interface ActionListener --------------------- + + private BufferedImage applyEffect(BufferedImage srcImage) + { + if (srcImage != null) { + int[] buffer; + IndexColorModel cm = null; + boolean isPremultiplied = false; + if (srcImage.getType() == BufferedImage.TYPE_BYTE_INDEXED) { + // paletted image + cm = (IndexColorModel)srcImage.getColorModel(); + buffer = new int[1 << cm.getPixelSize()]; + cm.getRGBs(buffer); + isPremultiplied = cm.isAlphaPremultiplied(); + // applying proper alpha + if (!cm.hasAlpha()) { + final int Green = 0x0000ff00; + boolean greenFound = false; + for (int i = 0; i < buffer.length; i++) { + if (!greenFound && buffer[i] == Green) { + greenFound = true; + buffer[i] &= 0x00ffffff; + } else { + buffer[i] |= 0xff000000; + } + } + } + } else if (srcImage.getRaster().getDataBuffer().getDataType() == DataBuffer.TYPE_INT) { + // truecolor image + buffer = ((DataBufferInt)srcImage.getRaster().getDataBuffer()).getData(); + isPremultiplied = srcImage.isAlphaPremultiplied(); + } else { + buffer = new int[0]; + } + + // L hue in range [0, 100] + double labL = ((Integer)spinnerL.getValue()).doubleValue(); + // a in range [-160, 160] + double labA = ((Integer)spinnerA.getValue()).doubleValue(); + // b in range [-160, 160] + double labB = ((Integer)spinnerB.getValue()).doubleValue(); + + for (int i = 0; i < buffer.length; i++) { + if ((cm == null || (cm != null && !pExcludeColors.isSelectedIndex(i))) && + (buffer[i] & 0xff000000) != 0) { + // convert RGB -> Lab + int fa = isPremultiplied ? (buffer[i] >>> 24) & 0xff : 255; + int fr = ((buffer[i] >>> 16) & 0xff) * fa / 255; + int fg = ((buffer[i] >>> 8) & 0xff) * fa / 255; + int fb = (buffer[i] & 0xff) * fa / 255; + int rgb = (fr << 16) | (fg << 8) | fb; + Triple lab = ColorConvert.convertRGBtoLab(rgb); + double l = lab.getValue0().doubleValue(); + double a = lab.getValue1().doubleValue(); + double b = lab.getValue2().doubleValue(); + +// float fa = isPremultiplied ? (float)((buffer[i] >>> 24) & 0xff) : 255.0f; +// float fr = (float)((buffer[i] >>> 16) & 0xff) / fa; +// float fg = (float)((buffer[i] >>> 8) & 0xff) / fa; +// float fb = (float)(buffer[i] & 0xff) / fa; +// float cmin = fr; if (fg < cmin) cmin = fg; if (fb < cmin) cmin = fb; +// float cmax = fr; if (fg > cmax) cmax = fg; if (fb > cmax) cmax = fb; +// float cdelta = cmax - cmin; +// float cdelta2 = cdelta / 2.0f; +// float h, s, l; +// +// l = (cmax + cmin) / 2.0f; +// +// if (cdelta == 0.0f) { +// h = 0.0f; +// s = 0.0f; +// } else { +// if (l < 0.5f) { +// s = cdelta / (cmax + cmin); +// } else { +// s = cdelta / (2.0f - cmax - cmin); +// } +// +// float dr = (((cmax - fr) / 6.0f) + cdelta2) / cdelta; +// float dg = (((cmax - fg) / 6.0f) + cdelta2) / cdelta; +// float db = (((cmax - fb) / 6.0f) + cdelta2) / cdelta; +// +// if (fr == cmax) { +// h = db - dg; +// } else if (fg == cmax) { +// h = (1.0f / 3.0f) + dr - db; +// } else { +// h = (2.0f / 3.0f) + dg - dr; +// } +// +// if (h < 0.0f) h += 1.0f; else if (h > 1.0f) h -= 1.0f; +// } + + // applying adjustments + + l = Math.max((double)LAB_L_MIN, Math.min((double)LAB_L_MAX, l + labL)); + a = Math.max((double)LAB_A_MIN, Math.min((double)LAB_A_MAX, a + labA)); + b = Math.max((double)LAB_B_MIN, Math.min((double)LAB_B_MAX, b + labB)); + + // converting Lab -> RGB + rgb = ColorConvert.convertLabToRGB(l, a, b); + buffer[i] = (buffer[i] & 0xff000000) | rgb; + +// if (s == 0.0f) { +// // achromatic +// int v = (int)(l * 255.0f); +// buffer[i] = (buffer[i] & 0xff000000) | (v << 16) | (v << 8) | v; +// } else { +// float f2 = (l < 0.5f) ? l * (1.0f + s) : (l + s) - (s * l); +// float f1 = 2.0f * l - f2; +// float res; +// +// // red +// float t = h + (1.0f / 3.0f); +// if (t < 0.0f) t += 1.0f; else if (t > 1.0f) t -= 1.0f; +// if ((6.0f * t) < 1.0f) { +// res = f1 + (f2 - f1) * 6.0f * t; +// } else if ((2.0f * t) < 1.0f) { +// res = f2; +// } else if ((3.0f * t) < 2.0f) { +// res = f1 + (f2 - f1) * ((2.0f / 3.0f) - t) * 6.0f; +// } else { +// res = f1; +// } +// int r = (int)(res * fa); +// +// // green +// t = h; +// if ((6.0f * t) < 1.0f) { +// res = f1 + (f2 - f1) * 6.0f * t; +// } else if ((2.0f * t) < 1.0f) { +// res = f2; +// } else if ((3.0f * t) < 2.0f) { +// res = f1 + (f2 - f1) * ((2.0f / 3.0f) - t) * 6.0f; +// } else { +// res = f1; +// } +// int g = (int)(res * fa); +// +// // blue +// t = h - (1.0f / 3.0f); +// if (t < 0.0f) t += 1.0f; else if (t > 1.0f) t -= 1.0f; +// if ((6.0f * t) < 1.0f) { +// res = f1 + (f2 - f1) * 6.0f * t; +// } else if ((2.0f * t) < 1.0f) { +// res = f2; +// } else if ((3.0f * t) < 2.0f) { +// res = f1 + (f2 - f1) * ((2.0f / 3.0f) - t) * 6.0f; +// } else { +// res = f1; +// } +// int b = (int)(res * fa); +// +// if (r < 0) r = 0; else if (r > 255) r = 255; +// if (g < 0) g = 0; else if (g > 255) g = 255; +// if (b < 0) b = 0; else if (b > 255) b = 255; +// buffer[i] = (buffer[i] & 0xff000000) | (r << 16) | (g << 8) | b; +// } + } + } + + if (cm != null) { + // recreating paletted image + IndexColorModel cm2 = new IndexColorModel(cm.getPixelSize(), buffer.length, buffer, 0, + cm.hasAlpha(), cm.getTransparentPixel(), DataBuffer.TYPE_BYTE); + int width = srcImage.getWidth(); + int height = srcImage.getHeight(); + BufferedImage dstImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_INDEXED, cm2); + byte[] srcPixels = ((DataBufferByte)srcImage.getRaster().getDataBuffer()).getData(); + byte[] dstPixels = ((DataBufferByte)dstImage.getRaster().getDataBuffer()).getData(); + System.arraycopy(srcPixels, 0, dstPixels, 0, srcPixels.length); + srcImage = dstImage; + srcPixels = null; dstPixels = null; + } + } + + return srcImage; + } +} diff --git a/src/org/infinity/gui/converter/BamFilterFactory.java b/src/org/infinity/gui/converter/BamFilterFactory.java index a46ce83f8..b0633dfa8 100644 --- a/src/org/infinity/gui/converter/BamFilterFactory.java +++ b/src/org/infinity/gui/converter/BamFilterFactory.java @@ -11,7 +11,7 @@ public class BamFilterFactory { - private static final List FilterInfoList = new ArrayList(); + private static final List FilterInfoList = new ArrayList<>(); static { // Registering individual BAM filters @@ -21,6 +21,9 @@ public class BamFilterFactory FilterInfoList.add(new FilterInfo(BamFilterColorHSL.getFilterName(), BamFilterColorHSL.getFilterDesc(), BamFilterColorHSL.class)); + FilterInfoList.add(new FilterInfo(BamFilterColorLab.getFilterName(), + BamFilterColorLab.getFilterDesc(), + BamFilterColorLab.class)); FilterInfoList.add(new FilterInfo(BamFilterColorBalance.getFilterName(), BamFilterColorBalance.getFilterDesc(), BamFilterColorBalance.class)); @@ -98,7 +101,7 @@ public static FilterInfo getFilterInfo(String filterName) /** Returns a list of all class types compatible with the specified class. */ public static Collection> getFiltersOf(Class classType) { - Collection> retVal = new ArrayList>(); + Collection> retVal = new ArrayList<>(); if (classType != null) { for (int i = 0; i < FilterInfoList.size(); i++) { if (classType.isAssignableFrom(FilterInfoList.get(i).getFilterClass())) { @@ -133,8 +136,8 @@ public static BamFilterBase createInstance(ConvertToBam parent, Class normalizeFilterList(ConvertToBam parent, List filterList) { - List retList = new ArrayList(); - List tmpList = new ArrayList(); + List retList = new ArrayList<>(); + List tmpList = new ArrayList<>(); if (filterList != null) { for (int i = 0; i < filterList.size(); i++) { if (filterList.get(i) instanceof BamFilterBaseOutput) { diff --git a/src/org/infinity/gui/converter/BamFilterOutputImage.java b/src/org/infinity/gui/converter/BamFilterOutputImage.java index 5a49c1b39..1c106b9a5 100644 --- a/src/org/infinity/gui/converter/BamFilterOutputImage.java +++ b/src/org/infinity/gui/converter/BamFilterOutputImage.java @@ -133,7 +133,7 @@ protected JPanel loadControls() JLabel l1 = new JLabel("Image output format:"); JLabel l2 = new JLabel("Digits for frame index:"); - cbImageType = new JComboBox(new String[]{"PNG", "BMP"}); + cbImageType = new JComboBox<>(new String[]{"PNG", "BMP"}); cbImageType.setEditable(false); cbImageType.setPreferredSize(new Dimension(cbImageType.getPreferredSize().width + 16, cbImageType.getPreferredSize().height)); cbImageType.setSelectedIndex(0); diff --git a/src/org/infinity/gui/converter/BamFilterOutputSplitted.java b/src/org/infinity/gui/converter/BamFilterOutputSplitted.java index ac2d4a26f..ba4e10035 100644 --- a/src/org/infinity/gui/converter/BamFilterOutputSplitted.java +++ b/src/org/infinity/gui/converter/BamFilterOutputSplitted.java @@ -340,7 +340,7 @@ private boolean applyEffect(PseudoBamDecoder decoder) throws Exception } // calculating individual splits for each frame - List> listSegments = new ArrayList>(decoder.frameCount()); + List> listSegments = new ArrayList<>(decoder.frameCount()); int segmentCount = segmentsX*segmentsY; for (int frameIdx = 0; frameIdx < decoder.frameCount(); frameIdx++) { listSegments.add(new ArrayList(segmentCount)); @@ -386,7 +386,7 @@ private boolean applyEffect(PseudoBamDecoder decoder) throws Exception // for each segment... for (int segIdx = 0; segIdx < segmentCount; segIdx++) { // creating segmented frames list - List framesList = new ArrayList(decoder.getFramesList().size()); + List framesList = new ArrayList<>(decoder.getFramesList().size()); for (int i = 0; i < listSegments.size(); i++) { framesList.add(createFrameSegment(decoder.getFramesList().get(i), listSegments.get(i).get(segIdx))); } diff --git a/src/org/infinity/gui/converter/BamOptionsDialog.java b/src/org/infinity/gui/converter/BamOptionsDialog.java index e7b7eb4fd..4a9efcb27 100644 --- a/src/org/infinity/gui/converter/BamOptionsDialog.java +++ b/src/org/infinity/gui/converter/BamOptionsDialog.java @@ -418,7 +418,7 @@ private void init() cbUseAlpha.addItemListener(this); cbUseAlpha.setSelectedIndex(getUseAlpha()); itemStateChanged(new ItemEvent(cbUseAlpha, ItemEvent.ITEM_FIRST, cbUseAlpha.getSelectedItem(), ItemEvent.SELECTED)); - cbSortPalette = new JComboBox(ColorConvert.SortType.values()); + cbSortPalette = new JComboBox<>(ColorConvert.SortType.values()); cbSortPalette.setSelectedItem(getSortPalette()); cbCompressBam = new JCheckBox("Select \"Compress BAM\" by default", getCompressBam()); JPanel pBamV1 = new JPanel(new GridBagLayout()); diff --git a/src/org/infinity/gui/converter/BamPaletteDialog.java b/src/org/infinity/gui/converter/BamPaletteDialog.java index fcd8e54f9..869853ee3 100644 --- a/src/org/infinity/gui/converter/BamPaletteDialog.java +++ b/src/org/infinity/gui/converter/BamPaletteDialog.java @@ -69,7 +69,7 @@ class BamPaletteDialog extends JDialog private static final String FmtInfoHexRGB = "#%02X%02X%02X%02X"; // Stores all available color values of the current BAM and their number of occurence for faster palette creation - private final LinkedHashMap colorMap = new LinkedHashMap(); + private final LinkedHashMap colorMap = new LinkedHashMap<>(); private final int[][] palettes = new int[2][]; private ConvertToBam converter; diff --git a/src/org/infinity/gui/converter/ConvertToBam.java b/src/org/infinity/gui/converter/ConvertToBam.java index 5af69c6b5..485236861 100644 --- a/src/org/infinity/gui/converter/ConvertToBam.java +++ b/src/org/infinity/gui/converter/ConvertToBam.java @@ -100,12 +100,12 @@ import org.infinity.resource.Profile; import org.infinity.resource.ResourceFactory; import org.infinity.resource.graphics.BamDecoder; +import org.infinity.resource.graphics.BamDecoder.BamControl; import org.infinity.resource.graphics.BamV1Decoder; import org.infinity.resource.graphics.ColorConvert; import org.infinity.resource.graphics.DxtEncoder; import org.infinity.resource.graphics.GifSequenceReader; import org.infinity.resource.graphics.PseudoBamDecoder; -import org.infinity.resource.graphics.BamDecoder.BamControl; import org.infinity.resource.graphics.PseudoBamDecoder.PseudoBamControl; import org.infinity.resource.graphics.PseudoBamDecoder.PseudoBamCycleEntry; import org.infinity.resource.graphics.PseudoBamDecoder.PseudoBamFrameEntry; @@ -115,11 +115,11 @@ import org.infinity.util.IniMapEntry; import org.infinity.util.IniMapSection; import org.infinity.util.Misc; -import org.infinity.util.Pair; import org.infinity.util.SimpleListModel; import org.infinity.util.io.FileEx; import org.infinity.util.io.FileManager; import org.infinity.util.io.StreamUtils; +import org.infinity.util.tuples.Couple; public class ConvertToBam extends ChildFrame implements ActionListener, PropertyChangeListener, FocusListener, ChangeListener, @@ -176,7 +176,7 @@ public class ConvertToBam extends ChildFrame // BamDecoder instance containing the final result of the current BAM structure private final PseudoBamDecoder bamDecoderFinal = new PseudoBamDecoder(); // Frame image lists (use BAM_ORIGINAL/BAM_FINAL constants for access) - private final List> listFrameEntries = new ArrayList>(2); + private final List> listFrameEntries = new ArrayList<>(2); // Frame entry used for preview in filter tab private final PseudoBamFrameEntry entryFilterPreview = new PseudoBamFrameEntry(null, 0, 0); // The palette dialog instance for BAM v1 export @@ -1637,7 +1637,7 @@ private JPanel createFiltersTab() GridBagConstraints c = new GridBagConstraints(); // creating "Filters" section - Vector filters = new Vector(); + Vector filters = new Vector<>(); for (int i = 0; i < BamFilterFactory.getFilterInfoSize(); i++) { filters.add(BamFilterFactory.getFilterInfo(i)); } @@ -1697,7 +1697,7 @@ private JPanel createFiltersTab() GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0); pFiltersDesc.add(taFiltersDesc, c); - modelFilters = new SimpleListModel(); + modelFilters = new SimpleListModel<>(); listFilters = new JList<>(modelFilters); listFilters.setCellRenderer(new IndexedCellRenderer()); listFilters.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); @@ -1855,10 +1855,10 @@ private void updateStatus() private void updateFramesList() { // updating button states - Pair bounds = getIndexBounds(listFrames.getSelectedIndices()); - bFramesUp.setEnabled(!modelFrames.isEmpty() && bounds.getFirst() > 0); - bFramesDown.setEnabled(!modelFrames.isEmpty() && bounds.getFirst() >= 0 && - bounds.getSecond() < modelFrames.getSize() - 1); + Couple bounds = getIndexBounds(listFrames.getSelectedIndices()); + bFramesUp.setEnabled(!modelFrames.isEmpty() && bounds.getValue0() > 0); + bFramesDown.setEnabled(!modelFrames.isEmpty() && bounds.getValue0() >= 0 && + bounds.getValue1() < modelFrames.getSize() - 1); miFramesRemove.setEnabled(!modelFrames.isEmpty() && !listFrames.isSelectionEmpty()); miFramesRemoveAll.setEnabled(!modelFrames.isEmpty()); miFramesDropUnused.setEnabled(!modelFrames.isEmpty()); @@ -1879,16 +1879,16 @@ private void updateFramesList() private void updateCyclesList() { listCycles.repaint(); - Pair bounds = getIndexBounds(listCycles.getSelectedIndices()); - int idx = (bounds.getFirst().compareTo(bounds.getSecond()) == 0) ? bounds.getFirst() : -1; + Couple bounds = getIndexBounds(listCycles.getSelectedIndices()); + int idx = (bounds.getValue0().compareTo(bounds.getValue1()) == 0) ? bounds.getValue0() : -1; if (idx >= 0) { listCycles.ensureIndexIsVisible(idx); } // updating button states - bCyclesUp.setEnabled(!modelCycles.isEmpty() && bounds.getFirst() > 0); - bCyclesDown.setEnabled(!modelCycles.isEmpty() && bounds.getFirst() >= 0 && - bounds.getSecond() < modelCycles.getSize() - 1); + bCyclesUp.setEnabled(!modelCycles.isEmpty() && bounds.getValue0() > 0); + bCyclesDown.setEnabled(!modelCycles.isEmpty() && bounds.getValue0() >= 0 && + bounds.getValue1() < modelCycles.getSize() - 1); bCyclesRemove.setEnabled(!listCycles.isSelectionEmpty()); bCyclesRemoveAll.setEnabled(!modelCycles.isEmpty()); @@ -1910,10 +1910,10 @@ private void updateCyclesList() private void updateCurrentCycle() { // updating button states - Pair bounds = getIndexBounds(listCurCycle.getSelectedIndices()); - bCurCycleUp.setEnabled(!modelCurCycle.isEmpty() && bounds.getFirst() > 0); - bCurCycleDown.setEnabled(!modelCurCycle.isEmpty() && bounds.getFirst() >= 0 && - bounds.getSecond() < modelCurCycle.getSize() - 1); + Couple bounds = getIndexBounds(listCurCycle.getSelectedIndices()); + bCurCycleUp.setEnabled(!modelCurCycle.isEmpty() && bounds.getValue0() > 0); + bCurCycleDown.setEnabled(!modelCurCycle.isEmpty() && bounds.getValue0() >= 0 && + bounds.getValue1() < modelCurCycle.getSize() - 1); bCurCycleAdd.setEnabled(!listFramesAvail.isSelectionEmpty()); bCurCycleRemove.setEnabled(!listCurCycle.isSelectionEmpty()); listFramesAvail.invalidate(); @@ -1923,16 +1923,16 @@ private void updateCurrentCycle() private void initCurrentCycle(int cycleIdx) { - initCurrentCycle(new Pair(cycleIdx, cycleIdx)); + initCurrentCycle(Couple.with(cycleIdx, cycleIdx)); } /** Initializes the "Current cycle" section of the Cycles tab. */ - private void initCurrentCycle(Pair cycleIndices) + private void initCurrentCycle(Couple cycleIndices) { if (cycleIndices != null) { - if (cycleIndices.getFirst().compareTo(cycleIndices.getSecond()) == 0 && - cycleIndices.getFirst() >= 0 && cycleIndices.getFirst() < modelCycles.getSize()) { - int cycleIdx = cycleIndices.getFirst(); + if (cycleIndices.getValue0().compareTo(cycleIndices.getValue1()) == 0 && + cycleIndices.getValue0() >= 0 && cycleIndices.getValue0() < modelCycles.getSize()) { + int cycleIdx = cycleIndices.getValue0(); // enabling components listFramesAvail.setEnabled(true); @@ -1960,7 +1960,7 @@ private void initCurrentCycle(Pair cycleIndices) listCurCycle.setSelectedIndices(new int[]{}); listCurCycle.setEnabled(false); - if (cycleIndices.getFirst() < 0 || cycleIndices.getSecond() < 0) { + if (cycleIndices.getValue0() < 0 || cycleIndices.getValue1() < 0) { pCurrentCycle.setBorder(BorderFactory.createTitledBorder("No cycle selected ")); } else { pCurrentCycle.setBorder(BorderFactory.createTitledBorder("Too many cycles selected ")); @@ -2067,12 +2067,12 @@ private void updateQuickPreview(RenderCanvas target, int[] indices, boolean show g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); float ratioX = (float)imgWidth / (float)image.getWidth(null); float ratioY = (float)imgHeight / (float)image.getHeight(null); - Pair minMaxRatio = new Pair(Math.min(ratioX, ratioY), Math.max(ratioX, ratioY)); - if ((float)image.getWidth(null)*minMaxRatio.getSecond() < (float)imgWidth && - (float)image.getHeight(null)*minMaxRatio.getSecond() < (float)imgHeight) { - ratio = minMaxRatio.getSecond(); + Couple minMaxRatio = Couple.with(Math.min(ratioX, ratioY), Math.max(ratioX, ratioY)); + if ((float)image.getWidth(null)*minMaxRatio.getValue1() < (float)imgWidth && + (float)image.getHeight(null)*minMaxRatio.getValue1() < (float)imgHeight) { + ratio = minMaxRatio.getValue1(); } else { - ratio = minMaxRatio.getFirst(); + ratio = minMaxRatio.getValue0(); } int newWidth = (int)((float)image.getWidth(null)*ratio); int newHeight = (int)((float)image.getHeight(null)*ratio); @@ -2553,8 +2553,7 @@ private boolean framesAddImage(int listIndex, ResourceEntry entry, int frameInde { boolean retVal = false; if (listIndex >= 0 && entry != null) { - try { - InputStream is = entry.getResourceDataAsStream(); + try (InputStream is = entry.getResourceDataAsStream()) { BufferedImage[] images; if (entry.getExtension().equalsIgnoreCase("gif")) { // Potential GIF animation @@ -2566,7 +2565,7 @@ private boolean framesAddImage(int listIndex, ResourceEntry entry, int frameInde } } else { // Everything else - ImageReader reader = (ImageReader)ImageIO.getImageReadersBySuffix(entry.getExtension()).next(); + ImageReader reader = ImageIO.getImageReadersBySuffix(entry.getExtension()).next(); reader.setInput(ImageIO.createImageInputStream(is), false); int numFrames = reader.getNumImages(true); images = new BufferedImage[numFrames]; @@ -3151,11 +3150,11 @@ private void currentCycleAdd() { int[] indices = listFramesAvail.getSelectedIndices(); if (indices != null && indices.length > 0) { - Pair dstBounds = getIndexBounds(listCurCycle.getSelectedIndices()); - int dstIdx = dstBounds.getSecond() + 1; + Couple dstBounds = getIndexBounds(listCurCycle.getSelectedIndices()); + int dstIdx = dstBounds.getValue1() + 1; modelCurCycle.insert(dstIdx, indices); modelCycles.contentChanged(modelCurCycle.getCycle()); - listFramesAvail.setSelectedIndices(new int[]{getIndexBounds(indices).getSecond()}); + listFramesAvail.setSelectedIndices(new int[]{getIndexBounds(indices).getValue1()}); listCurCycle.setSelectedIndex(dstIdx + indices.length - 1); updateCurrentCycle(); } @@ -3811,18 +3810,18 @@ private Path setBamOutput() /** Returns the min/max values from the specified array of indices in a Pair object. */ - private Pair getIndexBounds(int[] indices) + private Couple getIndexBounds(int[] indices) { - Pair retVal = new Pair(Integer.valueOf(-1), Integer.valueOf(-1)); + Couple retVal = Couple.with(Integer.valueOf(-1), Integer.valueOf(-1)); if (indices != null && indices.length > 0) { - retVal.setFirst(Integer.valueOf(Integer.MAX_VALUE)); - retVal.setSecond(Integer.valueOf(Integer.MIN_VALUE)); + retVal.setValue0(Integer.valueOf(Integer.MAX_VALUE)); + retVal.setValue1(Integer.valueOf(Integer.MIN_VALUE)); for (int i = 0; i < indices.length; i++) { - if (indices[i] < retVal.getFirst()) { - retVal.setFirst(Integer.valueOf(indices[i])); + if (indices[i] < retVal.getValue0()) { + retVal.setValue0(Integer.valueOf(indices[i])); } - if (indices[i] > retVal.getSecond()) { - retVal.setSecond(Integer.valueOf(indices[i])); + if (indices[i] > retVal.getValue1()) { + retVal.setValue1(Integer.valueOf(indices[i])); } } } @@ -3881,7 +3880,7 @@ private DxtEncoder.DxtType getAutoDxtType() private List convert() { - List result = new Vector(2); + List result = new Vector<>(2); try { updateFilteredBamDecoder(getBamVersion(), false); List outList = createOutputFilterList(); @@ -3930,7 +3929,7 @@ private PseudoBamFrameEntry getFilteredBamFrame(int bamVersion, int frameIdx, bo // processing each filter that exists before the selected filter PseudoBamFrameEntry entry = entryFilterPreview; for (int i = 0; i < curFilterIdx; i++) { - if (modelFilters.get(i) instanceof BamFilterBase) { + if (modelFilters.get(i) != null) { BamFilterBase filter = modelFilters.get(i); entry = filter.updatePreview(entry); } @@ -4021,8 +4020,8 @@ private void updateFilteredBamDecoder(int bamVersion, boolean force) throws Exce /** Creates a sorted list including all selected filters in the post-processing tab. */ private List createFilterList(boolean includeOutputFilters) { - List retVal = new ArrayList(); - List outFilters = new ArrayList(); + List retVal = new ArrayList<>(); + List outFilters = new ArrayList<>(); for (int i = 0; i < modelFilters.size(); i++) { BamFilterBase filter = modelFilters.get(i); if (filter instanceof BamFilterBaseOutput) { @@ -4047,7 +4046,7 @@ private List createFilterList(boolean includeOutputFilters) /** Creates a list of selected output filters only. */ private List createOutputFilterList() { - List retVal = new ArrayList(); + List retVal = new ArrayList<>(); for (int i = 0; i < modelFilters.size(); i++) { if (modelFilters.get(i) instanceof BamFilterBaseOutput) { retVal.add((BamFilterBaseOutput)modelFilters.get(i)); @@ -4101,7 +4100,7 @@ private void updateFinalBamDecoder(int bamVersion) throws Exception if (transIndex < 0) { transIndex = 0; } - HashMap colorCache = new HashMap(4096); + HashMap colorCache = new HashMap<>(4096); for (int i = 0; i < palette.length; i++) { if (i != transIndex) { colorCache.put(Integer.valueOf(palette[i]), Byte.valueOf((byte)i)); @@ -4129,7 +4128,8 @@ private void updateFinalBamDecoder(int bamVersion) throws Exception if (ci >= transIndex) ci++; dstBuf[ofs] = colIdx.byteValue();//(byte)ci; } else { - byte color = (byte)ColorConvert.nearestColorRGB(srcBuf[ofs], palette, !getUseAlpha()); + double weight = getUseAlpha() ? 1.0 : 0.0; + byte color = (byte)ColorConvert.getNearestColor(srcBuf[ofs], palette, weight, null); dstBuf[ofs] = color;//(byte)ci; colorCache.put(Integer.valueOf(c), Byte.valueOf(color)); } @@ -4195,7 +4195,7 @@ private void updateFinalBamFrame(int bamVersion, int frameIdx) if (transIndex < 0) { transIndex = 0; } - HashMap colorCache = new HashMap(4096); + HashMap colorCache = new HashMap<>(4096); for (int i = 0; i < palette.length; i++) { if (i != transIndex) { colorCache.put(Integer.valueOf(palette[i]), Byte.valueOf((byte)i)); @@ -4221,7 +4221,8 @@ private void updateFinalBamFrame(int bamVersion, int frameIdx) if (ci >= transIndex) ci++; dstBuf[ofs] = colIdx.byteValue(); } else { - byte color = (byte)ColorConvert.nearestColorRGB(srcBuf[ofs], palette, !getUseAlpha()); + double weight = getUseAlpha() ? 1.0 : 0.0; + byte color = (byte)ColorConvert.getNearestColor(srcBuf[ofs], palette, weight, null); dstBuf[ofs] = color;//(byte)ci; colorCache.put(Integer.valueOf(c), Byte.valueOf(color)); } @@ -5399,7 +5400,7 @@ public SourceData(Path image) bam.getPaletteDialog().clear(); // applying frames - HashMap sourceMap = new HashMap(); + HashMap sourceMap = new HashMap<>(); for (int i = 0; i < frames.length; i++) { SourceFrame frame = frames[i]; SourceData data = sourceMap.get(frame.entry); diff --git a/src/org/infinity/gui/converter/ConvertToBmp.java b/src/org/infinity/gui/converter/ConvertToBmp.java index 91a119f6e..c77a50b5a 100644 --- a/src/org/infinity/gui/converter/ConvertToBmp.java +++ b/src/org/infinity/gui/converter/ConvertToBmp.java @@ -18,9 +18,9 @@ import java.awt.image.DataBufferInt; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.io.File; import java.io.IOException; import java.io.OutputStream; -import java.io.File; import java.nio.ByteBuffer; import java.nio.file.DirectoryStream; import java.nio.file.Files; @@ -324,7 +324,7 @@ private void init() GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); pInputButtons.add(pRemove, c); - modelInputFiles = new SimpleListModel(); + modelInputFiles = new SimpleListModel<>(); listInputFiles = new JList<>(modelInputFiles); JScrollPane scroll = new JScrollPane(listInputFiles); JPanel pInputFrame = new JPanel(new GridBagLayout()); @@ -482,7 +482,7 @@ private void inputAdd() } Path[] files = getOpenFileName(this, "Choose file(s)", rootPath, true, getGraphicsFilters(), 0); if (files != null) { - List skippedFiles = new ArrayList(); + List skippedFiles = new ArrayList<>(); int idx = listInputFiles.getSelectedIndex() + 1; for (final Path file: files) { if (isValidInput(file)) { @@ -524,7 +524,7 @@ private void inputAddFolder() if (path != null && FileEx.create(path).isDirectory()) { // adding all files in the directory FileNameExtensionFilter[] filters = getGraphicsFilters(); - List skippedFiles = new ArrayList(); + List skippedFiles = new ArrayList<>(); int idx = listInputFiles.getSelectedIndex() + 1; try (DirectoryStream dstream = Files.newDirectoryStream(path)) { for (final Path file: dstream) { @@ -610,7 +610,7 @@ private void setOutput() private List convert() { - List result = new ArrayList(2); + List result = new ArrayList<>(2); final String progressMsg = "Converting file %d / %d"; int progressIdx = 0, progressMax = modelInputFiles.size() + 1; ProgressMonitor progress = new ProgressMonitor(this, "Converting files...", "Preparing", 0, progressMax); diff --git a/src/org/infinity/gui/converter/ConvertToMos.java b/src/org/infinity/gui/converter/ConvertToMos.java index f074296db..57af8805f 100644 --- a/src/org/infinity/gui/converter/ConvertToMos.java +++ b/src/org/infinity/gui/converter/ConvertToMos.java @@ -85,6 +85,7 @@ public class ConvertToMos extends ChildFrame * @param img The source image to convert into a MOS resource. * @param mosFileName The name of the resulting MOS file. * @param compressed If {@code true}, converts into a compressed BAMC file. + * @param fastConvert If {@code true}, uses a fast but less accurate color reduction algorith. * @param result Returns more specific information about the conversion process. Data placed in the * first item indicates success, data in the second item indicates failure. * @param showProgress Specify whether to show a progress monitor (needs a valid 'parent' parameter). @@ -138,7 +139,7 @@ public static boolean convertV1(Component parent, BufferedImage img, String mosF } // creating list of tiles as int[] arrays - List tileList = new ArrayList(cols*rows); + List tileList = new ArrayList<>(cols*rows); for (int y = 0; y < rows; y++) { for (int x = 0; x < cols; x++) { int tileX = x * 64; @@ -156,7 +157,7 @@ public static boolean convertV1(Component parent, BufferedImage img, String mosF byte[] tilePalette = new byte[1024]; byte[] tileData = new byte[64*64]; int curPalOfs = palOfs, curTableOfs = tableOfs, curDataOfs = dataOfs; - IntegerHashMap colorCache = new IntegerHashMap(1536); // caching RGBColor -> index + IntegerHashMap colorCache = new IntegerHashMap<>(1536); // caching RGBColor -> index for (int tileIdx = 0; tileIdx < tileList.size(); tileIdx++) { colorCache.clear(); if (showProgress) { @@ -194,7 +195,7 @@ public static boolean convertV1(Component parent, BufferedImage img, String mosF if (palIndex != null) { tileData[i] = (byte)(palIndex + 1); } else { - byte color = (byte)ColorConvert.nearestColorRGB(pixels[i], palette, true); + byte color = (byte)ColorConvert.getNearestColor(pixels[i], palette, 0.0, null); tileData[i] = (byte)(color + 1); colorCache.put(pixels[i], color); } @@ -286,8 +287,8 @@ public static boolean convertV2(Component parent, BufferedImage img, String mosF ProgressMonitor progress = null; int width = img.getWidth(); int height = img.getHeight(); - List pageList = new ArrayList(); - List entryList = new ArrayList(); + List pageList = new ArrayList<>(); + List entryList = new ArrayList<>(); try { if (showProgress) { @@ -971,7 +972,7 @@ private String getMosFileName(Path path) private List convert() { - List result = new Vector(2); + List result = new Vector<>(2); // validating input file Path inFile = FileManager.resolve(tfInputV1.getText()); diff --git a/src/org/infinity/gui/converter/ConvertToPvrz.java b/src/org/infinity/gui/converter/ConvertToPvrz.java index 995420dd9..4567a0421 100644 --- a/src/org/infinity/gui/converter/ConvertToPvrz.java +++ b/src/org/infinity/gui/converter/ConvertToPvrz.java @@ -542,14 +542,14 @@ private List convert() targetPath = FileManager.resolve(tfTargetDir.getText()); } if (!FileEx.create(targetPath).isDirectory()) { - List l = new Vector(2); + List l = new Vector<>(2); l.add(null); l.add("Invalid target directory specified. No conversion takes place."); return l; } if (lInputModel.isEmpty()) { - List l = new Vector(2); + List l = new Vector<>(2); l.add(null); l.add("No source file(s) specified. No conversion takes place."); return l; @@ -720,7 +720,7 @@ private List convert() if (progress.isCanceled()) { progress.close(); progress = null; - List l = new Vector(2); + List l = new Vector<>(2); l.add(null); l.add("Conversion cancelled."); return l; @@ -773,7 +773,7 @@ private List convert() progress = null; // constructing failure/success message - List l = new Vector(2); + List l = new Vector<>(2); StringBuilder sb = new StringBuilder(); if (warnings == 0 && errors == 0) { sb.append("Conversion finished successfully."); diff --git a/src/org/infinity/gui/converter/ConvertToTis.java b/src/org/infinity/gui/converter/ConvertToTis.java index 57cb757c7..95404db77 100644 --- a/src/org/infinity/gui/converter/ConvertToTis.java +++ b/src/org/infinity/gui/converter/ConvertToTis.java @@ -150,7 +150,7 @@ public static boolean convertV1(Component parent, BufferedImage img, String tisF progress.setMillisToPopup(0); } - IntegerHashMap colorCache = new IntegerHashMap(2048); // caching RGBColor -> index + IntegerHashMap colorCache = new IntegerHashMap<>(2048); // caching RGBColor -> index for (int tileIdx = 0; tileIdx < tileCount; tileIdx++) { if (showProgress) { if (progress.isCanceled()) { @@ -199,7 +199,7 @@ public static boolean convertV1(Component parent, BufferedImage img, String tisF if (palIndex != null) { tileData[i] = (byte)(palIndex + 1); } else { - byte color = (byte)ColorConvert.nearestColorRGB(srcBlock[i], palette, true); + byte color = (byte)ColorConvert.getNearestColor(srcBlock[i], palette, 0.0, null); tileData[i] = (byte)(color + 1); colorCache.put(srcBlock[i], color); } @@ -293,8 +293,8 @@ public static boolean convertV2(Component parent, BufferedImage img, String tisF // preparing variables ProgressMonitor progress = null; - List pageList = new ArrayList(); - List entryList = new ArrayList(tileCount); + List pageList = new ArrayList<>(); + List entryList = new ArrayList<>(tileCount); byte[] dst = new byte[24 + tileCount*12]; // header + tiles int dstOfs = 0; // current start offset for write operations @@ -1068,7 +1068,7 @@ private boolean validateInput(String inputFile) // Return value: First list element is used for success message, second element for error message. private List convert() { - List ret = new Vector(2); + List ret = new Vector<>(2); // validating input file Path inFile = FileManager.resolve(inFileName); diff --git a/src/org/infinity/gui/hexview/BasicColorMap.java b/src/org/infinity/gui/hexview/BasicColorMap.java index 2bce42036..95c5933ee 100644 --- a/src/org/infinity/gui/hexview/BasicColorMap.java +++ b/src/org/infinity/gui/hexview/BasicColorMap.java @@ -39,7 +39,7 @@ public enum Coloring { // Color definitions. Each entry consists of two slightly different color tones // that will be used alternately. - private static final EnumMap colorMap = new EnumMap(Coloring.class); + private static final EnumMap colorMap = new EnumMap<>(Coloring.class); static { // Populating color map @@ -86,8 +86,8 @@ public enum Coloring { // Contains color definitions for specific data types. // Works only on top-level datatypes that are preferably described by a section offset and count. - private final EnumMap typeMap = new EnumMap(Coloring.class); - private final MapEntry cachedColor = new MapEntry(); + private final EnumMap typeMap = new EnumMap<>(Coloring.class); + private final MapEntry cachedColor = new MapEntry<>(); private final AbstractStruct struct; private List listBlocks; @@ -156,7 +156,7 @@ public void reset() close(); if (listBlocks == null) { - listBlocks = new ArrayList(); + listBlocks = new ArrayList<>(); } if (!listBlocks.isEmpty()) { @@ -345,7 +345,7 @@ public int compare(ColoredBlock obj, ColoredBlock key) private class Structure { // only used if isTable = true - private final List structures = new ArrayList(); + private final List structures = new ArrayList<>(); private final Class classType; @@ -484,6 +484,17 @@ public ColoredBlock(int offset, int size, Coloring color, int index) public int getColorIndex() { return index; } // public void setColorIndex(int index) { this.index = index & 1; } + @Override + public int hashCode() + { + int hash = 7; + hash = 31 * hash + offset; + hash = 31 * hash + size; + hash = 31 * hash + index; + hash = 31 * hash + ((color == null) ? 0 : color.hashCode()); + return hash; + } + @Override public boolean equals(Object o) { diff --git a/src/org/infinity/gui/hexview/FindDataDialog.java b/src/org/infinity/gui/hexview/FindDataDialog.java index b0166ff09..317865eed 100644 --- a/src/org/infinity/gui/hexview/FindDataDialog.java +++ b/src/org/infinity/gui/hexview/FindDataDialog.java @@ -251,7 +251,7 @@ public void actionPerformed(ActionEvent e) // Attempts to parse useful byte values from the specified text string private byte[] parseBytes(String text) { - List list = new ArrayList(); + List list = new ArrayList<>(); // parsing text string StringBuilder sb = new StringBuilder(); diff --git a/src/org/infinity/gui/hexview/ResourceDataProvider.java b/src/org/infinity/gui/hexview/ResourceDataProvider.java index feae67a32..9ce86803b 100644 --- a/src/org/infinity/gui/hexview/ResourceDataProvider.java +++ b/src/org/infinity/gui/hexview/ResourceDataProvider.java @@ -20,8 +20,8 @@ */ public class ResourceDataProvider implements IDataProvider { - private final ArrayList listeners = new ArrayList(); - private final HashMap modifiedMap = new HashMap(); + private final ArrayList listeners = new ArrayList<>(); + private final HashMap modifiedMap = new HashMap<>(); private final ResourceEntry entry; private int size; diff --git a/src/org/infinity/gui/hexview/ResourceMenuCreator.java b/src/org/infinity/gui/hexview/ResourceMenuCreator.java index 5f8f2f693..8510331bd 100644 --- a/src/org/infinity/gui/hexview/ResourceMenuCreator.java +++ b/src/org/infinity/gui/hexview/ResourceMenuCreator.java @@ -5,7 +5,6 @@ package org.infinity.gui.hexview; import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.util.ArrayList; import java.util.List; @@ -18,13 +17,12 @@ import org.infinity.resource.AbstractStruct; import org.infinity.resource.StructEntry; -import tv.porst.jhexview.IMenuCreator; import tv.porst.jhexview.JHexView; /** * Provides a dynamic popupmenu for the StructHexViewer component. */ -public class ResourceMenuCreator extends MenuCreator implements IMenuCreator, ActionListener +public class ResourceMenuCreator extends MenuCreator { private final AbstractStruct struct; @@ -118,7 +116,7 @@ public AbstractStruct getStruct() // Creates a list of all structures containing the specified offset, starting from topmost level. private List createStructEntries(int offset) { - List list = new ArrayList(); + List list = new ArrayList<>(); if (getHexView().getData() instanceof StructuredDataProvider) { StructEntry curEntry = ((StructuredDataProvider)getHexView().getData()).getFieldAt(offset); if (curEntry != null) { diff --git a/src/org/infinity/gui/hexview/StructHexViewer.java b/src/org/infinity/gui/hexview/StructHexViewer.java index 3791f37fd..21b9812e5 100644 --- a/src/org/infinity/gui/hexview/StructHexViewer.java +++ b/src/org/infinity/gui/hexview/StructHexViewer.java @@ -43,16 +43,14 @@ import javax.swing.table.TableModel; import org.infinity.NearInfinity; -import org.infinity.datatype.Bitmap; +import org.infinity.datatype.AbstractBitmap; import org.infinity.datatype.ColorPicker; import org.infinity.datatype.ColorValue; import org.infinity.datatype.DecNumber; import org.infinity.datatype.Flag; import org.infinity.datatype.FloatNumber; -import org.infinity.datatype.HashBitmap; import org.infinity.datatype.MultiNumber; import org.infinity.datatype.ProRef; -import org.infinity.datatype.ResourceBitmap; import org.infinity.datatype.ResourceRef; import org.infinity.datatype.SectionCount; import org.infinity.datatype.SectionOffset; @@ -144,8 +142,7 @@ public static String getTypeDesc(StructEntry type) } else if (type instanceof DecNumber || type instanceof MultiNumber || type instanceof FloatNumber) { return "Number"; - } else if (type instanceof Bitmap || type instanceof HashBitmap || - type instanceof ResourceBitmap) { + } else if (type instanceof AbstractBitmap) { return "Numeric type or identifier"; } else if (type instanceof TextBitmap || type instanceof TextEdit || type instanceof TextString) { @@ -192,7 +189,6 @@ public StructHexViewer(AbstractStruct struct, IColormap colorMap, IDataProvider initGui(); } - // @Override public void stateChanged(HexViewEvent event) { @@ -209,17 +205,13 @@ public void stateChanged(HexViewEvent event) updateStatusBar(offset); } } - // - // @Override public void dataChanged(DataChangedEvent event) { getStruct().setStructChanged(true); } - // - // @Override public void actionPerformed(ActionEvent event) { @@ -329,9 +321,7 @@ public void actionPerformed(ActionEvent event) getHexView().requestFocusInWindow(); } } - // - // @Override public void stateChanged(ChangeEvent e) { @@ -350,9 +340,7 @@ public void stateChanged(ChangeEvent e) } } } - // - // @Override public void close() throws Exception { @@ -374,7 +362,6 @@ public void close() throws Exception findData = null; } } - // /** Returns the associated resource structure. */ public AbstractStruct getStruct() @@ -625,7 +612,7 @@ private void updatePanel(int offset) list.remove(0); } } else { - list = new ArrayList(); + list = new ArrayList<>(); } // removing invalid models and controls diff --git a/src/org/infinity/gui/hexview/VariableDataProvider.java b/src/org/infinity/gui/hexview/VariableDataProvider.java index cd1fecf20..b598afd2f 100644 --- a/src/org/infinity/gui/hexview/VariableDataProvider.java +++ b/src/org/infinity/gui/hexview/VariableDataProvider.java @@ -19,7 +19,7 @@ */ public class VariableDataProvider implements IDataProvider { - private final List listeners = new ArrayList(); + private final List listeners = new ArrayList<>(); private byte[] buffer; diff --git a/src/org/infinity/gui/layeritem/AbstractLayerItem.java b/src/org/infinity/gui/layeritem/AbstractLayerItem.java index a8664da30..7b497b0f8 100644 --- a/src/org/infinity/gui/layeritem/AbstractLayerItem.java +++ b/src/org/infinity/gui/layeritem/AbstractLayerItem.java @@ -156,11 +156,10 @@ public Viewable getViewable() public void showViewable() { if (viewable != null && getTopLevelAncestor() != null) { - new ViewFrame(getTopLevelAncestor(), (Viewable)viewable); + new ViewFrame(getTopLevelAncestor(), viewable); } } - // @Override public void mouseClicked(MouseEvent event) { @@ -197,9 +196,7 @@ public void mouseReleased(MouseEvent event) setItemState(ItemState.NORMAL); } } - // - // @Override public void mouseDragged(MouseEvent event) { @@ -214,16 +211,13 @@ public void mouseMoved(MouseEvent event) setItemState(ItemState.NORMAL); } } - // - // @Override public boolean contains(int x, int y) { // Non-visible parts of the component are disregarded by mouse events return isMouseOver(new Point(x, y)); } - // /** Returns whether the mouse cursor is over the relevant part of the component. */ protected boolean isMouseOver(Point pt) diff --git a/src/org/infinity/gui/layeritem/AnimatedLayerItem.java b/src/org/infinity/gui/layeritem/AnimatedLayerItem.java index 469b1479f..1118f30f1 100644 --- a/src/org/infinity/gui/layeritem/AnimatedLayerItem.java +++ b/src/org/infinity/gui/layeritem/AnimatedLayerItem.java @@ -8,13 +8,13 @@ import java.awt.BasicStroke; import java.awt.BorderLayout; import java.awt.Color; +import java.awt.Composite; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.awt.image.BufferedImage; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; @@ -24,6 +24,7 @@ import org.infinity.gui.RenderCanvas; import org.infinity.resource.Viewable; +import org.infinity.resource.are.viewer.AbstractAnimationProvider; import org.infinity.resource.are.viewer.ViewerConstants; import org.infinity.resource.graphics.ColorConvert; @@ -177,6 +178,22 @@ public void setForcedInterpolation(boolean set) } } + /** + * Returns the {@link Composite} object assigned to the canvas. + */ + public Composite getComposite() + { + return rcCanvas.getComposite(); + } + + /** + * Sets the {@link Composite} object for the canvas. + */ + public void setComposite(Composite comp) + { + rcCanvas.setComposite(comp); + } + /** * Returns whether the animation will automatically restart after playing the last frame. * (Note: Merely returns the value provided by the attached BasicAnimationProvider object.) @@ -307,7 +324,6 @@ public void setVisible(boolean aFlag) super.setVisible(aFlag); } - // @Override public void layerItemChanged(LayerItemEvent event) { @@ -315,9 +331,7 @@ public void layerItemChanged(LayerItemEvent event) updateDisplay(false); } } - // - // @Override public void actionPerformed(ActionEvent event) { @@ -346,9 +360,7 @@ protected Void doInBackground() throws Exception { } } } - // - // @Override public void propertyChange(PropertyChangeEvent event) { @@ -360,7 +372,6 @@ public void propertyChange(PropertyChangeEvent event) } } } - // @Override public void repaint() @@ -457,8 +468,8 @@ private void initAnimation(BasicAnimationProvider anim) if (anim != null) { animation = anim; } else { - if (!(animation instanceof DefaultAnimationProvider)) { - animation = new DefaultAnimationProvider(); + if (!(animation instanceof AbstractAnimationProvider.DefaultAnimationProvider)) { + animation = AbstractAnimationProvider.DEFAULT_ANIMATION_PROVIDER; } } @@ -620,45 +631,4 @@ public void setColor(Color color) this.color = color; } } - - - /** A pseudo animation provider that always returns a transparent image of 16x16 size. */ - private static final class DefaultAnimationProvider implements BasicAnimationProvider - { - private final BufferedImage image; - - public DefaultAnimationProvider() - { - image = ColorConvert.createCompatibleImage(16, 16, true); - } - - @Override - public Image getImage() - { - return image; - } - - @Override - public boolean advanceFrame() - { - return false; - } - - @Override - public void resetFrame() - { - } - - @Override - public boolean isLooping() - { - return false; - } - - @Override - public Point getLocationOffset() - { - return new Point(); - } - } } diff --git a/src/org/infinity/gui/layeritem/IconLayerItem.java b/src/org/infinity/gui/layeritem/IconLayerItem.java index d4ada27f6..06a4b46e7 100644 --- a/src/org/infinity/gui/layeritem/IconLayerItem.java +++ b/src/org/infinity/gui/layeritem/IconLayerItem.java @@ -232,7 +232,6 @@ public void setLabelEnabled(boolean set) validate(); } - // @Override public void layerItemChanged(LayerItemEvent event) { @@ -240,7 +239,6 @@ public void layerItemChanged(LayerItemEvent event) setCurrentImage(getItemState()); } } - // /** Returns whether the mouse cursor is over the relevant part of the component. */ @Override diff --git a/src/org/infinity/gui/layeritem/ShapedLayerItem.java b/src/org/infinity/gui/layeritem/ShapedLayerItem.java index f589339be..8e9599676 100644 --- a/src/org/infinity/gui/layeritem/ShapedLayerItem.java +++ b/src/org/infinity/gui/layeritem/ShapedLayerItem.java @@ -246,7 +246,6 @@ private void updateShape() label.repaint(); } - // @Override public void layerItemChanged(LayerItemEvent event) { @@ -254,7 +253,6 @@ public void layerItemChanged(LayerItemEvent event) updateShape(); } } - // //----------------------------- INNER CLASSES ----------------------------- diff --git a/src/org/infinity/icon/CreViewer24.png b/src/org/infinity/icon/CreViewer24.png new file mode 100644 index 000000000..df068df18 Binary files /dev/null and b/src/org/infinity/icon/CreViewer24.png differ diff --git a/src/org/infinity/icon/Icons.java b/src/org/infinity/icon/Icons.java index 26c225870..8ef6faf70 100644 --- a/src/org/infinity/icon/Icons.java +++ b/src/org/infinity/icon/Icons.java @@ -33,6 +33,7 @@ public class Icons public static final String ICON_COLLAPSE_ALL_24 = "CollapseAll24.png"; public static final String ICON_COLOR_16 = "Color16.gif"; public static final String ICON_COPY_16 = "Copy16.gif"; + public static final String ICON_CRE_VIEWER_24 = "CreViewer24.png"; public static final String ICON_CUT_16 = "Cut16.gif"; public static final String ICON_DELETE_16 = "Delete16.gif"; public static final String ICON_DOWN_16 = "Down16.gif"; @@ -81,7 +82,7 @@ public class Icons public static final String ICON_YELLOW_CIRCLE_20 = "YellowCircle20.gif"; public static final String ICON_ZOOM_16 = "Zoom16.gif"; - private static final Map ICONMAP = new HashMap(100); + private static final Map ICONMAP = new HashMap<>(100); /** * Returns an ImageIcon object of the specified graphics filename. diff --git a/src/org/infinity/resource/AbstractAbility.java b/src/org/infinity/resource/AbstractAbility.java index 0b86431d0..80563aaf6 100644 --- a/src/org/infinity/resource/AbstractAbility.java +++ b/src/org/infinity/resource/AbstractAbility.java @@ -9,8 +9,7 @@ import java.nio.ByteBuffer; import org.infinity.datatype.DecNumber; -import org.infinity.datatype.SectionCount; -import org.infinity.datatype.SectionOffset; +import org.infinity.datatype.IsNumeric; import org.infinity.resource.spl.Ability; import org.infinity.resource.spl.SplResource; @@ -362,15 +361,15 @@ public void write(OutputStream os) throws IOException protected void setAddRemovableOffset(AddRemovable datatype) { if (datatype instanceof Effect && getEffectsCount() >= 1) { - final SectionOffset effectOffset = (SectionOffset)getParent().getAttribute(SplResource.SPL_OFFSET_EFFECTS); - int effectIndex = ((DecNumber)getAttribute(Ability.ABILITY_FIRST_EFFECT_INDEX)).getValue() + getEffectsCount() - 1; + final IsNumeric effectOffset = (IsNumeric)getParent().getAttribute(SplResource.SPL_OFFSET_EFFECTS); + int effectIndex = ((IsNumeric)getAttribute(Ability.ABILITY_FIRST_EFFECT_INDEX)).getValue() + getEffectsCount() - 1; datatype.setOffset(effectOffset.getValue() + effectIndex * 48); } } public int getEffectsCount() { - return ((SectionCount)getAttribute(Ability.ABILITY_NUM_EFFECTS)).getValue(); + return ((IsNumeric)getAttribute(Ability.ABILITY_NUM_EFFECTS)).getValue(); } public void incEffectsIndex(int value) @@ -380,7 +379,7 @@ public void incEffectsIndex(int value) public int readEffects(ByteBuffer buffer, int off) throws Exception { - int effect_count = ((SectionCount)getAttribute(Ability.ABILITY_NUM_EFFECTS)).getValue(); + int effect_count = ((IsNumeric)getAttribute(Ability.ABILITY_NUM_EFFECTS)).getValue(); for (int i = 0; i < effect_count; i++) { Effect eff = new Effect(this, buffer, off, i); off = eff.getEndOffset(); diff --git a/src/org/infinity/resource/AbstractStruct.java b/src/org/infinity/resource/AbstractStruct.java index ebeb9a367..d9aaaab28 100644 --- a/src/org/infinity/resource/AbstractStruct.java +++ b/src/org/infinity/resource/AbstractStruct.java @@ -34,7 +34,6 @@ import org.infinity.gui.BrowserMenuBar; import org.infinity.gui.StructViewer; import org.infinity.resource.are.Actor; -import org.infinity.resource.cre.CreResource; import org.infinity.resource.dlg.AbstractCode; import org.infinity.resource.itm.ItmResource; import org.infinity.resource.key.ResourceEntry; @@ -52,6 +51,7 @@ public abstract class AbstractStruct extends AbstractTableModel implements Struc public static final String COMMON_UNKNOWN = "Unknown"; public static final String COMMON_UNUSED = "Unused"; public static final String COMMON_UNUSED_BYTES = "Unused bytes?"; + public static final String SUFFIX_UNUSED = " (unused)"; // Commonly used string arrays public static final String[] OPTION_NOYES = {"No", "Yes"}; @@ -135,8 +135,7 @@ else if (so.getValue() + superStruct.getExtraOffset() == datatype.getOffset()) { so.incValue(amount); } } - else if (!(so.getSection().equals(datatype.getClass()) || - (Profile.getEngine() == Profile.Engine.IWD2 && superStruct instanceof CreResource))) { + else if (!so.getSection().equals(datatype.getClass())) { so.incValue(amount); } } @@ -194,7 +193,6 @@ protected AbstractStruct(AbstractStruct superStruct, String name, ByteBuffer buf } } - // @Override public void close() throws Exception { @@ -205,9 +203,7 @@ public void close() throws Exception viewer.close(); } } - // - // @Override public boolean isReferenceable() { @@ -219,17 +215,13 @@ public void searchReferences(Component parent) { new ReferenceSearcher(getResourceEntry(), parent); } - // - // @Override public int compareTo(StructEntry o) { return getOffset() - o.getOffset(); } - // - // @Override public AbstractStruct clone() throws CloneNotSupportedException { @@ -341,9 +333,7 @@ public void setParent(AbstractStruct parent) addPropertyChangeListener(parent); } } - // - // @Override public int getRowCount() { @@ -433,9 +423,7 @@ public void setValueAt(Object value, int row, int column) } } } - // - // @Override public JComponent makeViewer(ViewableContainer container) { @@ -445,9 +433,7 @@ public JComponent makeViewer(ViewableContainer container) } return viewer; } - // - // @Override public void write(OutputStream os) throws IOException { @@ -456,7 +442,6 @@ public void write(OutputStream os) throws IOException e.write(os); } } - // @Override public String toString() @@ -485,6 +470,35 @@ public String toString() return sb.toString(); } + @Override + public int hashCode() + { + int hash = 7; + hash = 31 * hash + ((fields == null) ? 0 : fields.hashCode()); + hash = 31 * hash + Integer.hashCode(startoffset); + hash = 31 * hash + Integer.hashCode(endoffset); + hash = 31 * hash + Integer.hashCode(extraoffset); + return hash; + } + + @Override + public boolean equals(Object o) + { + if (o == this) { + return true; + } + if (!(o instanceof AbstractStruct)) { + return false; + } + AbstractStruct other = (AbstractStruct)o; + boolean retVal = (fields == null && other.fields == null) || + (fields != null && fields.equals(other.fields)); + retVal &= (startoffset == other.startoffset); + retVal &= (endoffset == other.endoffset); + retVal &= (extraoffset == other.extraoffset); + return retVal; + } + /** Returns the table row index where the specified AddRemovable structure can be inserted. */ public int getDatatypeIndex(AddRemovable addedEntry) { diff --git a/src/org/infinity/resource/AbstractVariable.java b/src/org/infinity/resource/AbstractVariable.java index 5dd8e7aa6..ac067cd9e 100644 --- a/src/org/infinity/resource/AbstractVariable.java +++ b/src/org/infinity/resource/AbstractVariable.java @@ -90,13 +90,11 @@ protected AbstractVariable(AbstractStruct superStruct, String name, ByteBuffer b super(superStruct, name, buffer, offset); } - // @Override public boolean canRemove() { return true; } - // @Override public int read(ByteBuffer buffer, int offset) throws Exception @@ -117,7 +115,7 @@ public int read(ByteBuffer buffer, int offset) throws Exception */ public Type getType() { - final Bitmap type = (Bitmap)getAttribute(VAR_TYPE, false); + final IsNumeric type = (IsNumeric)getAttribute(VAR_TYPE, false); return Type.values()[type.getValue()]; } /** @@ -136,7 +134,7 @@ public Type getType() */ public Object getValue(Type type) { - final Bitmap t = (Bitmap)getAttribute(VAR_TYPE, false); + final IsNumeric t = (IsNumeric)getAttribute(VAR_TYPE, false); return type.ordinal() == t.getValue() ? type.getValue(this) : null; } } diff --git a/src/org/infinity/resource/Effect.java b/src/org/infinity/resource/Effect.java index 962b537f4..d6b24d44e 100644 --- a/src/org/infinity/resource/Effect.java +++ b/src/org/infinity/resource/Effect.java @@ -119,7 +119,7 @@ public Object clone(boolean asV2) throws Exception retVal.setOffset(offset); ((AbstractStruct)retVal).realignStructOffsets(); } else { - retVal = (StructEntry)clone(); + retVal = clone(); } return retVal; diff --git a/src/org/infinity/resource/Effect2.java b/src/org/infinity/resource/Effect2.java index 77759eeff..446539457 100644 --- a/src/org/infinity/resource/Effect2.java +++ b/src/org/infinity/resource/Effect2.java @@ -113,8 +113,8 @@ public static int readCommon(List list, ByteBuffer buffer, int offs } else { list.add(new DecNumber(buffer, offset + 84, 4, EFFECT_IMPACT_PROJECTILE)); } - IdsBitmap slot_type = new IdsBitmap(buffer, offset + 88, 4, EFFECT_SOURCE_ITEM_SLOT, "SLOTS.IDS"); - slot_type.addIdsMapEntry(new IdsMapEntry(4294967295L, "NONE")); + IdsBitmap slot_type = new IdsBitmap(buffer, offset + 88, 4, EFFECT_SOURCE_ITEM_SLOT, "SLOTS.IDS", true, false, true); + slot_type.addIdsMapEntry(new IdsMapEntry(-1L, "NONE")); list.add(slot_type); list.add(new TextString(buffer, offset + 92, 32, EFFECT_VARIABLE_NAME)); list.add(new DecNumber(buffer, offset + 124, 4, EFFECT_CASTER_LEVEL)); @@ -208,7 +208,7 @@ public Object clone(boolean asV1) throws Exception retVal.setOffset(offset); ((AbstractStruct)retVal).realignStructOffsets(); } else { - retVal = (StructEntry)clone(); + retVal = clone(); } return retVal; diff --git a/src/org/infinity/resource/EffectFactory.java b/src/org/infinity/resource/EffectFactory.java index 30925d30c..8ed9f44e9 100644 --- a/src/org/infinity/resource/EffectFactory.java +++ b/src/org/infinity/resource/EffectFactory.java @@ -10,8 +10,10 @@ import java.util.Arrays; import java.util.EnumMap; import java.util.List; +import java.util.TreeMap; import java.util.stream.Stream; +import org.infinity.datatype.AnimateBitmap; import org.infinity.datatype.Bitmap; import org.infinity.datatype.ColorPicker; import org.infinity.datatype.ColorValue; @@ -36,9 +38,9 @@ import org.infinity.datatype.Unknown; import org.infinity.datatype.UnsignDecNumber; import org.infinity.datatype.UpdateListener; +import org.infinity.resource.are.AutomapNote; import org.infinity.resource.itm.ItmResource; import org.infinity.util.IdsMapEntry; -import org.infinity.util.LongIntegerHashMap; import org.infinity.util.StringTable; import org.infinity.util.Table2da; import org.infinity.util.Table2daCache; @@ -128,10 +130,10 @@ public static enum EffectEntry { } // contains IDS mappings for BGEE's opcode 319 "Item Usability" - public static final LongIntegerHashMap m_duration = new LongIntegerHashMap(); - public static final LongIntegerHashMap m_colorloc = new LongIntegerHashMap(); - public static final LongIntegerHashMap m_proj_iwd = new LongIntegerHashMap(); - public static final LongIntegerHashMap m_inctype = new LongIntegerHashMap(); + public static final TreeMap m_duration = new TreeMap<>(); + public static final TreeMap m_colorloc = new TreeMap<>(); + public static final TreeMap m_proj_iwd = new TreeMap<>(); + public static final TreeMap m_inctype = new TreeMap<>(); public static final String[] s_inctype = {"Increment", "Set", "Set % of"}; public static final String[] s_buttontype = { "Unknown", "Unknown", "Bard Song", "Cast Spell", "Find Traps", @@ -764,7 +766,7 @@ public static boolean updateOpcode(AbstractStruct struct) throws Exception if (struct != null) { EffectType effType = (EffectType)getEntry(struct, EffectEntry.IDX_OPCODE); if (effType != null) { - int opcode = ((EffectType)getEntry(struct, EffectEntry.IDX_OPCODE)).getValue(); + int opcode = ((IsNumeric)getEntry(struct, EffectEntry.IDX_OPCODE)).getValue(); switch (opcode) { case 1: // Modify attacks per round return updateOpcode1(struct); @@ -797,9 +799,9 @@ private static boolean updateOpcode1(AbstractStruct struct) throws Exception { if (struct != null) { if (Profile.isEnhancedEdition()) { - int opcode = ((EffectType)getEntry(struct, EffectEntry.IDX_OPCODE)).getValue(); + int opcode = ((IsNumeric)getEntry(struct, EffectEntry.IDX_OPCODE)).getValue(); if (opcode == 1) { - int param2 = ((Bitmap)getEntry(struct, EffectEntry.IDX_PARAM2)).getValue(); + int param2 = ((IsNumeric)getEntry(struct, EffectEntry.IDX_PARAM2)).getValue(); if (param2 == 2) { // Set % of replaceEntry(struct, EffectEntry.IDX_PARAM1, EffectEntry.OFS_PARAM1, new DecNumber(getEntryData(struct, EffectEntry.IDX_PARAM1), 0, 4, "Value")); @@ -817,24 +819,27 @@ private static boolean updateOpcode1(AbstractStruct struct) throws Exception // Effect type "Dexterity bonus" (15) private static boolean updateOpcode15(AbstractStruct struct) throws Exception { + boolean retVal = false; if (struct != null) { if (Profile.getEngine() == Profile.Engine.IWD || Profile.getEngine() == Profile.Engine.IWD2 || Profile.isEnhancedEdition() || (boolean)Profile.getProperty(Profile.Key.IS_GAME_TOBEX)) { - int opcode = ((EffectType)getEntry(struct, EffectEntry.IDX_OPCODE)).getValue(); - int param2 = ((Bitmap)getEntry(struct, EffectEntry.IDX_PARAM2)).getValue(); + boolean isV1 = (getEntry(struct, EffectEntry.IDX_OPCODE).getSize() == 2); + int opcode = ((IsNumeric)getEntry(struct, EffectEntry.IDX_OPCODE)).getValue(); + int param2 = ((IsNumeric)getEntry(struct, EffectEntry.IDX_PARAM2)).getValue(); if (opcode == 15) { - if (param2 == 3) { + if (isV1 && param2 == 3) { replaceEntry(struct, EffectEntry.IDX_PARAM1, EffectEntry.OFS_PARAM1, new DecNumber(getEntryData(struct, EffectEntry.IDX_PARAM1), 0, 4, AbstractStruct.COMMON_UNUSED)); + retVal = true; } else { replaceEntry(struct, EffectEntry.IDX_PARAM1, EffectEntry.OFS_PARAM1, new DecNumber(getEntryData(struct, EffectEntry.IDX_PARAM1), 0, 4, "Value")); + retVal = true; } - return true; } } } - return false; + return retVal; } // Effect type "Reset morale" (23) @@ -842,9 +847,9 @@ private static boolean updateOpcode23(AbstractStruct struct) throws Exception { if (struct != null) { if (Profile.isEnhancedEdition()) { - int opcode = ((EffectType)getEntry(struct, EffectEntry.IDX_OPCODE)).getValue(); + int opcode = ((IsNumeric)getEntry(struct, EffectEntry.IDX_OPCODE)).getValue(); if (opcode == 23) { - int special = ((Bitmap)getEntry(struct, EffectEntry.IDX_SPECIAL)).getValue(); + int special = ((IsNumeric)getEntry(struct, EffectEntry.IDX_SPECIAL)).getValue(); if (special == 0 ) { // Activate BG2 mode replaceEntry(struct, EffectEntry.IDX_PARAM1, EffectEntry.OFS_PARAM1, @@ -868,24 +873,27 @@ private static boolean updateOpcode23(AbstractStruct struct) throws Exception // Effect type "Strength bonus" (44) private static boolean updateOpcode44(AbstractStruct struct) throws Exception { + boolean retVal = false; if (struct != null) { if (Profile.getEngine() == Profile.Engine.IWD || Profile.getEngine() == Profile.Engine.IWD2 || Profile.isEnhancedEdition() || (boolean)Profile.getProperty(Profile.Key.IS_GAME_TOBEX)) { - int opcode = ((EffectType)getEntry(struct, EffectEntry.IDX_OPCODE)).getValue(); - int param2 = ((Bitmap)getEntry(struct, EffectEntry.IDX_PARAM2)).getValue(); + boolean isV1 = (getEntry(struct, EffectEntry.IDX_OPCODE).getSize() == 2); + int opcode = ((IsNumeric)getEntry(struct, EffectEntry.IDX_OPCODE)).getValue(); + int param2 = ((IsNumeric)getEntry(struct, EffectEntry.IDX_PARAM2)).getValue(); if (opcode == 44) { - if (param2 == 3) { + if (isV1 && param2 == 3) { replaceEntry(struct, EffectEntry.IDX_PARAM1, EffectEntry.OFS_PARAM1, new DecNumber(getEntryData(struct, EffectEntry.IDX_PARAM1), 0, 4, AbstractStruct.COMMON_UNUSED)); + retVal = true; } else { replaceEntry(struct, EffectEntry.IDX_PARAM1, EffectEntry.OFS_PARAM1, new DecNumber(getEntryData(struct, EffectEntry.IDX_PARAM1), 0, 4, "Value")); + retVal = true; } - return true; } } } - return false; + return retVal; } // Effect type "Disease" (78) @@ -893,9 +901,9 @@ private static boolean updateOpcode78(AbstractStruct struct) throws Exception { if (struct != null) { if (Profile.isEnhancedEdition()) { - int opcode = ((EffectType)getEntry(struct, EffectEntry.IDX_OPCODE)).getValue(); + int opcode = ((IsNumeric)getEntry(struct, EffectEntry.IDX_OPCODE)).getValue(); if (opcode == 78) { - int param2 = ((Bitmap)getEntry(struct, EffectEntry.IDX_PARAM2)).getValue(); + int param2 = ((IsNumeric)getEntry(struct, EffectEntry.IDX_PARAM2)).getValue(); switch (param2) { case 11: // Mold Touch/Single case 12: // Mold Touch/Decrement @@ -920,10 +928,10 @@ private static boolean updateOpcode78(AbstractStruct struct) throws Exception private static boolean updateOpcode232(AbstractStruct struct) throws Exception { if (struct != null) { - if (Profile.getEngine() == Profile.Engine.BG2 || Profile.isEnhancedEdition()) { - int opcode = ((EffectType)getEntry(struct, EffectEntry.IDX_OPCODE)).getValue(); + if (Profile.isEnhancedEdition()) { + int opcode = ((IsNumeric)getEntry(struct, EffectEntry.IDX_OPCODE)).getValue(); if (opcode == 232) { - int param2 = ((Bitmap)getEntry(struct, EffectEntry.IDX_PARAM2)).getValue(); + int param2 = ((IsNumeric)getEntry(struct, EffectEntry.IDX_PARAM2)).getValue(); switch (param2) { case 13: // Time of day replaceEntry(struct, EffectEntry.IDX_SPECIAL, EffectEntry.OFS_SPECIAL, @@ -956,7 +964,7 @@ private static boolean updateOpcode233(AbstractStruct struct) throws Exception { if (struct != null) { if (Profile.isEnhancedEdition()) { - int opcode = ((EffectType)getEntry(struct, EffectEntry.IDX_OPCODE)).getValue(); + int opcode = ((IsNumeric)getEntry(struct, EffectEntry.IDX_OPCODE)).getValue(); if (opcode == 233) { boolean signed = ((MultiNumber)getEntry(struct, EffectEntry.IDX_PARAM1)).isSigned(); int mode = ((IsNumeric)getEntry(struct, EffectEntry.IDX_PARAM2B)).getValue(); @@ -978,11 +986,11 @@ private static boolean updateOpcode319(AbstractStruct struct) throws Exception boolean retVal = false; if (struct != null) { if (Profile.getEngine() == Profile.Engine.BG2 || Profile.isEnhancedEdition()) { - int opcode = ((EffectType)getEntry(struct, EffectEntry.IDX_OPCODE)).getValue(); + int opcode = ((IsNumeric)getEntry(struct, EffectEntry.IDX_OPCODE)).getValue(); if (opcode == 319) { boolean isEEex = Profile.getProperty(Profile.Key.IS_GAME_EEEX); if (isEEex) { - int power = ((Bitmap)getEntry(struct, EffectEntry.IDX_POWER)).getValue(); + int power = ((IsNumeric)getEntry(struct, EffectEntry.IDX_POWER)).getValue(); if (power == 2 || power == 3) { SpellProtType param2 = new SpellProtType(getEntryData(struct, EffectEntry.IDX_PARAM2), 0, 4); param2.setName("EEex: " + param2.getName()); @@ -1022,9 +1030,9 @@ private static boolean updateOpcode328(AbstractStruct struct) throws Exception { if (struct != null) { if (Profile.isEnhancedEdition()) { - int opcode = ((EffectType)getEntry(struct, EffectEntry.IDX_OPCODE)).getValue(); + int opcode = ((IsNumeric)getEntry(struct, EffectEntry.IDX_OPCODE)).getValue(); if (opcode == 328) { - int special = ((Bitmap)getEntry(struct, EffectEntry.IDX_SPECIAL)).getValue(); + int special = ((IsNumeric)getEntry(struct, EffectEntry.IDX_SPECIAL)).getValue(); if (special == 1 && ResourceFactory.resourceExists("SPLSTATE.IDS")) { // Activate IWD2 mode replaceEntry(struct, EffectEntry.IDX_PARAM2, EffectEntry.OFS_PARAM2, @@ -1046,7 +1054,7 @@ private static boolean updateOpcode342(AbstractStruct struct) throws Exception { if (struct != null) { if (Profile.isEnhancedEdition()) { - int opcode = ((EffectType)getEntry(struct, EffectEntry.IDX_OPCODE)).getValue(); + int opcode = ((IsNumeric)getEntry(struct, EffectEntry.IDX_OPCODE)).getValue(); if (opcode == 342) { int param2 = ((IsNumeric)getEntry(struct, EffectEntry.IDX_PARAM2)).getValue(); StructEntry newEntry = null; @@ -1055,7 +1063,7 @@ private static boolean updateOpcode342(AbstractStruct struct) throws Exception 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"); + newEntry = new ColorValue(getEntryData(struct, EffectEntry.IDX_PARAM1), 0, 4, "Color", false); break; default: newEntry = new DecNumber(getEntryData(struct, EffectEntry.IDX_PARAM1), 0, 4, "Value"); @@ -2096,7 +2104,7 @@ private String makeEffectParamsGeneric(Datatype parent, ByteBuffer buffer, int o if (Profile.getEngine() == Profile.Engine.PST || Profile.getEngine() == Profile.Engine.IWD2) { s.add(new DecNumber(buffer, offset + 4, 4, AbstractStruct.COMMON_UNUSED)); } else { - final LongIntegerHashMap idsmap = new LongIntegerHashMap(); + final TreeMap idsmap = new TreeMap<>(); idsmap.put(0L, "Charmed (neutral)"); idsmap.put(1L, "Charmed (hostile)"); idsmap.put(2L, "Dire charmed (neutral)"); @@ -2149,7 +2157,7 @@ private String makeEffectParamsGeneric(Datatype parent, ByteBuffer buffer, int o break; case 7: // Set color - s.add(new ColorValue(buffer, offset, 4, "Color")); + s.add(new ColorValue(buffer, offset, 4, "Color", false)); s.add(new HashBitmap(buffer, offset + 4, 4, "Location", m_colorloc, false)); break; @@ -2218,7 +2226,7 @@ private String makeEffectParamsGeneric(Datatype parent, ByteBuffer buffer, int o if (Profile.getEngine() == Profile.Engine.IWD || Profile.getEngine() == Profile.Engine.IWD2 || Profile.isEnhancedEdition() || isTobEx) { int type = buffer.getInt(offset + 4); - s.add(new DecNumber(buffer, offset, 4, (type == 3) ? AbstractStruct.COMMON_UNUSED : "Value")); + s.add(new DecNumber(buffer, offset, 4, (isV1 && type == 3) ? AbstractStruct.COMMON_UNUSED : "Value")); Bitmap item = new Bitmap(buffer, offset + 4, 4, "Modifier type", new String[]{"Increment", "Set", "Set % of", "Cat's grace"}); s.add(item); @@ -2451,7 +2459,7 @@ private String makeEffectParamsGeneric(Datatype parent, ByteBuffer buffer, int o if (Profile.getEngine() == Profile.Engine.IWD || Profile.getEngine() == Profile.Engine.IWD2 || Profile.isEnhancedEdition()) { int type = buffer.getInt(offset + 4); - s.add(new DecNumber(buffer, offset, 4, (type == 3) ? AbstractStruct.COMMON_UNUSED : "Value")); + s.add(new DecNumber(buffer, offset, 4, (isV1 && type == 3) ? AbstractStruct.COMMON_UNUSED : "Value")); item = new Bitmap(buffer, offset + 4, 4, "Modifier type", new String[]{"Increment", "Set", "Set % of", "Bull's Strength"}); s.add(item); @@ -2493,7 +2501,7 @@ private String makeEffectParamsGeneric(Datatype parent, ByteBuffer buffer, int o break; case 53: // Animation change - s.add(new IdsBitmap(buffer, offset, 4, "Morph into", "ANIMATE.IDS")); + s.add(new AnimateBitmap(buffer, offset, 4, "Morph into")); s.add(new Bitmap(buffer, offset + 4, 4, "Morph type", new String[]{"Temporary change", "Remove temporary change", "Permanent change"})); @@ -2690,11 +2698,11 @@ private String makeEffectParamsGeneric(Datatype parent, ByteBuffer buffer, int o ids.addIdsMapEntry(new IdsMapEntry(0L, "None")); s.add(ids); } else { - LongIntegerHashMap idsmap; + TreeMap idsmap; if (Profile.getEngine() == Profile.Engine.IWD || Profile.getEngine() == Profile.Engine.IWD2) { idsmap = m_proj_iwd; } else { - idsmap = new LongIntegerHashMap(); + idsmap = new TreeMap<>(); idsmap.put(0L, "None"); idsmap.put(4L, "Arrow"); idsmap.put(9L, "Axe"); @@ -2916,7 +2924,7 @@ private String makeEffectParamsGeneric(Datatype parent, ByteBuffer buffer, int o break; case 135: // Polymorph - s.add(new IdsBitmap(buffer, offset, 4, "Animation", "ANIMATE.IDS")); + s.add(new AnimateBitmap(buffer, offset, 4, "Animation")); s.add(new Bitmap(buffer, offset + 4, 4, "Polymorph type", new String[]{"Change into", "Appearance only", "Appearance only", "Appearance only"})); @@ -2950,7 +2958,7 @@ private String makeEffectParamsGeneric(Datatype parent, ByteBuffer buffer, int o case 140: // Casting glow { - final LongIntegerHashMap m_castglow = new LongIntegerHashMap(); + final TreeMap m_castglow = new TreeMap<>(); if (Profile.isEnhancedEdition()) { m_castglow.put(0L, "Use projectile"); } @@ -3003,7 +3011,7 @@ private String makeEffectParamsGeneric(Datatype parent, ByteBuffer buffer, int o "Inventory 19", "Inventory 20", "Magic weapon", "Weapon 1", "Weapon 2", "Weapon 3", "Weapon 4"})); } else { - s.add(new IdsBitmap(buffer, offset, 4, "Slot", "SLOTS.IDS")); + s.add(new IdsBitmap(buffer, offset, 4, "Slot", "SLOTS.IDS", true, false, true)); } s.add(new DecNumber(buffer, offset + 4, 4, AbstractStruct.COMMON_UNUSED)); restype = "ITM"; @@ -3574,37 +3582,45 @@ private String makeEffectParamsBG2(Datatype parent, ByteBuffer buffer, int offse case 232: // Cast spell on condition { - s.add(new Bitmap(buffer, offset, 4, "Target", - new String[]{"Caster", "Last hit by", "Nearest enemy", "Anyone"})); + s.add(new Bitmap(buffer, offset, 4, "Target", new String[]{"Myself", "LastHitter", "NearestEnemyOf", "Anyone"})); + final List cndList = new ArrayList() {{ + add("HitBy([ANYONE]) / instant"); + add("See(NearestEnemyOf(Myself)) / per round"); + add("HPPercentLT(Myself,50) / per round"); + add("HPPercentLT(Myself,25) / per round"); + add("HPPercentLT(Myself,10) / per round"); + add("StateCheck(Myself,STATE_HELPLESS) / per round"); + add("StateCheck(Myself,STATE_POISONED) / per round"); + add("AttackedBy([ANYONE]) / instant"); + add("Range([ANYONE],4) / per round"); + add("Range([ANYONE],10) / per round"); + add("-Crash- / per round"); + add("TookDamage() / instant"); + if (Profile.isEnhancedEdition()) { + add("Killed([ANYONE]) / instant"); + add("TimeOfDay('Special') / per round"); + add("Range([ANYONE],'Special') / per round"); + add("StateCheck(Myself,'Special') / per round"); + add("Died(Myself) / instant"); + add("Died([ANYONE]) / instant"); + add("TurnedBy([ANYONE]) / instant"); + add("HPLT(Myself,'Special') / per round"); + add("HPPercentLT(Myself,'Special') / per round"); + add("CheckSpellState(Myself,'Special') / per round"); + } + }}; + final String[] conditions = cndList.toArray(new String[cndList.size()]); if (Profile.isEnhancedEdition()) { - Bitmap item = new Bitmap(buffer, offset + 4, 4, "Condition", - new String[]{"Target hit", "Enemy sighted", "HP below 50%", - "HP below 25%", "HP below 10%", "If helpless", - "If poisoned", "When attacked", - "Target in range 4'", "Target in range 10'", - "Unknown (every round)", "Took damage", "Actor killed", - "Time of day is 'Special'", - "Target in 'Special' range", - "Target's state is 'Special'", "Target dies", - "Target died", "Target turned by", - "Target HP < 'Special'", "Target HP % < 'Special'", - "Target's spell state is 'Special'"}); + Bitmap item = new Bitmap(buffer, offset + 4, 4, "Condition", conditions); s.add(item); if (parent != null && parent instanceof UpdateListener) { item.addUpdateListener((UpdateListener)parent); } + } else if (isTobEx) { + s.add(new Bitmap(buffer, offset + 4, 2, "Condition", conditions)); + s.add(new DecNumber(buffer, offset + 6, 2, "Trigger check period")); } else { - String[] condition = new String[]{"Target hit", "Enemy sighted", "HP below 50%", - "HP below 25%", "HP below 10%", "If helpless", - "If poisoned", "When attacked", - "Target in range 4'", "Target in range 10'", - "Unknown (every round)", "Took damage"}; - if (isTobEx) { - s.add(new Bitmap(buffer, offset + 4, 2, "Condition", condition)); - s.add(new DecNumber(buffer, offset + 6, 2, "Trigger check period")); - } else { - s.add(new Bitmap(buffer, offset + 4, 4, "Condition", condition)); - } + s.add(new Bitmap(buffer, offset + 4, 4, "Condition", conditions)); } restype = "SPL"; break; @@ -3652,7 +3668,7 @@ private String makeEffectParamsBG2(Datatype parent, ByteBuffer buffer, int offse case 237: // Set image type { - final LongIntegerHashMap map = new LongIntegerHashMap(); + final TreeMap map = new TreeMap<>(); map.put(0L, "Player1"); map.put(1L, "Player2"); map.put(2L, "Player3"); @@ -3687,7 +3703,7 @@ private String makeEffectParamsBG2(Datatype parent, ByteBuffer buffer, int offse case 241: // Control creature { - final LongIntegerHashMap map = new LongIntegerHashMap(); + final TreeMap map = new TreeMap<>(); map.put(0L, "Charmed (neutral)"); map.put(1L, "Charmed (hostile)"); map.put(2L, "Dire charmed (neutral)"); @@ -3723,7 +3739,7 @@ private String makeEffectParamsBG2(Datatype parent, ByteBuffer buffer, int offse case 248: // Melee hit effect s.add(new DecNumber(buffer, offset, 4, AbstractStruct.COMMON_UNUSED)); if (Profile.isEnhancedEdition()) { - LongIntegerHashMap map = new LongIntegerHashMap<>(); + TreeMap map = new TreeMap<>(); map.put(0L, "Default"); map.put(4L, "Fists only"); s.add(new HashBitmap(buffer, offset + 4, 4, "Type", map, false)); @@ -3745,6 +3761,16 @@ private String makeEffectParamsBG2(Datatype parent, ByteBuffer buffer, int offse break; case 253: // Set automap note + s.add(new StringRef(buffer, offset, "String")); + if (Profile.isEnhancedEdition() && ResourceFactory.resourceExists("MAPNOTES.IDS")) { + s.add(new IdsBitmap(buffer, offset + 4, 4, "Color", "MAPNOTES.IDS", false)); + } else if (Profile.getEngine() == Profile.Engine.BG2) { + s.add(new Bitmap(buffer, offset + 4, 4, "Color", AutomapNote.s_flag)); + } else { + s.add(new DecNumber(buffer, offset + 4, 4, AbstractStruct.COMMON_UNUSED)); + } + break; + case 254: // Remove automap note case 267: // Disable display string s.add(new StringRef(buffer, offset, "String")); @@ -3957,7 +3983,7 @@ private String makeEffectParamsBG2(Datatype parent, ByteBuffer buffer, int offse case 303: // Backstab every hit s.add(new DecNumber(buffer, offset, 4, AbstractStruct.COMMON_UNUSED)); if (isTobEx) { - LongIntegerHashMap idsmap = new LongIntegerHashMap(); + TreeMap idsmap = new TreeMap<>(); idsmap.put(0L, "Normal conditions"); idsmap.put(1L, "Ignore visual state and position"); idsmap.put(2L, "Ignore visual state only"); @@ -4270,7 +4296,7 @@ private String makeEffectParamsBG2(Datatype parent, ByteBuffer buffer, int offse "Personal space"}); switch (bmp.getValue()) { 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; + case 2: s.add(new ColorValue(buffer, offset, 4, "Color", false)); break; default: s.add(new DecNumber(buffer, offset, 4, "Value")); } s.add(bmp); @@ -4677,7 +4703,7 @@ private String makeEffectParamsPST(Datatype parent, ByteBuffer buffer, int offse case 195: // Tint screen { - final LongIntegerHashMap m_fadeType = new LongIntegerHashMap(); + final TreeMap m_fadeType = new TreeMap<>(); m_fadeType.put(0L, "Quick fade light->dark->light"); m_fadeType.put(1L, "Quick fade light->dark->light"); m_fadeType.put(2L, "Quick fade light->dark, instant fade light"); @@ -4970,7 +4996,7 @@ private String makeEffectParamsIWD(Datatype parent, ByteBuffer buffer, int offse case 263: // Evil turn undead { - final LongIntegerHashMap map = new LongIntegerHashMap(); + final TreeMap map = new TreeMap<>(); map.put(0L, "Charmed (neutral)"); map.put(1L, "Charmed (hostile)"); map.put(2L, "Dire charmed (neutral)"); @@ -5214,7 +5240,7 @@ private String makeEffectParamsIWD2(Datatype parent, ByteBuffer buffer, int offs case 263: // Evil turn undead { - final LongIntegerHashMap map = new LongIntegerHashMap(); + final TreeMap map = new TreeMap<>(); map.put(0L, "Charmed (neutral)"); map.put(1L, "Charmed (hostile)"); map.put(2L, "Dire charmed (neutral)"); @@ -5539,12 +5565,17 @@ private int makeEffectParam25(Datatype parent, ByteBuffer buffer, int offset, Li boolean isEEex = Profile.getProperty(Profile.Key.IS_GAME_EEEX); switch (effectType) { case 12: // Damage - s.add(new Flag(buffer, offset, 4, EFFECT_SPECIAL, new String[] { - "Default", "Drain HP to caster", "Transfer HP to target", - "Fist damage only", "Drain HP to caster (non-cumulative)", null, - "Suppress damage feedback", "Drain HP (limited to cur. HP of target)", - "Drain HP (limited to max. HP of caster)", "Save for half", - "Made save", "Does not wake sleepers"})); + s.add(new Flag(buffer, offset, 4, "Flags", new String[] { + "Default", + "Transfer HP to caster (cumulative)*;Bits 0, 1, 3 and 4 are mutually exclusive. Cumulative temporary extra HP.", + "Transfer HP to target (cumulative)*;Bits 0, 1, 3 and 4 are mutually exclusive. Cumulative temporary extra HP.", + "Fist damage only", + "Transfer HP to caster (non-cumulative)*;Bits 0, 1, 3 and 4 are mutually exclusive. Non-cumulative temporary extra HP.", + "Transfer HP to target (non-cumulative)*;Bits 0, 1, 3 and 4 are mutually exclusive. Non-cumulative temporary extra HP.", + "Suppress damage feedback", + "Limit to cur. HP of target minus MINHP*;Bits 1 and 4 switch target -> caster.", + "Limit to cur./max. HP difference of caster*;Bits 1 and 4 switch caster -> target.", + "Save for half", "Made save", "Does not wake sleepers"})); break; case 18: // Maximum HP bonus diff --git a/src/org/infinity/resource/Profile.java b/src/org/infinity/resource/Profile.java index 57b74ebd8..1e9eb9656 100644 --- a/src/org/infinity/resource/Profile.java +++ b/src/org/infinity/resource/Profile.java @@ -7,7 +7,10 @@ import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; +import java.io.EOFException; +import java.io.File; import java.io.IOException; +import java.io.RandomAccessFile; import java.nio.charset.StandardCharsets; import java.nio.file.DirectoryStream; import java.nio.file.FileSystem; @@ -44,9 +47,8 @@ import org.infinity.gui.BrowserMenuBar; import org.infinity.gui.ViewerUtil; import org.infinity.resource.key.ResourceEntry; -import org.infinity.resource.key.ResourceTreeFolder; import org.infinity.resource.key.ResourceTreeModel; -import org.infinity.util.ObjectString; +import org.infinity.util.DataString; import org.infinity.util.Platform; import org.infinity.util.Table2da; import org.infinity.util.Table2daCache; @@ -157,6 +159,8 @@ public enum Key { GET_GLOBAL_DIALOG_NAME, /** Property: ({@code String}) Returns "{@code dialogf.tlk}". */ GET_GLOBAL_DIALOG_NAME_FEMALE, + /** Property: ({@code Boolean}) Returns whether NI checks for case-sensitive filesystems before accessing files. */ + GET_GLOBAL_FILE_CASE_CHECK, // Static properties which require an additional parameter. /** Property: ({@code String}) Returns the game's title. Extra parameter: Desired {@link Game}. */ @@ -237,7 +241,7 @@ public enum Key { * (Sorted by priority in descending order for Enhanced Editions, * sorted by entries found in ini file for non-enhanced games) */ GET_GAME_BIFF_FOLDERS, - /** Property: {@code Map} Map of "Equipped appearance" codes with associated + /** Property: ({@code Map}) Map of "Equipped appearance" codes with associated * descriptions. Map is generated on first call of {@code getEquippedAppearanceMap()}. */ GET_GAME_EQUIPPED_APPEARANCES, @@ -249,6 +253,14 @@ public enum Key { IS_GAME_EEEX, /** Property: ({@code Boolean}) Has type of current game been forcibly set? */ IS_FORCED_GAME, + /** Property: ({@code Integer}) Returns the Infinity Animations installed version: + *
    +     *            0: not installed
    +     *            1: old IA format (v5 or earlier)
    +     *            2: new format (v6 or later)
    +     * 
    + */ + GET_INFINITY_ANIMATIONS, /** Property: ({@code Boolean}) Are {@code 2DA} resources supported? */ IS_SUPPORTED_2DA, @@ -413,6 +425,8 @@ public enum Key { IS_SUPPORTED_KITS, /** Property: ({@code String}) The name of the ALIGNMENT IDS resource. */ GET_IDS_ALIGNMENT, + /** Property: ({@code String}) The name of the .GAM file that is stored in saved games. */ + GET_GAM_NAME, /** Property: ({@code Boolean}) Indices whether overlays in tilesets are stenciled. */ IS_TILESET_STENCILED, } @@ -420,13 +434,13 @@ public enum Key { // Container for Property entries private static final EnumMap properties = new EnumMap<>(Key.class); // Unique titles for all supported games - private static final EnumMap GAME_TITLE = new EnumMap(Game.class); + private static final EnumMap GAME_TITLE = new EnumMap<>(Game.class); // List of supported extra folders for all supported games - private static final EnumMap> GAME_EXTRA_FOLDERS = new EnumMap>(Game.class); + private static final EnumMap> GAME_EXTRA_FOLDERS = new EnumMap<>(Game.class); // List of supported saved game folders for all supported games - private static final EnumMap> GAME_SAVE_FOLDERS = new EnumMap>(Game.class); + private static final EnumMap> GAME_SAVE_FOLDERS = new EnumMap<>(Game.class); // Home folder name for Enhanced Edition Games - private static final EnumMap GAME_HOME_FOLDER = new EnumMap(Game.class); + private static final EnumMap GAME_HOME_FOLDER = new EnumMap<>(Game.class); // Set of resource extensions supported by Infinity Engine games private static final HashSet SUPPORTED_RESOURCE_TYPES = new HashSet<>(); private static final HashMap KNOWN_EQUIPPED_APPEARANCE = new HashMap<>(); @@ -960,7 +974,7 @@ public static String[] getAvailableResourceTypes() */ public static String[] getAvailableResourceTypes(boolean ignoreGame) { - ArrayList list = new ArrayList(); + ArrayList list = new ArrayList<>(); if (ignoreGame || (Boolean)getProperty(Key.IS_SUPPORTED_2DA)) { list.add("2DA"); } if (ignoreGame || @@ -1124,7 +1138,7 @@ public static Map getEquippedAppearanceMap() // space for "no type" codes.add(" "); - retVal = new TreeMap(); + retVal = new TreeMap<>(); for (final String code: codes) { String desc = KNOWN_EQUIPPED_APPEARANCE.get(code); if (desc != null) { @@ -1334,7 +1348,7 @@ private static void initStaticProperties() addEntry(Key.GET_GLOBAL_NEARINFINITY_VERSION, Type.STRING, NearInfinity.getVersion()); // setting list of supported games and associated data - List gameList = new ArrayList(); + List gameList = new ArrayList<>(); Collections.addAll(gameList, Game.values()); addEntry(Key.GET_GLOBAL_GAMES, Type.LIST, gameList); @@ -1352,6 +1366,9 @@ private static void initStaticProperties() // setting dialog.tlk file names addEntry(Key.GET_GLOBAL_DIALOG_NAME, Type.STRING, "dialog.tlk"); addEntry(Key.GET_GLOBAL_DIALOG_NAME_FEMALE, Type.STRING, "dialogf.tlk"); + + // setting misc. properties + addEntry(Key.GET_GLOBAL_FILE_CASE_CHECK, Type.BOOLEAN, Boolean.valueOf(true)); } // Initializes a list of potential executable filenames for each game and platform @@ -1359,7 +1376,7 @@ private static void initDefaultGameBinaries() { DEFAULT_GAME_BINARIES.clear(); EnumMap> osMap; - List emptyList = new ArrayList<>();; + List emptyList = new ArrayList<>(); List list; // BG1 & BG1TotSC (Windows) @@ -1675,7 +1692,7 @@ private void initGame() throws Exception Game game = null; // Preparing available root paths - List gameRoots = new ArrayList(); + List gameRoots = new ArrayList<>(); if (Profile.getGameRoot() != null) { gameRoots.add(Profile.getGameRoot()); } @@ -1972,7 +1989,7 @@ private void initRootDirs() addEntry(Key.GET_GAME_LANG_FOLDER, Type.PATH, langRoot); List langPaths = ResourceFactory.getAvailableGameLanguages(); addEntry(Key.GET_GAME_LANG_FOLDERS_AVAILABLE, Type.LIST, langPaths); - List languages = new ArrayList(langPaths.size()); + List languages = new ArrayList<>(langPaths.size()); langPaths.forEach((path) -> languages.add(path.getFileName().toString())); addEntry(Key.GET_GAME_LANG_FOLDER_NAMES_AVAILABLE, Type.LIST, languages); listRoots.add(langRoot); @@ -2272,6 +2289,21 @@ private void initFeatures() // the actual name of the "Alignment" IDS resource addEntry(Key.GET_IDS_ALIGNMENT, Type.STRING, (engine == Engine.IWD2) ? "ALIGNMNT.IDS" : "ALIGNMEN.IDS"); + // the GAM filename used in saved games + switch (engine) { + case IWD: + addEntry(Key.GET_GAM_NAME, Type.STRING, "ICEWIND.GAM"); + break; + case IWD2: + addEntry(Key.GET_GAM_NAME, Type.STRING, "ICEWIND2.GAM"); + break; + case PST: + addEntry(Key.GET_GAM_NAME, Type.STRING, "TORMENT.GAM"); + break; + default: + addEntry(Key.GET_GAM_NAME, Type.STRING, "BALDUR.GAM"); + } + // display mode of overlays in tilesets addEntry(Key.IS_TILESET_STENCILED, Type.BOOLEAN, (engine == Engine.BG2 || game == Game.BG2EE)); @@ -2291,6 +2323,52 @@ private void initFeatures() addEntry(Key.IS_GAME_EEEX, Type.BOOLEAN, Boolean.FALSE); } + // Is Infinity Animations installed? + boolean isIAv1 = false; + boolean isIAv2 = false; + if (engine == Engine.BG2) { + Path exe = FileManager.queryExisting(getGameRoot(), "bgmain.exe"); + if (exe != null) { + File exeFile = exe.toFile(); + if (exeFile != null && exeFile.length() == 7839790L) { + try (RandomAccessFile raf = new RandomAccessFile(exeFile, "r")) { + // checking key signatures + final int[] sigCheckV1 = { 0x3db6d84, 0xc6004c48, 0x54464958, 0x004141de, 0xf9 }; + final int[] sigCheckV2 = { 0x3db6d84, 0x34004c48, 0x54464958, 0x0041412d, 0xf9 }; + long ofs[] = { 0x40742cL, 0x40a8daL, 0x7536e7L, 0x407713L }; + int sig[] = new int[ofs.length + 1]; + for (int i = 0; i < ofs.length; i++) { + // reading int signatures + raf.seek(ofs[i]); + int b1 = raf.read(); + int b2 = raf.read(); + int b3 = raf.read(); + int b4 = raf.read(); + if ((b1 | b2 | b3 | b4) < 0) { + throw new EOFException(); + } + sig[i] = b1 | (b2 << 8) | (b3 << 16) | (b4 << 24); + } + + // reading byte signature + raf.seek(0x4595c9L); + sig[ofs.length] = raf.read(); + + isIAv1 = Arrays.equals(sig, sigCheckV1); + isIAv2 = Arrays.equals(sig, sigCheckV2); + } catch (IOException e) { + } + } + } + } + if (isIAv1) { + addEntry(Key.GET_INFINITY_ANIMATIONS, Type.INTEGER, Integer.valueOf(1)); // v5 or earlier + } else if (isIAv2) { + addEntry(Key.GET_INFINITY_ANIMATIONS, Type.INTEGER, Integer.valueOf(2)); // v6 or later + } else { + addEntry(Key.GET_INFINITY_ANIMATIONS, Type.INTEGER, Integer.valueOf(0)); // not installed + } + // Add campaign-specific extra folders initCampaigns(); } @@ -2364,7 +2442,7 @@ private void initCampaigns() if (FileEx.create(path).isDirectory()) { String folderName = path.getFileName().toString(); if (model.getFolder(folderName) == null) { - model.addDirectory((ResourceTreeFolder)model.getRoot(), path, false); + model.addDirectory(model.getRoot(), path, false); } } } @@ -2383,20 +2461,20 @@ private List initDlc(Path rootDir, Path homeDir) return retVal; } - List gameFolders = new ArrayList<>(); + List> gameFolders = new ArrayList<>(); // Getting potential DLC folders (search order is important) if (rootDir != null && FileEx.create(rootDir).isDirectory()) { - gameFolders.add(new ObjectString("mod", rootDir.resolve("workshop"))); - gameFolders.add(new ObjectString("zip", rootDir.resolve("dlc"))); - gameFolders.add(new ObjectString("zip", rootDir)); + gameFolders.add(DataString.with("mod", rootDir.resolve("workshop"))); + gameFolders.add(DataString.with("zip", rootDir.resolve("dlc"))); + gameFolders.add(DataString.with("zip", rootDir)); } if (homeDir != null && FileEx.create(homeDir).isDirectory()) { - gameFolders.add(new ObjectString("zip", homeDir)); + gameFolders.add(DataString.with("zip", homeDir)); } - for (final ObjectString root: gameFolders) { + for (final DataString root: gameFolders) { String ext = root.getString(); - Path dir = root.getObject(); + Path dir = root.getData(); if (dir != null && FileEx.create(dir).isDirectory()) { List list = new ArrayList<>(); try (DirectoryStream dstream = Files.newDirectoryStream(dir)) { diff --git a/src/org/infinity/resource/ResourceFactory.java b/src/org/infinity/resource/ResourceFactory.java index 5af28ee65..edf5ed2f6 100644 --- a/src/org/infinity/resource/ResourceFactory.java +++ b/src/org/infinity/resource/ResourceFactory.java @@ -26,8 +26,8 @@ import java.util.List; import java.util.Locale; import java.util.regex.Pattern; -import javax.swing.JComponent; +import javax.swing.JComponent; import javax.swing.JFileChooser; import javax.swing.JOptionPane; import javax.swing.filechooser.FileSystemView; @@ -85,11 +85,11 @@ import org.infinity.resource.video.WbmResource; import org.infinity.resource.wed.WedResource; import org.infinity.resource.wmp.WmpResource; -import org.infinity.util.StaticSimpleXorDecryptor; import org.infinity.util.CreMapCache; import org.infinity.util.DynamicArray; import org.infinity.util.IdsMapCache; import org.infinity.util.Misc; +import org.infinity.util.StaticSimpleXorDecryptor; import org.infinity.util.io.FileEx; import org.infinity.util.io.FileManager; import org.infinity.util.io.FileWatcher; @@ -263,7 +263,7 @@ public static Resource getResource(ResourceEntry entry, String forcedExtension) if (cls != null) { Constructor con = cls.getConstructor(ResourceEntry.class); if (con != null) - res = (Resource)con.newInstance(entry); + res = con.newInstance(entry); } } catch (Exception e) { if (NearInfinity.getInstance() != null && !BrowserMenuBar.getInstance().ignoreReadErrors()) { @@ -783,14 +783,16 @@ static Path getHomeRoot(boolean allowMissing) try { Process p = Runtime.getRuntime().exec("reg query \"HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders\" /v personal"); p.waitFor(); - InputStream in = p.getInputStream(); - byte[] b = new byte[in.available()]; - in.read(b); - in.close(); + byte[] b; + try (InputStream in = p.getInputStream()) { + b = new byte[in.available()]; + in.read(b); + } String[] splitted = new String(b).split("\\s\\s+"); userPrefix = splitted[splitted.length-1]; userPath = FileManager.resolve(userPrefix, EE_DIR); } catch (Throwable t) { + t.printStackTrace(); return null; } } else if (osName.contains("mac") || osName.contains("darwin")) { diff --git a/src/org/infinity/resource/StructureFactory.java b/src/org/infinity/resource/StructureFactory.java index 08360b4d2..d83dbfbed 100644 --- a/src/org/infinity/resource/StructureFactory.java +++ b/src/org/infinity/resource/StructureFactory.java @@ -36,7 +36,7 @@ public static enum ResType { RES_VEF, RES_VVC, RES_WED, RES_WFX, RES_WMAP } - private static final EnumMap resExt = new EnumMap(ResType.class); + private static final EnumMap resExt = new EnumMap<>(ResType.class); private static StructureFactory sfactory; static { @@ -178,7 +178,7 @@ public ResourceStructure createStructure(ResType type, String fileName, Window p } } - private ResourceStructure create2DA() throws StructureException + private ResourceStructure create2DA() { ResourceStructure s_2da = new ResourceStructure(); final String s = normalizeString("2DA V1.0\n0\n COLUMN1\nROW1 0\n"); @@ -187,7 +187,7 @@ private ResourceStructure create2DA() throws StructureException return s_2da; } - private ResourceStructure createARE(String fileName) throws StructureException + private ResourceStructure createARE(String fileName) { ResourceStructure s_are = new ResourceStructure(); @@ -242,7 +242,7 @@ private ResourceStructure createARE(String fileName) throws StructureException return s_are; } - private ResourceStructure createBAF() throws StructureException + private ResourceStructure createBAF() { ResourceStructure s_baf = new ResourceStructure(); final String s = "// Empty BCS script" + Misc.LINE_SEPARATOR; @@ -251,7 +251,7 @@ private ResourceStructure createBAF() throws StructureException return s_baf; } - private ResourceStructure createBCS() throws StructureException + private ResourceStructure createBCS() { ResourceStructure s_bcs = new ResourceStructure(); final String s = normalizeString("SC\nSC\n"); @@ -295,7 +295,7 @@ private ResourceStructure createCHR(Window parent) throws StructureException return cancelOperation(); } - private ResourceStructure createCRE() throws StructureException + private ResourceStructure createCRE() { final String[] version = {"V1.0", "V1.2", "V2.2", "V9.0"}; final int[] ofs = {0x2d4, 0x378, 0, 0x33c}; @@ -388,7 +388,7 @@ private ResourceStructure createCRE() throws StructureException return s_cre; } - private ResourceStructure createEFF() throws StructureException + private ResourceStructure createEFF() { ResourceStructure s_eff = new ResourceStructure(); s_eff.add(ResourceStructure.ID_STRING, 4, "EFF "); // Signature @@ -400,7 +400,7 @@ private ResourceStructure createEFF() throws StructureException return s_eff; } - private ResourceStructure createIDS() throws StructureException + private ResourceStructure createIDS() { ResourceStructure s_ids = new ResourceStructure(); final String s = normalizeString("1\n0 Identifier1\n"); @@ -409,7 +409,7 @@ private ResourceStructure createIDS() throws StructureException return s_ids; } - private ResourceStructure createINI() throws StructureException + private ResourceStructure createINI() { // TODO: distinguish between games ResourceStructure s_ini = new ResourceStructure(); @@ -419,7 +419,7 @@ private ResourceStructure createINI() throws StructureException return s_ini; } - private ResourceStructure createITM() throws StructureException + private ResourceStructure createITM() { final String[] version = {"V1 ", "V1.1", "V2.0"}; final int[] ofs = {0x72, 0x9a, 0x82}; @@ -485,7 +485,7 @@ private ResourceStructure createRES(Window parent) throws StructureException return cancelOperation(); } - private ResourceStructure createSPL() throws StructureException + private ResourceStructure createSPL() { final String[] version = {"V1 ", "V2.0"}; final int[] ofs = {0x72, 0x82}; @@ -533,7 +533,7 @@ private ResourceStructure createSRC() throws StructureException return unsupportedFormat(ResType.RES_SRC); } - private ResourceStructure createSTO() throws StructureException + private ResourceStructure createSTO() { final String[] version = {"V1.0", "V1.1", "V9.0"}; final int[] ofs = {0x9c, 0x9c, 0xf0}; @@ -564,7 +564,7 @@ private ResourceStructure createSTO() throws StructureException return s_sto; } - private ResourceStructure createVEF() throws StructureException + private ResourceStructure createVEF() { ResourceStructure s_vef = new ResourceStructure(); s_vef.add(ResourceStructure.ID_STRING, 4, "VEF "); // Signature @@ -577,7 +577,7 @@ private ResourceStructure createVEF() throws StructureException return s_vef; } - private ResourceStructure createVVC() throws StructureException + private ResourceStructure createVVC() { ResourceStructure s_vvc = new ResourceStructure(); s_vvc.add(ResourceStructure.ID_STRING, 4, "VVC "); // Signature @@ -587,7 +587,7 @@ private ResourceStructure createVVC() throws StructureException return s_vvc; } - private ResourceStructure createWED() throws StructureException + private ResourceStructure createWED() { ResourceStructure s_wed = new ResourceStructure(); s_wed.add(ResourceStructure.ID_STRING, 4, "WED "); // Signature @@ -612,7 +612,7 @@ private ResourceStructure createWED() throws StructureException return s_wed; } - private ResourceStructure createWFX() throws StructureException + private ResourceStructure createWFX() { ResourceStructure s_wfx = new ResourceStructure(); s_wfx.add(ResourceStructure.ID_STRING, 4, "WFX "); // Signature @@ -622,7 +622,7 @@ private ResourceStructure createWFX() throws StructureException return s_wfx; } - private ResourceStructure createWMAP() throws StructureException + private ResourceStructure createWMAP() { ResourceStructure s_wmp = new ResourceStructure(); s_wmp.add(ResourceStructure.ID_STRING, 4, "WMAP"); // Signature diff --git a/src/org/infinity/resource/are/AreResource.java b/src/org/infinity/resource/are/AreResource.java index dac3b55e5..cc148cf09 100644 --- a/src/org/infinity/resource/are/AreResource.java +++ b/src/org/infinity/resource/are/AreResource.java @@ -385,7 +385,6 @@ public AreResource(ResourceEntry entry) throws Exception super(entry); } - // @Override public void close() throws Exception { @@ -395,9 +394,7 @@ public void close() throws Exception areaViewer = null; } } - // - // @Override public AddRemovable[] getPrototypes() throws Exception { @@ -425,9 +422,7 @@ public AddRemovable confirmAddEntry(AddRemovable entry) throws Exception { return entry; } - // - // @Override public int getViewerTabCount() { @@ -472,17 +467,13 @@ public boolean viewerTabAddedBefore(int index) { return (index == 0); } - // - // @Override public void write(OutputStream os) throws IOException { super.writeFlatFields(os); } - // - // @Override protected void viewerInitialized(StructViewer viewer) { @@ -582,9 +573,7 @@ protected void datatypeRemovedInChild(AbstractStruct child, AddRemovable datatyp hexViewer.dataModified(); } } - // - // @Override public int read(ByteBuffer buffer, int offset) throws Exception { @@ -905,7 +894,6 @@ else if (Profile.getEngine() == Profile.Engine.IWD2) { } return endoffset; } - // private void updateActorCREOffsets() { @@ -977,8 +965,8 @@ public static boolean matchSearchOptions(ResourceEntry entry, SearchOptions sear Object o; // preparing substructures - DecNumber ofs = (DecNumber)are.getAttribute(ARE_OFFSET_ACTORS, false); - DecNumber cnt = (DecNumber)are.getAttribute(ARE_NUM_ACTORS, false); + IsNumeric ofs = (IsNumeric)are.getAttribute(ARE_OFFSET_ACTORS, false); + IsNumeric cnt = (IsNumeric)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++) { @@ -988,8 +976,8 @@ public static boolean matchSearchOptions(ResourceEntry entry, SearchOptions sear actors = new Actor[0]; } - ofs = (DecNumber)are.getAttribute(ARE_OFFSET_ANIMATIONS, false); - cnt = (DecNumber)are.getAttribute(ARE_NUM_ANIMATIONS, false); + ofs = (IsNumeric)are.getAttribute(ARE_OFFSET_ANIMATIONS, false); + cnt = (IsNumeric)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++) { @@ -999,15 +987,15 @@ public static boolean matchSearchOptions(ResourceEntry entry, SearchOptions sear animations = new Animation[0]; } - ofs = (DecNumber)are.getAttribute(ARE_OFFSET_CONTAINERS, false); - cnt = (DecNumber)are.getAttribute(ARE_NUM_CONTAINERS, false); + ofs = (IsNumeric)are.getAttribute(ARE_OFFSET_CONTAINERS, false); + cnt = (IsNumeric)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); + IsNumeric cnt2 = (IsNumeric)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++) { diff --git a/src/org/infinity/resource/are/AutomapNote.java b/src/org/infinity/resource/are/AutomapNote.java index aa4dd1358..39cd27db6 100644 --- a/src/org/infinity/resource/are/AutomapNote.java +++ b/src/org/infinity/resource/are/AutomapNote.java @@ -8,10 +8,12 @@ import org.infinity.datatype.Bitmap; import org.infinity.datatype.DecNumber; +import org.infinity.datatype.IdsBitmap; import org.infinity.datatype.StringRef; import org.infinity.datatype.Unknown; import org.infinity.resource.AbstractStruct; import org.infinity.resource.AddRemovable; +import org.infinity.resource.ResourceFactory; import org.infinity.util.io.StreamUtils; public final class AutomapNote extends AbstractStruct implements AddRemovable @@ -56,7 +58,11 @@ public int read(ByteBuffer buffer, int offset) throws Exception addField(new DecNumber(buffer, offset + 2, 2, ARE_AUTOMAP_LOCATION_Y)); addField(new StringRef(buffer, offset + 4, ARE_AUTOMAP_TEXT)); addField(new Bitmap(buffer, offset + 8, 2, ARE_AUTOMAP_TEXT_LOCATION, s_source)); - addField(new Bitmap(buffer, offset + 10, 2, ARE_AUTOMAP_MARKER_COLOR, s_flag)); + if (ResourceFactory.resourceExists("MAPNOTES.IDS")) { + addField(new IdsBitmap(buffer, offset + 10, 2, ARE_AUTOMAP_MARKER_COLOR, "MAPNOTES.IDS", false)); + } else { + addField(new Bitmap(buffer, offset + 10, 2, ARE_AUTOMAP_MARKER_COLOR, s_flag)); + } addField(new DecNumber(buffer, offset + 12, 4, ARE_AUTOMAP_CONTROL_ID)); addField(new Unknown(buffer, offset + 16, 36)); return offset + 52; diff --git a/src/org/infinity/resource/are/Container.java b/src/org/infinity/resource/are/Container.java index 9d71ba83f..94b367eea 100644 --- a/src/org/infinity/resource/are/Container.java +++ b/src/org/infinity/resource/are/Container.java @@ -11,7 +11,7 @@ import org.infinity.datatype.Bitmap; import org.infinity.datatype.DecNumber; import org.infinity.datatype.Flag; -import org.infinity.datatype.HexNumber; +import org.infinity.datatype.IsNumeric; import org.infinity.datatype.ResourceRef; import org.infinity.datatype.StringRef; import org.infinity.datatype.TextString; @@ -72,7 +72,6 @@ public Container(AbstractStruct superStruct, ByteBuffer buffer, int offset, int super(superStruct, ARE_CONTAINER + " " + nr, buffer, offset); } - // @Override public AddRemovable[] getPrototypes() throws Exception { @@ -84,17 +83,13 @@ public AddRemovable confirmAddEntry(AddRemovable entry) throws Exception { return entry; } - // - // @Override public boolean canRemove() { return true; } - // - // @Override public int getViewerTabCount() { @@ -118,14 +113,12 @@ public boolean viewerTabAddedBefore(int index) { return true; } - // - // @Override public void readVertices(ByteBuffer buffer, int offset) throws Exception { - int firstVertex = ((DecNumber)getAttribute(ARE_CONTAINER_FIRST_VERTEX_INDEX)).getValue(); - int numVertices = ((DecNumber)getAttribute(ARE_CONTAINER_NUM_VERTICES)).getValue(); + int firstVertex = ((IsNumeric)getAttribute(ARE_CONTAINER_FIRST_VERTEX_INDEX)).getValue(); + int numVertices = ((IsNumeric)getAttribute(ARE_CONTAINER_NUM_VERTICES)).getValue(); offset += firstVertex << 2; for (int i = 0; i < numVertices; i++) { addField(new Vertex(this, buffer, offset + 4 * i, i)); @@ -148,22 +141,21 @@ public int updateVertices(int offset, int number) ((DecNumber)getAttribute(ARE_CONTAINER_NUM_VERTICES)).setValue(count); return count; } - // @Override protected void setAddRemovableOffset(AddRemovable datatype) { if (datatype instanceof Vertex) { - int index = ((DecNumber)getAttribute(ARE_CONTAINER_FIRST_VERTEX_INDEX)).getValue(); - index += ((DecNumber)getAttribute(ARE_CONTAINER_NUM_VERTICES)).getValue(); - final int offset = ((HexNumber)getParent().getAttribute(AreResource.ARE_OFFSET_VERTICES)).getValue(); + int index = ((IsNumeric)getAttribute(ARE_CONTAINER_FIRST_VERTEX_INDEX)).getValue(); + index += ((IsNumeric)getAttribute(ARE_CONTAINER_NUM_VERTICES)).getValue(); + final int offset = ((IsNumeric)getParent().getAttribute(AreResource.ARE_OFFSET_VERTICES)).getValue(); datatype.setOffset(offset + 4 * index); ((AbstractStruct)datatype).realignStructOffsets(); } else if (datatype instanceof Item) { - int index = ((DecNumber)getAttribute(ARE_CONTAINER_FIRST_ITEM_INDEX)).getValue(); - index += ((DecNumber)getAttribute(ARE_CONTAINER_NUM_ITEMS)).getValue(); - final int offset = ((HexNumber)getParent().getAttribute(AreResource.ARE_OFFSET_ITEMS)).getValue(); + int index = ((IsNumeric)getAttribute(ARE_CONTAINER_FIRST_ITEM_INDEX)).getValue(); + index += ((IsNumeric)getAttribute(ARE_CONTAINER_NUM_ITEMS)).getValue(); + final int offset = ((IsNumeric)getParent().getAttribute(AreResource.ARE_OFFSET_ITEMS)).getValue(); datatype.setOffset(offset + 20 * index); ((AbstractStruct)datatype).realignStructOffsets(); } @@ -171,8 +163,8 @@ else if (datatype instanceof Item) { public void readItems(ByteBuffer buffer, int offset) throws Exception { - int firstIndex = ((DecNumber)getAttribute(ARE_CONTAINER_FIRST_ITEM_INDEX)).getValue(); - int numItems = ((DecNumber)getAttribute(ARE_CONTAINER_NUM_ITEMS)).getValue(); + int firstIndex = ((IsNumeric)getAttribute(ARE_CONTAINER_FIRST_ITEM_INDEX)).getValue(); + int numItems = ((IsNumeric)getAttribute(ARE_CONTAINER_NUM_ITEMS)).getValue(); offset += firstIndex * 20; for (int i = 0; i < numItems; i++) { addField(new Item(this, buffer, offset + 20 * i, i)); @@ -196,7 +188,6 @@ public int updateItems(int offset, int number) return count; } - // @Override public int read(ByteBuffer buffer, int offset) throws Exception { @@ -229,5 +220,4 @@ public int read(ByteBuffer buffer, int offset) throws Exception addField(new Unknown(buffer, offset + 136, 56)); return offset + 192; } - // } diff --git a/src/org/infinity/resource/are/Door.java b/src/org/infinity/resource/are/Door.java index b046f44f3..06570b455 100644 --- a/src/org/infinity/resource/are/Door.java +++ b/src/org/infinity/resource/are/Door.java @@ -9,7 +9,7 @@ import org.infinity.datatype.Bitmap; import org.infinity.datatype.DecNumber; import org.infinity.datatype.Flag; -import org.infinity.datatype.HexNumber; +import org.infinity.datatype.IsNumeric; import org.infinity.datatype.ResourceRef; import org.infinity.datatype.SectionCount; import org.infinity.datatype.StringRef; @@ -94,7 +94,6 @@ public Door(AbstractStruct superStruct, ByteBuffer buffer, int offset, int nr) t super(superStruct, ARE_DOOR + " " + nr, buffer, offset); } - // @Override public AddRemovable[] getPrototypes() throws Exception { @@ -107,40 +106,36 @@ public AddRemovable confirmAddEntry(AddRemovable entry) throws Exception { return entry; } - // - // @Override public boolean canRemove() { return true; } - // - // @Override public void readVertices(ByteBuffer buffer, int offset) throws Exception { - DecNumber firstVertex = (DecNumber)getAttribute(ARE_DOOR_FIRST_VERTEX_INDEX_OPEN); - DecNumber numVertices = (DecNumber)getAttribute(ARE_DOOR_NUM_VERTICES_OPEN); + IsNumeric firstVertex = (IsNumeric)getAttribute(ARE_DOOR_FIRST_VERTEX_INDEX_OPEN); + IsNumeric numVertices = (IsNumeric)getAttribute(ARE_DOOR_NUM_VERTICES_OPEN); for (int i = 0; i < numVertices.getValue(); i++) { addField(new OpenVertex(this, buffer, offset + 4 * (firstVertex.getValue() + i), i)); } - firstVertex = (DecNumber)getAttribute(ARE_DOOR_FIRST_VERTEX_INDEX_CLOSED); - numVertices = (DecNumber)getAttribute(ARE_DOOR_NUM_VERTICES_CLOSED); + firstVertex = (IsNumeric)getAttribute(ARE_DOOR_FIRST_VERTEX_INDEX_CLOSED); + numVertices = (IsNumeric)getAttribute(ARE_DOOR_NUM_VERTICES_CLOSED); for (int i = 0; i < numVertices.getValue(); i++) { addField(new ClosedVertex(this, buffer, offset + 4 * (firstVertex.getValue() + i), i)); } - firstVertex = (DecNumber)getAttribute(ARE_DOOR_FIRST_VERTEX_INDEX_IMPEDED_OPEN); - numVertices = (DecNumber)getAttribute(ARE_DOOR_NUM_VERTICES_IMPEDED_OPEN); + firstVertex = (IsNumeric)getAttribute(ARE_DOOR_FIRST_VERTEX_INDEX_IMPEDED_OPEN); + numVertices = (IsNumeric)getAttribute(ARE_DOOR_NUM_VERTICES_IMPEDED_OPEN); for (int i = 0; i < numVertices.getValue(); i++) { addField(new OpenVertexImpeded(this, buffer, offset + 4 * (firstVertex.getValue() + i), i)); } - firstVertex = (DecNumber)getAttribute(ARE_DOOR_FIRST_VERTEX_INDEX_IMPEDED_CLOSED); - numVertices = (DecNumber)getAttribute(ARE_DOOR_NUM_VERTICES_IMPEDED_CLOSED); + firstVertex = (IsNumeric)getAttribute(ARE_DOOR_FIRST_VERTEX_INDEX_IMPEDED_CLOSED); + numVertices = (IsNumeric)getAttribute(ARE_DOOR_NUM_VERTICES_IMPEDED_CLOSED); for (int i = 0; i < numVertices.getValue(); i++) { addField(new ClosedVertexImpeded(this, buffer, offset + 4 * (firstVertex.getValue() + i), i)); } @@ -151,13 +146,13 @@ public int updateVertices(int offset, int number) { // Must assume that the number is correct ((DecNumber)getAttribute(ARE_DOOR_FIRST_VERTEX_INDEX_OPEN)).setValue(number); - int count = ((DecNumber)getAttribute(ARE_DOOR_NUM_VERTICES_OPEN)).getValue(); + int count = ((IsNumeric)getAttribute(ARE_DOOR_NUM_VERTICES_OPEN)).getValue(); ((DecNumber)getAttribute(ARE_DOOR_FIRST_VERTEX_INDEX_CLOSED)).setValue(number + count); - count += ((DecNumber)getAttribute(ARE_DOOR_NUM_VERTICES_CLOSED)).getValue(); + count += ((IsNumeric)getAttribute(ARE_DOOR_NUM_VERTICES_CLOSED)).getValue(); ((DecNumber)getAttribute(ARE_DOOR_FIRST_VERTEX_INDEX_IMPEDED_OPEN)).setValue(number + count); - count += ((DecNumber)getAttribute(ARE_DOOR_NUM_VERTICES_IMPEDED_OPEN)).getValue(); + count += ((IsNumeric)getAttribute(ARE_DOOR_NUM_VERTICES_IMPEDED_OPEN)).getValue(); ((DecNumber)getAttribute(ARE_DOOR_FIRST_VERTEX_INDEX_IMPEDED_CLOSED)).setValue(number + count); - count += ((DecNumber)getAttribute(ARE_DOOR_NUM_VERTICES_IMPEDED_CLOSED)).getValue(); + count += ((IsNumeric)getAttribute(ARE_DOOR_NUM_VERTICES_IMPEDED_CLOSED)).getValue(); for (final StructEntry entry : getFields()) { if (entry instanceof Vertex) { @@ -168,37 +163,33 @@ public int updateVertices(int offset, int number) } return count; } - // - // @Override protected void setAddRemovableOffset(AddRemovable datatype) { - final int offset = ((HexNumber)getParent().getAttribute(AreResource.ARE_OFFSET_VERTICES)).getValue(); + final int offset = ((IsNumeric)getParent().getAttribute(AreResource.ARE_OFFSET_VERTICES)).getValue(); if (datatype instanceof OpenVertex) { - int index = ((DecNumber)getAttribute(ARE_DOOR_FIRST_VERTEX_INDEX_OPEN)).getValue(); - index += ((DecNumber)getAttribute(ARE_DOOR_NUM_VERTICES_OPEN)).getValue(); + int index = ((IsNumeric)getAttribute(ARE_DOOR_FIRST_VERTEX_INDEX_OPEN)).getValue(); + index += ((IsNumeric)getAttribute(ARE_DOOR_NUM_VERTICES_OPEN)).getValue(); datatype.setOffset(offset + 4 * (index - 1)); } else if (datatype instanceof ClosedVertex) { - int index = ((DecNumber)getAttribute(ARE_DOOR_FIRST_VERTEX_INDEX_CLOSED)).getValue(); - index += ((DecNumber)getAttribute(ARE_DOOR_NUM_VERTICES_CLOSED)).getValue(); + int index = ((IsNumeric)getAttribute(ARE_DOOR_FIRST_VERTEX_INDEX_CLOSED)).getValue(); + index += ((IsNumeric)getAttribute(ARE_DOOR_NUM_VERTICES_CLOSED)).getValue(); datatype.setOffset(offset + 4 * (index - 1)); } else if (datatype instanceof OpenVertexImpeded) { - int index = ((DecNumber)getAttribute(ARE_DOOR_FIRST_VERTEX_INDEX_IMPEDED_OPEN)).getValue(); - index += ((DecNumber)getAttribute(ARE_DOOR_NUM_VERTICES_IMPEDED_OPEN)).getValue(); + int index = ((IsNumeric)getAttribute(ARE_DOOR_FIRST_VERTEX_INDEX_IMPEDED_OPEN)).getValue(); + index += ((IsNumeric)getAttribute(ARE_DOOR_NUM_VERTICES_IMPEDED_OPEN)).getValue(); datatype.setOffset(offset + 4 * (index - 1)); } else if (datatype instanceof ClosedVertexImpeded) { - int index = ((DecNumber)getAttribute(ARE_DOOR_FIRST_VERTEX_INDEX_IMPEDED_CLOSED)).getValue(); - index += ((DecNumber)getAttribute(ARE_DOOR_NUM_VERTICES_IMPEDED_CLOSED)).getValue(); + int index = ((IsNumeric)getAttribute(ARE_DOOR_FIRST_VERTEX_INDEX_IMPEDED_CLOSED)).getValue(); + index += ((IsNumeric)getAttribute(ARE_DOOR_NUM_VERTICES_IMPEDED_CLOSED)).getValue(); datatype.setOffset(offset + 4 * (index - 1)); } } - // - // @Override public int read(ByteBuffer buffer, int offset) throws Exception { @@ -256,5 +247,4 @@ public int read(ByteBuffer buffer, int offset) throws Exception addField(new Unknown(buffer, offset + 192, 8)); return offset + 200; } - // } diff --git a/src/org/infinity/resource/are/ITEPoint.java b/src/org/infinity/resource/are/ITEPoint.java index 7034f8c23..b3407c136 100644 --- a/src/org/infinity/resource/are/ITEPoint.java +++ b/src/org/infinity/resource/are/ITEPoint.java @@ -9,7 +9,7 @@ import org.infinity.datatype.Bitmap; import org.infinity.datatype.DecNumber; import org.infinity.datatype.Flag; -import org.infinity.datatype.HexNumber; +import org.infinity.datatype.IsNumeric; import org.infinity.datatype.ResourceRef; import org.infinity.datatype.StringRef; import org.infinity.datatype.TextString; @@ -75,9 +75,7 @@ public ITEPoint(AbstractStruct superStruct, ByteBuffer buffer, int offset, int n { super(superStruct, ARE_TRIGGER + " " + number, buffer, offset); } - // - // @Override public AddRemovable[] getPrototypes() throws Exception { @@ -89,22 +87,18 @@ public AddRemovable confirmAddEntry(AddRemovable entry) throws Exception { return entry; } - // - // @Override public boolean canRemove() { return true; } - // - // @Override public void readVertices(ByteBuffer buffer, int offset) throws Exception { - int firstVertex = ((DecNumber)getAttribute(ARE_TRIGGER_FIRST_VERTEX_INDEX)).getValue(); - int numVertices = ((DecNumber)getAttribute(ARE_TRIGGER_NUM_VERTICES)).getValue(); + int firstVertex = ((IsNumeric)getAttribute(ARE_TRIGGER_FIRST_VERTEX_INDEX)).getValue(); + int numVertices = ((IsNumeric)getAttribute(ARE_TRIGGER_NUM_VERTICES)).getValue(); offset += firstVertex << 2; for (int i = 0; i < numVertices; i++) { addField(new Vertex(this, buffer, offset + 4 * i, i)); @@ -127,23 +121,19 @@ public int updateVertices(int offset, int number) ((DecNumber)getAttribute(ARE_TRIGGER_NUM_VERTICES)).setValue(count); return count; } - // - // @Override protected void setAddRemovableOffset(AddRemovable datatype) { if (datatype instanceof Vertex) { - int index = ((DecNumber)getAttribute(ARE_TRIGGER_FIRST_VERTEX_INDEX)).getValue(); - index += ((DecNumber)getAttribute(ARE_TRIGGER_NUM_VERTICES)).getValue(); - final int offset = ((HexNumber)getParent().getAttribute(AreResource.ARE_OFFSET_VERTICES)).getValue(); + int index = ((IsNumeric)getAttribute(ARE_TRIGGER_FIRST_VERTEX_INDEX)).getValue(); + index += ((IsNumeric)getAttribute(ARE_TRIGGER_NUM_VERTICES)).getValue(); + final int offset = ((IsNumeric)getParent().getAttribute(AreResource.ARE_OFFSET_VERTICES)).getValue(); datatype.setOffset(offset + 4 * index); ((AbstractStruct)datatype).realignStructOffsets(); } } - // - // @Override public int read(ByteBuffer buffer, int offset) throws Exception { @@ -200,5 +190,4 @@ else if (Profile.getEngine() == Profile.Engine.IWD || Profile.getEngine() == Pro } return offset + 196; } - // } diff --git a/src/org/infinity/resource/are/Song.java b/src/org/infinity/resource/are/Song.java index c1d69bb34..c01d3e892 100644 --- a/src/org/infinity/resource/are/Song.java +++ b/src/org/infinity/resource/are/Song.java @@ -75,7 +75,7 @@ public int read(ByteBuffer buffer, int offset) throws Exception addField(new DecNumber(buffer, offset + 76, 4, ARE_SONGS_AMBIENT_VOLUME_NIGHT)); } if (ResourceFactory.resourceExists("REVERB.IDS")) { - addField(new IdsBitmap(buffer, offset + 80, 4, ARE_SONGS_REVERB, "REVERB.IDS")); + addField(new IdsBitmap(buffer, offset + 80, 4, ARE_SONGS_REVERB, "REVERB.IDS", true, false, true)); addField(new Unknown(buffer, offset + 84, 60)); } else if (ResourceFactory.resourceExists("REVERB.2DA")) { addField(new TableBitmap(buffer, offset + 80, 4, ARE_SONGS_REVERB, "REVERB.2DA")); diff --git a/src/org/infinity/resource/are/viewer/AbstractAnimationProvider.java b/src/org/infinity/resource/are/viewer/AbstractAnimationProvider.java new file mode 100644 index 000000000..dd34b0ba8 --- /dev/null +++ b/src/org/infinity/resource/are/viewer/AbstractAnimationProvider.java @@ -0,0 +1,172 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.are.viewer; + +import java.awt.Image; +import java.awt.Point; +import java.awt.image.BufferedImage; + +import org.infinity.gui.layeritem.BasicAnimationProvider; +import org.infinity.resource.graphics.ColorConvert; + +/** + * Provides base functionality for rendering animations. + */ +public abstract class AbstractAnimationProvider implements BasicAnimationProvider +{ + /** A default animation provider that can be used as placeholder. */ + public static final DefaultAnimationProvider DEFAULT_ANIMATION_PROVIDER = new DefaultAnimationProvider(); + + private BufferedImage image, working; + private boolean isActive, isActiveIgnored; + + protected AbstractAnimationProvider() + { + this.image = null; + this.working = null; + this.isActive = false; + } + + /** Returns the active/visibility state of the animation. */ + public boolean isActive() + { + return isActive; + } + + /** Specify whether to actually display the animation on screen. */ + public void setActive(boolean set) + { + if (set != isActive) { + isActive = set; + updateGraphics(); + } + } + + /** + * Returns whether to ignore the activation state of the animation. + */ + public boolean isActiveIgnored() + { + return isActiveIgnored; + } + + /** + * Specify whether to ignore the activation state of the animation and display it regardless. + */ + public void setActiveIgnored(boolean set) + { + if (set != isActiveIgnored) { + isActiveIgnored = set; + updateGraphics(); + } + } + + @Override + public Image getImage() + { + return image; + } + + protected void setImage(Image img) + { + if (img instanceof BufferedImage) { + this.image = (BufferedImage)img; + } + } + + @Override + public boolean isLooping() + { + return true; + } + + protected BufferedImage getWorkingImage() + { + return working; + } + + protected void setWorkingImage(BufferedImage img) + { + this.working = img; + } + + protected abstract void updateGraphics(); + + + // Applies lightning conditions to all pixels + protected void applyLighting(int[] buffer, int cw, int ch, int fw, int fh, int lighting) + { + if (buffer != null && cw > 0 && ch > 0 && fw > 0 && fh > 0) { + int maxOfs = fh*cw; + if (buffer.length >= maxOfs) { + if (lighting < ViewerConstants.LIGHTING_DAY) lighting = ViewerConstants.LIGHTING_DAY; + else if (lighting > ViewerConstants.LIGHTING_NIGHT) lighting = ViewerConstants.LIGHTING_NIGHT; + int ofs = 0; + while (ofs < maxOfs) { + for (int x = 0; x < fw; x++) { + int pixel = buffer[ofs+x]; + if ((pixel & 0xff000000) != 0) { + int r = (pixel >>> 16) & 0xff; + int g = (pixel >>> 8) & 0xff; + int b = pixel & 0xff; + r = (r*TilesetRenderer.LightingAdjustment[lighting][0]) >>> TilesetRenderer.LightingAdjustmentShift; + g = (g*TilesetRenderer.LightingAdjustment[lighting][1]) >>> TilesetRenderer.LightingAdjustmentShift; + b = (b*TilesetRenderer.LightingAdjustment[lighting][2]) >>> TilesetRenderer.LightingAdjustmentShift; + if (r > 255) r = 255; + if (g > 255) g = 255; + if (b > 255) b = 255; + buffer[ofs+x] = (pixel & 0xff000000) | (r << 16) | (g << 8) | b; + } + } + ofs += cw; + } + } + } + } + + +//-------------------------- INNER CLASSES -------------------------- + + /** A pseudo animation provider that always returns a transparent image of 16x16 size. */ + public static final class DefaultAnimationProvider implements BasicAnimationProvider + { + private final BufferedImage image; + + public DefaultAnimationProvider() + { + image = ColorConvert.createCompatibleImage(16, 16, true); + } + + @Override + public Image getImage() + { + return image; + } + + @Override + public boolean advanceFrame() + { + return false; + } + + @Override + public void resetFrame() + { + } + + @Override + public boolean isLooping() + { + return false; + } + + @Override + public Point getLocationOffset() + { + return new Point(); + } + } + +} diff --git a/src/org/infinity/resource/are/viewer/ActorAnimationProvider.java b/src/org/infinity/resource/are/viewer/ActorAnimationProvider.java new file mode 100644 index 000000000..07086872b --- /dev/null +++ b/src/org/infinity/resource/are/viewer/ActorAnimationProvider.java @@ -0,0 +1,348 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.are.viewer; + +import java.awt.AlphaComposite; +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.image.BufferedImage; +import java.awt.image.DataBufferInt; +import java.util.Arrays; +import java.util.Objects; + +import org.infinity.resource.cre.decoder.SpriteDecoder; +import org.infinity.resource.cre.decoder.SpriteDecoder.SpriteBamControl; +import org.infinity.resource.cre.decoder.util.Direction; +import org.infinity.resource.graphics.ColorConvert; + +/** + * Implements functionality for properly displaying actor sprites. + */ +public class ActorAnimationProvider extends AbstractAnimationProvider +{ + private static final Color TransparentColor = new Color(0, true); + + private SpriteDecoder decoder; + private SpriteBamControl control; + private boolean isLooping, isSelectionCircleEnabled, isPersonalSpaceEnabled; + private int lighting, orientation, cycle, startFrame, endFrame; + private Rectangle imageRect; + + public ActorAnimationProvider(SpriteDecoder decoder) + { + setDefaults(); + setDecoder(decoder); + } + + /** Returns the BAM sprite decoder instance. */ + public SpriteDecoder getDecoder() + { + return decoder; + } + + public void setDecoder(SpriteDecoder decoder) + { + this.decoder = Objects.requireNonNull(decoder, "Sprite decoder cannot be null"); + control = this.decoder.createControl(); + control.setMode(SpriteBamControl.Mode.INDIVIDUAL); + control.setSharedPerCycle(false); + control.cycleSet(getCycle()); + resetFrame(); + + updateCanvas(); + updateGraphics(); + } + + /** Returns whether the selection circle underneath actor sprites is drawn. */ + public boolean isSelectionCircleEnabled() + { + return isSelectionCircleEnabled; + } + + /** Specify whether the selection circle underneath actor sprites should be drawn. */ + public void setSelectionCircleEnabled(boolean enable) + { + if (enable != isSelectionCircleEnabled) { + isSelectionCircleEnabled = enable; + decoder.setSelectionCircleEnabled(isSelectionCircleEnabled); + updateCanvas(); + updateGraphics(); + } + } + + /** Returns whether the personal space indicator underneath actor sprites is drawn. */ + public boolean isPersonalSpaceEnabled() + { + return isPersonalSpaceEnabled; + } + + /** Specify whether the selection circle underneath actor sprites should be drawn. */ + public void setPersonalSpaceEnabled(boolean enable) + { + if (enable != isPersonalSpaceEnabled) { + isPersonalSpaceEnabled = enable; + decoder.setPersonalSpaceVisible(isPersonalSpaceEnabled); + updateCanvas(); + updateGraphics(); + } + } + + /** Returns the lighting condition of the animation. */ + public int getLighting() + { + return lighting; + } + + /** Defines a new lighting condition for the animation. No change if the animation is self-illuminated. */ + public void setLighting(int state) + { + switch (state) { + case ViewerConstants.LIGHTING_DAY: + case ViewerConstants.LIGHTING_TWILIGHT: + case ViewerConstants.LIGHTING_NIGHT: + if (state != lighting) { + lighting = state; + if (!decoder.isLightSource()) { + updateGraphics(); + } + } + break; + } + } + + /** Returns the numeric orientation value of the actor sprite. */ + public int getOrientation() + { + return orientation; + } + + /** + * Sets the specified orientation value and updates the sprite cycle accordingly. + * Should be called after a sprite sequence has been loaded. + */ + public void setOrientation(int dir) + { + dir = Math.abs(dir) % Direction.values().length; + if (dir != orientation) { + orientation = dir; + Direction direction = getDecoder().getExistingDirection(Direction.from(orientation)); + int idx = getDecoder().getDirectionMap().getOrDefault(direction, 0); + setCycle(idx); + } + } + + /** + * Returns the currently selected animation cycle. + */ + public int getCycle() + { + int idx = cycle < 0 ? (control.cycleCount() + cycle) : cycle; + return Math.max(0, Math.min(control.cycleCount() - 1, idx)); + } + + /** + * Sets the current BAM cycle. + * Specify positive values to set an absolute cycle index. + * Specify negative values to set the cycle relative to the cycle length + * where -1 indicates the last BAM cycle. + */ + public void setCycle(int cycleIdx) + { + cycleIdx = Math.min(control.cycleCount() - 1, cycleIdx); + if (cycleIdx != cycle) { + cycle = cycleIdx; + control.cycleSet(getCycle()); + updateCanvas(); + resetFrame(); + } + } + + /** Returns the first frame of the current BAM cycle to be displayed. */ + public int getStartFrame() + { + int idx = startFrame < 0 ? (control.cycleFrameCount() + startFrame) : startFrame; + return Math.max(0, Math.min(control.cycleFrameCount() - 1, idx)); + } + + /** + * Sets the first frame of the current BAM cycle to be displayed. + * Specify positive values to set an absolute frame index. + * Specify negative values to set the start frame relative to the number of frames in the cycle + * where -1 indicates the last frame of the cycle. + */ + public void setStartFrame(int frameIdx) + { + frameIdx = Math.min(control.cycleFrameCount() - 1, frameIdx); + if (frameIdx != startFrame) { + startFrame = frameIdx; + resetFrame(); + } + } + + /** Returns the frame index after which the animation sequence ends. */ + public int getFrameCap() + { + int idx = endFrame < 0 ? (control.cycleFrameCount() + endFrame) : endFrame; + return Math.max(0, Math.min(control.cycleFrameCount() - 1, idx)); + } + + /** + * Sets the frame index after which the animation sequence ends. + * Specify positive values to set an absolute frame index. + * Specify negative values to set the end frame relative to the number of frames in the cycle + * where -1 indicates the last frame of the cycle. + */ + public void setFrameCap(int frameIdx) + { + frameIdx = Math.min(control.cycleFrameCount() -1, frameIdx); + if (frameIdx != endFrame) { + endFrame = frameIdx; + resetFrame(); + } + } + + /** Sets a new looping state. */ + public void setLooping(boolean set) + { + if (set != isLooping) { + isLooping = set; + } + } + +//--------------------- Begin Interface BasicAnimationProvider --------------------- + + @Override + public boolean advanceFrame() + { + boolean retVal = control.cycleGetFrameIndex() < getFrameCap() - 1; + if (retVal) { + control.cycleNextFrame(); + } +// retVal = control.cycleNextFrame(); + updateGraphics(); + return retVal; + } + + @Override + public void resetFrame() + { + control.cycleSetFrameIndex(getStartFrame()); + updateGraphics(); + } + + @Override + public boolean isLooping() + { + return isLooping; + } + + @Override + public Point getLocationOffset() + { + return imageRect.getLocation(); + } + +//--------------------- End Interface BasicAnimationProvider --------------------- + + // Sets sane default values for all properties + private void setDefaults() + { + setImage(null); + setWorkingImage(null); + setActive(true); + isLooping = true; + isSelectionCircleEnabled = true; + isPersonalSpaceEnabled = false; + orientation = 0; // south + cycle = 0; + startFrame = 0; + endFrame = -1; + lighting = ViewerConstants.LIGHTING_TWILIGHT; + imageRect = null; + } + + // Updates the global image object to match the shared size of the current BAM (cycle). + private void updateCanvas() + { + imageRect = control.calculateSharedCanvas(false); + BufferedImage image = (BufferedImage)getImage(); + if (getWorkingImage() == null || image == null || + image.getWidth() != imageRect.width || image.getHeight() != imageRect.height) { + setImage(ColorConvert.createCompatibleImage(imageRect.width, imageRect.height, true)); + setWorkingImage(new BufferedImage(imageRect.width, imageRect.height, BufferedImage.TYPE_INT_ARGB)); + } + } + + // Renders the current frame + @Override + protected synchronized void updateGraphics() + { + BufferedImage image = (BufferedImage)getImage(); + if (image != null) { + Graphics2D g; + if (isActive()) { + g = image.createGraphics(); + try { + // clearing old content + g.setComposite(AlphaComposite.Src); + g.setColor(TransparentColor); + g.fillRect(0, 0, image.getWidth(), image.getHeight()); + } finally { + g.dispose(); + g = null; + } + + g = image.createGraphics(); + try { + int frameIndex = control.cycleGetFrameIndexAbsolute(); + int left = -imageRect.x - decoder.getFrameInfo(frameIndex).getCenterX(); + int top = -imageRect.y - decoder.getFrameInfo(frameIndex).getCenterY(); + Point pos = new Point(left, top); + + // rendering visual markers + control.getVisualMarkers(g, pos); + + // rendering frame + // fetching frame data + BufferedImage working = getWorkingImage(); + int[] buffer = ((DataBufferInt)working.getRaster().getDataBuffer()).getData(); + Arrays.fill(buffer, 0); + decoder.frameGet(control, frameIndex, working); + + // post-processing frame + buffer = ((DataBufferInt)working.getRaster().getDataBuffer()).getData(); + int canvasWidth = working.getWidth(); + int canvasHeight = working.getHeight(); + int frameWidth = decoder.getFrameInfo(frameIndex).getWidth(); + int frameHeight = decoder.getFrameInfo(frameIndex).getHeight(); + + if (!decoder.isLightSource()) { + applyLighting(buffer, canvasWidth, canvasHeight, frameWidth, frameHeight, getLighting()); + } + buffer = null; + + // rendering frame + g.drawImage(working, left, top, left+frameWidth, top+frameHeight, 0, 0, frameWidth, frameHeight, null); + } finally { + g.dispose(); + g = null; + } + } else { + // draw placeholder instead + g = image.createGraphics(); + try { + g.setComposite(AlphaComposite.Src); + g.setColor(TransparentColor); + g.fillRect(0, 0, image.getWidth(), image.getHeight()); + } finally { + g.dispose(); + g = null; + } + } + } + } +} diff --git a/src/org/infinity/resource/are/viewer/AreaViewer.java b/src/org/infinity/resource/are/viewer/AreaViewer.java index c0a760f89..eb505a139 100644 --- a/src/org/infinity/resource/are/viewer/AreaViewer.java +++ b/src/org/infinity/resource/are/viewer/AreaViewer.java @@ -36,6 +36,7 @@ import java.util.ArrayList; import java.util.EventObject; import java.util.Hashtable; +import java.util.Iterator; import java.util.List; import java.util.Locale; @@ -135,6 +136,7 @@ public class AreaViewer extends ChildFrame private final Point mapCoordinates = new Point(); private final String windowTitle; private final JCheckBox[] cbLayers = new JCheckBox[LayerManager.getLayerTypeCount()]; + private final JCheckBox[] cbLayerRealActor = new JCheckBox[2]; private final JCheckBox[] cbLayerRealAnimation = new JCheckBox[2]; private final JCheckBox[] cbMiniMaps = new JCheckBox[3]; // private final JToggleButton[] tbAddLayerItem = new JToggleButton[LayerManager.getLayerTypeCount()]; @@ -228,7 +230,6 @@ protected Void doInBackground() throws Exception { workerInitGui.execute(); } - // @Override public void close() { @@ -243,18 +244,17 @@ public void close() map.clear(); if (rcCanvas != null) { removeLayerItems(); - rcCanvas.clear(); - rcCanvas.setImage(null); + rcCanvas.dispose(); } if (layerManager != null) { layerManager.close(); layerManager = null; } + SharedResourceCache.clearCache(); dispose(); System.gc(); super.close(); } - // /** * Returns the tileset renderer for this viewer instance. @@ -384,7 +384,17 @@ private void init() cbLayers[i].addActionListener(getListeners()); t2 = new DefaultMutableTreeNode(cbLayers[i]); t.add(t2); - if (i == LayerManager.getLayerTypeIndex(LayerType.AMBIENT)) { + if (i == LayerManager.getLayerTypeIndex(LayerType.ACTOR)) { + // Initializing real creature animation checkboxes + cbLayerRealActor[0] = new JCheckBox("Show actor animation"); + cbLayerRealActor[0].addActionListener(getListeners()); + t3 = new DefaultMutableTreeNode(cbLayerRealActor[0]); + t2.add(t3); + cbLayerRealActor[1] = new JCheckBox("Animate actor animation"); + cbLayerRealActor[1].addActionListener(getListeners()); + t3 = new DefaultMutableTreeNode(cbLayerRealActor[1]); + t2.add(t3); + } else if (i == LayerManager.getLayerTypeIndex(LayerType.AMBIENT)) { // Initializing ambient sound range checkbox cbLayerAmbientRange = new JCheckBox("Show local sound ranges"); cbLayerAmbientRange.addActionListener(getListeners()); @@ -755,6 +765,17 @@ private void initGuiSettings() showLayer(LayerManager.getLayerType(i), cbLayers[i].isSelected()); } + // initializing actor sprites display + // Disabling animated frames for performance and safety reasons + if (Settings.ShowActorSprites == ViewerConstants.ANIM_SHOW_ANIMATED) { + Settings.ShowActorSprites = ViewerConstants.ANIM_SHOW_STILL; + } + ((LayerActor)layerManager.getLayer(LayerType.ACTOR)).setRealActorFrameState(Settings.ShowActorFrame); + cbLayerRealActor[0].setSelected(Settings.ShowActorSprites == ViewerConstants.ANIM_SHOW_STILL); + cbLayerRealActor[1].setSelected(false); + updateRealActors(); + updateRealActorsLighting(getVisualState()); + // Setting up ambient sound ranges LayerAmbient layerAmbient = (LayerAmbient)layerManager.getLayer(ViewerConstants.LayerType.AMBIENT); if (layerAmbient.getLayerObjectCount(ViewerConstants.AMBIENT_TYPE_LOCAL) > 0) { @@ -768,11 +789,11 @@ private void initGuiSettings() if (Settings.ShowRealAnimations == ViewerConstants.ANIM_SHOW_ANIMATED) { Settings.ShowRealAnimations = ViewerConstants.ANIM_SHOW_STILL; } - ((LayerAnimation)layerManager.getLayer(LayerType.ANIMATION)).setRealAnimationFrameState(Settings.ShowFrame); + ((LayerAnimation)layerManager.getLayer(LayerType.ANIMATION)).setRealAnimationFrameState(Settings.ShowAnimationFrame); cbLayerRealAnimation[0].setSelected(Settings.ShowRealAnimations == ViewerConstants.ANIM_SHOW_STILL); cbLayerRealAnimation[1].setSelected(false); updateRealAnimation(); - updateRealAnimationsLighting(getDayTime()); + updateRealAnimationsLighting(getVisualState()); updateWindowTitle(); applySettings(); @@ -845,17 +866,21 @@ private int getCurrentWedIndex() } - /** Returns the currently selected visual state (day/twilight/night). */ - private int getVisualState() + /** Returns the currently selected visual state (day/twilight/night) depending on whether the map supports day/night cycles. */ + public int getVisualState() { - return getDayTime(); + if (map.hasDayNight()) { + return getDayTime(); + } else { + return ViewerConstants.LIGHTING_DAY; + } } /** Set the lighting condition of the current map (day/twilight/night) and real background animations. */ private synchronized void setVisualState(int hour) { - while (hour < 0) { hour += 24; } hour %= 24; + while (hour < 0) { hour += 24; } int index = ViewerConstants.getDayTime(hour); if (!map.hasDayNight()) { index = ViewerConstants.LIGHTING_DAY; @@ -903,7 +928,8 @@ private synchronized void setVisualState(int hour) updateMiniMap(); updateToolBarButtons(); - updateRealAnimationsLighting(getDayTime()); + updateRealActorsLighting(getVisualState()); + updateRealAnimationsLighting(getVisualState()); updateScheduledItems(); updateWindowTitle(); } @@ -1367,6 +1393,7 @@ private void showItemPopup(MouseEvent event) /** Updates all available layer items. */ private void reloadLayers() { + SharedResourceCache.clearCache(); rcCanvas.reload(true); reloadAreLayers(false); reloadWedLayers(false); @@ -1392,6 +1419,8 @@ private void reloadAreLayers(boolean order) } } } + updateRealActors(); + updateRealActorsLighting(getVisualState()); updateAmbientRange(); updateRealAnimation(); updateRealAnimationsLighting(getVisualState()); @@ -1489,7 +1518,41 @@ private Window getViewerWindow(AbstractStruct as) } } // Window not found, creating and returning a new one - return new ViewFrame(NearInfinity.getInstance(), as); + List structChain = new ArrayList<>(); + structChain.add(as); + AbstractStruct as2 = as; + while (as2.getParent() != null) { + structChain.add(0, as2.getParent()); + as2 = as2.getParent(); + } + + if (map.getAre() == structChain.get(0)) { + // no need to create the whole Viewable chain + return new ViewFrame(NearInfinity.getInstance(), as); + } + + // recycling existing Viewables if possible + Window wnd = NearInfinity.getInstance(); + for (final Iterator iter = ChildFrame.getFrameIterator(cf -> cf instanceof ViewFrame && + ((ViewFrame)cf).getViewable() instanceof AbstractStruct); + iter.hasNext(); ) { + ViewFrame vf = (ViewFrame)iter.next(); + AbstractStruct struct = (AbstractStruct)vf.getViewable(); + if (struct.getResourceEntry() != null && + struct.getResourceEntry().equals(structChain.get(0).getResourceEntry())) { + structChain.remove(0); + wnd = vf; + break; + } + } + + // creating Viewable chain + for (int i = 0; i < structChain.size(); i++) { + wnd = new ViewFrame(wnd, structChain.get(i)); + } + if (wnd != null && wnd != NearInfinity.getInstance()) { + return wnd; + } } // Last resort: returning NearInfinity instance return NearInfinity.getInstance(); @@ -1553,6 +1616,19 @@ private void updateAmbientRange() } } + /** Applies the specified lighting condition to real actor items. */ + private void updateRealActorsLighting(int visualState) + { + if (layerManager != null) { + List list = layerManager.getLayerObjects(LayerType.ACTOR); + if (list != null) { + for (final LayerObject obj : list) { + ((LayerObjectActor)obj).setLighting(visualState); + } + } + } + } + /** Applies the specified lighting condition to real animation items. */ private void updateRealAnimationsLighting(int visualState) { @@ -1566,6 +1642,46 @@ private void updateRealAnimationsLighting(int visualState) } } + /** Updates the state of real actor checkboxes and their associated functionality. */ + private void updateRealActors() + { + if (layerManager != null) { + LayerActor layer = (LayerActor)layerManager.getLayer(LayerType.ACTOR); + if (layer != null) { + JCheckBox cb = cbLayers[LayerManager.getLayerTypeIndex(LayerType.ACTOR)]; + boolean enabled = cb.isEnabled() && cb.isSelected(); + cbLayerRealActor[0].setEnabled(enabled); + cbLayerRealActor[1].setEnabled(enabled); + boolean animEnabled = false; + boolean animPlaying = false; + if (enabled) { + if (cbLayerRealActor[0].isSelected()) { + animEnabled = true; + } else if (cbLayerRealActor[1].isSelected()) { + animEnabled = true; + animPlaying = true; + } + } + layer.setRealActorEnabled(animEnabled); + layer.setRealActorPlaying(animPlaying); + } else { + cbLayerRealActor[0].setEnabled(false); + cbLayerRealActor[1].setEnabled(false); + } + updateTreeNode(cbLayerRealActor[0]); + updateTreeNode(cbLayerRealActor[1]); + + // Storing settings + if (!cbLayerRealActor[0].isSelected() && !cbLayerRealActor[1].isSelected()) { + Settings.ShowActorSprites = ViewerConstants.ANIM_SHOW_NONE; + } else if (cbLayerRealActor[0].isSelected() && !cbLayerRealActor[1].isSelected()) { + Settings.ShowActorSprites = ViewerConstants.ANIM_SHOW_STILL; + } else if (!cbLayerRealActor[0].isSelected() && cbLayerRealActor[1].isSelected()) { + Settings.ShowActorSprites = ViewerConstants.ANIM_SHOW_ANIMATED; + } + } + } + /** Updates the state of real animation checkboxes and their associated functionality. */ private void updateRealAnimation() { @@ -1896,8 +2012,14 @@ private void applySettings() } if (layerManager != null) { + // applying actor frame settings + ((LayerActor)layerManager.getLayer(LayerType.ACTOR)).setRealActorFrameState(Settings.ShowActorFrame); + // applying actor selection circle visibility + ((LayerActor)layerManager.getLayer(LayerType.ACTOR)).setRealActorSelectionCircleEnabled(Settings.ShowActorSelectionCircle); + // applying actor personal space visibility + ((LayerActor)layerManager.getLayer(LayerType.ACTOR)).setRealActorPersonalSpaceEnabled(Settings.ShowActorPersonalSpace); // applying animation frame settings - ((LayerAnimation)layerManager.getLayer(LayerType.ANIMATION)).setRealAnimationFrameState(Settings.ShowFrame); + ((LayerAnimation)layerManager.getLayer(LayerType.ANIMATION)).setRealAnimationFrameState(Settings.ShowAnimationFrame); // applying animation active override settings ((LayerAnimation)layerManager.getLayer(LayerType.ANIMATION)).setRealAnimationActiveIgnored(Settings.OverrideAnimVisibility); // applying interpolation settings to animations @@ -1919,7 +2041,7 @@ private void applySettings() if (interval != timerOverlays.getDelay()) { timerOverlays.setDelay(interval); } - // applying frame rate to background animations + // applying frame rate to actor sprites and background animations layerManager.setRealAnimationFrameRate(Settings.FrameRateAnimations); } } @@ -2002,7 +2124,10 @@ public void actionPerformed(ActionEvent event) LayerType layer = getLayerType(cb); if (layer != null) { showLayer(layer, cb.isSelected()); - if (layer == LayerType.AMBIENT) { + if (layer == LayerType.ACTOR) { + // Taking care of real animation display + updateRealActors(); + } else if (layer == LayerType.AMBIENT) { // Taking care of local ambient ranges updateAmbientRange(); } else if (layer == LayerType.ANIMATION) { @@ -2010,6 +2135,16 @@ public void actionPerformed(ActionEvent event) updateRealAnimation(); } updateScheduledItems(); + } else if (cb == cbLayerRealActor[0]) { + if (cbLayerRealActor[0].isSelected()) { + cbLayerRealActor[1].setSelected(false); + } + updateRealActors(); + } else if (cb == cbLayerRealActor[1]) { + if (cbLayerRealActor[1].isSelected()) { + cbLayerRealActor[0].setSelected(false); + } + updateRealActors(); } else if (cb == cbLayerAmbientRange) { updateAmbientRange(); } else if (cb == cbLayerRealAnimation[0]) { @@ -2870,7 +3005,6 @@ public ChangeListener[] getChangeListeners() return listeners.toArray(new ChangeListener[listeners.size()]); } - // @Override public void actionPerformed(ActionEvent event) { @@ -2884,9 +3018,7 @@ public void actionPerformed(ActionEvent event) } } } - // - // @Override public void stateChanged(ChangeEvent event) { @@ -2901,7 +3033,6 @@ public void stateChanged(ChangeEvent event) } } } - // /** Fires a stateChanged event for all registered listeners. */ private void fireStateChanged() diff --git a/src/org/infinity/resource/are/viewer/BackgroundAnimationProvider.java b/src/org/infinity/resource/are/viewer/BackgroundAnimationProvider.java index baf4b5dc2..95c150148 100644 --- a/src/org/infinity/resource/are/viewer/BackgroundAnimationProvider.java +++ b/src/org/infinity/resource/are/viewer/BackgroundAnimationProvider.java @@ -7,14 +7,12 @@ import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Graphics2D; -import java.awt.Image; import java.awt.Point; import java.awt.Rectangle; import java.awt.image.BufferedImage; import java.awt.image.DataBufferInt; import java.util.Arrays; -import org.infinity.gui.layeritem.BasicAnimationProvider; import org.infinity.resource.graphics.BamDecoder; import org.infinity.resource.graphics.BamV1Decoder; import org.infinity.resource.graphics.ColorConvert; @@ -22,7 +20,7 @@ /** * Implements functionality for properly displaying background animations. */ -public class BackgroundAnimationProvider implements BasicAnimationProvider +public class BackgroundAnimationProvider extends AbstractAnimationProvider { private static final Color TransparentColor = new Color(0, true); @@ -39,13 +37,12 @@ public class BackgroundAnimationProvider implements BasicAnimationProvider private BamDecoder bam; private BamDecoder.BamControl control; - private boolean isActive, isActiveIgnored, isBlended, isMirrored, isLooping, isSelfIlluminated, + private boolean isBlended, isMirrored, isLooping, isSelfIlluminated, isMultiPart, isPaletteEnabled; private int lighting, baseAlpha; private int firstFrame, lastFrame; private int[] palette; // external palette private Rectangle imageRect; - private BufferedImage image, working; public BackgroundAnimationProvider() { @@ -116,40 +113,6 @@ public void setPaletteEnabled(boolean set) } } - /** Returns the active/visibility state of the animation. */ - public boolean isActive() - { - return isActive; - } - - /** Specify whether to actually display the animation on screen. */ - public void setActive(boolean set) - { - if (set != isActive) { - isActive = set; - updateGraphics(); - } - } - - /** - * Returns whether to ignore the activation state of the animation. - */ - public boolean isActiveIgnored() - { - return isActiveIgnored; - } - - /** - * Specify whether to ignore the activation state of the animation and display it regardless. - */ - public void setActiveIgnored(boolean set) - { - if (set != isActiveIgnored) { - isActiveIgnored = set; - updateGraphics(); - } - } - /** Returns whether the current frame of all available cycles are displayed simultanuously. */ public boolean isMultiPart() { @@ -340,12 +303,6 @@ public void setFrameCap(int frameIdx) //--------------------- Begin Interface BasicAnimationProvider --------------------- - @Override - public Image getImage() - { - return image; - } - @Override public boolean advanceFrame() { @@ -364,7 +321,7 @@ public boolean advanceFrame() @Override public void resetFrame() { - control.cycleSetFrameIndex(firstFrame); + control.cycleSetFrameIndex(getStartFrame()); updateGraphics(); } @@ -385,8 +342,11 @@ public Point getLocationOffset() // Sets sane default values for all properties private void setDefaults() { - isActive = true; - isActiveIgnored = false; + setImage(null); + setWorkingImage(null); + setActive(true); + setActiveIgnored(false); + imageRect = null; isBlended = false; isMirrored = false; isLooping = true; @@ -398,25 +358,25 @@ private void setDefaults() baseAlpha = 255; firstFrame = 0; lastFrame = -1; - imageRect = null; - image = null; - working = null; } // Updates the global image object to match the shared size of the current BAM (cycle). private void updateCanvas() { imageRect = control.calculateSharedCanvas(isMirrored()); - if (working == null || image == null || + BufferedImage image = (BufferedImage)getImage(); + if (getWorkingImage() == null || image == null || image.getWidth() != imageRect.width || image.getHeight() != imageRect.height) { - image = ColorConvert.createCompatibleImage(imageRect.width, imageRect.height, true); - working = new BufferedImage(imageRect.width, imageRect.height, BufferedImage.TYPE_INT_ARGB); + setImage(ColorConvert.createCompatibleImage(imageRect.width, imageRect.height, true)); + setWorkingImage(new BufferedImage(imageRect.width, imageRect.height, BufferedImage.TYPE_INT_ARGB)); } } // Renders the current frame - private synchronized void updateGraphics() + @Override + protected synchronized void updateGraphics() { + BufferedImage image = (BufferedImage)getImage(); if (image != null) { if (isActive() || isActiveIgnored()) { // preparing frames @@ -438,6 +398,7 @@ private synchronized void updateGraphics() g.fillRect(0, 0, image.getWidth(), image.getHeight()); // rendering frame + BufferedImage working = getWorkingImage(); for (int i = 0; i < frameIndices.length; i++) { // fetching frame data int[] buffer = ((DataBufferInt)working.getRaster().getDataBuffer()).getData(); @@ -571,35 +532,4 @@ private void applyBlending(int[] buffer, int cw, int ch, int fw, int fh) } } } - - // Applies lightning conditions to all pixels - private void applyLighting(int[] buffer, int cw, int ch, int fw, int fh, int lighting) - { - if (buffer != null && cw > 0 && ch > 0 && fw > 0 && fh > 0) { - int maxOfs = fh*cw; - if (buffer.length >= maxOfs) { - if (lighting < ViewerConstants.LIGHTING_DAY) lighting = ViewerConstants.LIGHTING_DAY; - else if (lighting > ViewerConstants.LIGHTING_NIGHT) lighting = ViewerConstants.LIGHTING_NIGHT; - int ofs = 0; - while (ofs < maxOfs) { - for (int x = 0; x < fw; x++) { - int pixel = buffer[ofs+x]; - if ((pixel & 0xff000000) != 0) { - int r = (pixel >>> 16) & 0xff; - int g = (pixel >>> 8) & 0xff; - int b = pixel & 0xff; - r = (r*TilesetRenderer.LightingAdjustment[lighting][0]) >>> TilesetRenderer.LightingAdjustmentShift; - g = (g*TilesetRenderer.LightingAdjustment[lighting][1]) >>> TilesetRenderer.LightingAdjustmentShift; - b = (b*TilesetRenderer.LightingAdjustment[lighting][2]) >>> TilesetRenderer.LightingAdjustmentShift; - if (r > 255) r = 255; - if (g > 255) g = 255; - if (b > 255) b = 255; - buffer[ofs+x] = (pixel & 0xff000000) | (r << 16) | (g << 8) | b; - } - } - ofs += cw; - } - } - } - } } diff --git a/src/org/infinity/resource/are/viewer/BasicLayer.java b/src/org/infinity/resource/are/viewer/BasicLayer.java index b57e9f3ca..1e3429100 100644 --- a/src/org/infinity/resource/are/viewer/BasicLayer.java +++ b/src/org/infinity/resource/are/viewer/BasicLayer.java @@ -158,9 +158,10 @@ public final int loadLayer(boolean forced) * * @param Type of the items on the layer */ - protected final void loadLayerItems(String offsetAttribute, String countAttribute, - Class itemClass, Function newLayerObject) + protected void loadLayerItems(String offsetAttribute, String countAttribute, + Class itemClass, Function newLayerObject) { +// long timeStart = System.nanoTime(); final SectionOffset so = (SectionOffset)parent.getAttribute(offsetAttribute); final SectionCount sc = (SectionCount )parent.getAttribute(countAttribute); if (so != null && sc != null) { @@ -173,6 +174,8 @@ protected final void loadLayerItems(String offsetAttribu } setInitialized(true); } +// long timeEnd = System.nanoTime(); +// System.out.printf("Area viewer > load layer items (%s): %,d µs\n", itemClass.getSimpleName(), (timeEnd - timeStart) / 1000); } /** Loads all available objects of this layer. */ diff --git a/src/org/infinity/resource/are/viewer/LayerActor.java b/src/org/infinity/resource/are/viewer/LayerActor.java index a29ab07ee..133584f6b 100644 --- a/src/org/infinity/resource/are/viewer/LayerActor.java +++ b/src/org/infinity/resource/are/viewer/LayerActor.java @@ -4,12 +4,32 @@ package org.infinity.resource.are.viewer; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.List; import java.util.Locale; +import javax.swing.ProgressMonitor; +import javax.swing.SwingWorker; + +import org.infinity.datatype.IsTextual; +import org.infinity.gui.WindowBlocker; +import org.infinity.gui.layeritem.AbstractLayerItem; +import org.infinity.gui.layeritem.AnimatedLayerItem; +import org.infinity.gui.layeritem.IconLayerItem; +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.Actor; import org.infinity.resource.are.AreResource; +import org.infinity.resource.gam.GamResource; +import org.infinity.resource.gam.PartyNPC; +import org.infinity.resource.key.FileResourceEntry; +import org.infinity.resource.key.ResourceEntry; + import static org.infinity.resource.are.AreResource.ARE_NUM_ACTORS; import static org.infinity.resource.are.AreResource.ARE_OFFSET_ACTORS; import org.infinity.resource.text.PlainTextResource; @@ -21,10 +41,17 @@ /** * Manages actor layer objects. */ -public class LayerActor extends BasicLayer +public class LayerActor extends BasicLayer implements PropertyChangeListener { private static final String AvailableFmt = "Actors: %d"; + private boolean realEnabled, realPlaying, forcedInterpolation, selectionCircleEnabled, personalSpaceEnabled; + private int frameState; + private Object interpolationType = ViewerConstants.TYPE_NEAREST_NEIGHBOR; + private double frameRate = ViewerConstants.FRAME_AUTO; + private SwingWorker loadWorker; + private WindowBlocker blocker; + public LayerActor(AreResource are, AreaViewer viewer) { super(are, ViewerConstants.LayerType.ACTOR, viewer); @@ -38,7 +65,7 @@ protected void loadLayer() loadLayerItems(ARE_OFFSET_ACTORS, ARE_NUM_ACTORS, Actor.class, a -> new LayerObjectAreActor(parent, a)); - final List list = getLayerObjects(); + final List objectList = getLayerObjects(); // loading actors from associated INI final String iniFile = parent.getResourceEntry().getResourceName().toUpperCase(Locale.ENGLISH).replace(".ARE", ".INI"); IniMap ini = ResourceFactory.resourceExists(iniFile) ? IniMapCache.get(iniFile) : null; @@ -53,7 +80,7 @@ protected void loadLayer() PlainTextResource iniRes = new PlainTextResource(ResourceFactory.getResourceEntry(iniFile)); LayerObjectActor obj = new LayerObjectIniActor(iniRes, section, j); setListeners(obj); - list.add(obj); + objectList.add(obj); } catch (Exception e) { e.printStackTrace(); } @@ -61,6 +88,48 @@ protected void loadLayer() } } } + + // loading global actors from save's baldur.gam or default .gam + ResourceEntry areEntry = parent.getResourceEntry(); + if (areEntry != null) { + // loading associated GAM resource + ResourceEntry gamEntry = null; + Path arePath = areEntry.getActualPath(); + if (arePath != null) { + Path gamPath = arePath.getParent().resolve((String)Profile.getProperty(Profile.Key.GET_GAM_NAME)); + if (Files.isRegularFile(gamPath)) { + gamEntry = new FileResourceEntry(gamPath, false); + } + } + if (gamEntry == null) { + gamEntry = ResourceFactory.getResourceEntry(Profile.getProperty(Profile.Key.GET_GAM_NAME)); + } + + // scanning global NPCs + if (gamEntry != null) { + Resource res = ResourceFactory.getResource(gamEntry); + if (res instanceof GamResource) { + GamResource gamRes = (GamResource)res; + List npcList = gamRes.getFields(PartyNPC.class); + for (int i = 0, cnt = npcList.size(); i < cnt; i++) { + PartyNPC npc = (PartyNPC)npcList.get(i); + String area = ((IsTextual)npc.getAttribute(PartyNPC.GAM_NPC_CURRENT_AREA)).getText(); + if (areEntry.getResourceRef().equalsIgnoreCase(area)) { + try { + LayerObjectActor loa = new LayerObjectGlobalActor(gamRes, npc); + setListeners(loa); + objectList.add(loa); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + } + } + + // sorting entries by vertical position to fix overlapping issues + getLayerObjects().sort((c1, c2) -> c2.location.y - c1.location.y); } @Override @@ -69,4 +138,318 @@ public String getAvailability() int cnt = getLayerObjectCount(); return String.format(AvailableFmt, cnt); } + + /** + * Sets the visibility state of all items in the layer. Takes enabled states of the different + * item types into account. + */ + @Override + public void setLayerVisible(boolean visible) + { + setVisibilityState(visible); + + loadWorker = new SwingWorker() { + @Override + public Void doInBackground() + { + final List list = getLayerObjects(); + final ProgressMonitor progress = new ProgressMonitor(getViewer(), "Loading actor animations...", "0 %", 0, list.size()); + progress.setMillisToDecideToPopup(500); + progress.setMillisToPopup(1000); + + try { + int visualState = getViewer().getVisualState(); + for (int i = 0, size = list.size(); i < size; i++) { + boolean state = isLayerVisible() && (!isScheduleEnabled() || isScheduled(i)); + LayerObjectActor loa = list.get(i); + + IconLayerItem iconItem = (IconLayerItem)loa.getLayerItem(ViewerConstants.ITEM_ICON); + if (iconItem != null) { + iconItem.setVisible(state && !realEnabled); + } + + AnimatedLayerItem animItem = (AnimatedLayerItem)loa.getLayerItem(ViewerConstants.ITEM_REAL); + if (animItem != null) { + if (animItem.getAnimation() == AbstractAnimationProvider.DEFAULT_ANIMATION_PROVIDER && state && realEnabled) { + // real actor animations loaded on demand + if (blocker == null) { + blocker = new WindowBlocker(getViewer()); + blocker.setBlocked(true); + } + loa.loadAnimation(); + loa.setLighting(visualState); + } + + animItem.setVisible(state && realEnabled); + if (isRealActorEnabled() && isRealActorPlaying()) { + animItem.play(); + } else { + animItem.stop(); + } + } + progress.setNote(((i + 1) * 100 / size) + " %"); + progress.setProgress(i + 1); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + progress.close(); + if (blocker != null) { + blocker.setBlocked(false); + blocker = null; + } + } + return null; + } + }; + + loadWorker.addPropertyChangeListener(this); + loadWorker.execute(); + } + +//--------------------- Begin Interface PropertyChangeListener --------------------- + + @Override + public void propertyChange(PropertyChangeEvent e) + { + if (e.getSource() == loadWorker) { + if ("state".equals(e.getPropertyName()) && + SwingWorker.StateValue.DONE == e.getNewValue()) { + loadWorker = null; + } + } + } + +//--------------------- End Interface PropertyChangeListener --------------------- + + /** + * Returns the currently active interpolation type for real actors. + * @return Either one of ViewerConstants.TYPE_NEAREST_NEIGHBOR, ViewerConstants.TYPE_NEAREST_BILINEAR + * or ViewerConstants.TYPE_BICUBIC. + */ + public Object getRealActorInterpolation() + { + return interpolationType; + } + + /** + * Sets the interpolation type for real actors. + * @param interpolationType Either one of ViewerConstants.TYPE_NEAREST_NEIGHBOR, + * ViewerConstants.TYPE_NEAREST_BILINEAR or ViewerConstants.TYPE_BICUBIC. + */ + public void setRealActorInterpolation(Object interpolationType) + { + if (interpolationType != this.interpolationType) { + this.interpolationType = interpolationType; + for (final LayerObjectActor layer : getLayerObjects()) { + final AnimatedLayerItem item = (AnimatedLayerItem)layer.getLayerItem(ViewerConstants.ITEM_REAL); + if (item != null) { + item.setInterpolationType(interpolationType); + } + } + } + } + + /** + * Returns whether to force the specified interpolation type or use the best one available, depending + * on the current zoom factor. + */ + public boolean isRealActorForcedInterpolation() + { + return forcedInterpolation; + } + + /** + * Specify whether to force the specified interpolation type or use the best one available, depending + * on the current zoom factor. + */ + public void setRealActorForcedInterpolation(boolean forced) + { + if (forced != forcedInterpolation) { + forcedInterpolation = forced; + for (final LayerObjectActor layer : getLayerObjects()) { + final AnimatedLayerItem item = (AnimatedLayerItem)layer.getLayerItem(ViewerConstants.ITEM_REAL); + if (item != null) { + item.setForcedInterpolation(forced); + } + } + } + } + + /** + * Returns whether real actor items or iconic actor items are enabled. + * @return If {@code true}, real actor items are enabled. + * If {@code false}, iconic actor items are enabled. + */ + public boolean isRealActorEnabled() + { + return realEnabled; + } + + /** + * Specify whether iconic actor type or real actor type is enabled. + * @param enable If {@code true}, real actor items will be shown. + * If {@code false}, iconic actor items will be shown. + */ + public void setRealActorEnabled(boolean enable) + { + if (enable != realEnabled) { + realEnabled = enable; + if (isLayerVisible()) { + setLayerVisible(isLayerVisible()); + } + } + } + + /** + * Returns whether real actor items are enabled and animated. + */ + public boolean isRealActorPlaying() + { + return realEnabled && realPlaying; + } + + /** + * Specify whether real actor should be animated. Setting to {@code true} will enable + * real actors automatically. + */ + public void setRealActorPlaying(boolean play) + { + if (play != realPlaying) { + realPlaying = play; + if (realPlaying && !realEnabled) { + realEnabled = true; + } + if (isLayerVisible()) { + setLayerVisible(isLayerVisible()); + } + } + } + + /** + * Returns the current frame visibility. + * @return One of ViewerConstants.FRAME_NEVER, ViewerConstants.FRAME_AUTO or ViewerConstants.FRAME_ALWAYS. + */ + public int getRealActorFrameState() + { + return frameState; + } + + /** + * Specify the frame visibility for real actors + * @param state One of ViewerConstants.FRAME_NEVER, ViewerConstants.FRAME_AUTO or ViewerConstants.FRAME_ALWAYS. + */ + public void setRealActorFrameState(int state) + { + switch (state) { + case ViewerConstants.FRAME_NEVER: + case ViewerConstants.FRAME_AUTO: + case ViewerConstants.FRAME_ALWAYS: + { + frameState = state; + updateFrameState(); + break; + } + } + } + + /** + * Returns whether selection circle of actor sprites is enabled. + */ + public boolean isRealActorSelectionCircleEnabled() + { + return selectionCircleEnabled; + } + + /** + * Specify whether selection circle of actor sprites is enabled. + */ + public void setRealActorSelectionCircleEnabled(boolean enable) + { + if (enable != selectionCircleEnabled) { + selectionCircleEnabled = enable; + for (final LayerObjectActor layer : getLayerObjects()) { + final AnimatedLayerItem item = (AnimatedLayerItem)layer.getLayerItem(ViewerConstants.ITEM_REAL); + if (item.getAnimation() instanceof ActorAnimationProvider) { + ActorAnimationProvider aap = (ActorAnimationProvider)item.getAnimation(); + aap.setSelectionCircleEnabled(selectionCircleEnabled); + } + } + } + } + + /** + * Returns whether personal space indicator of actor sprites is enabled. + */ + public boolean isRealActorPersonalSpaceEnabled() + { + return personalSpaceEnabled; + } + + /** + * Specify whether personal space indicator of actor sprites is enabled. + */ + public void setRealActorPersonalSpaceEnabled(boolean enable) + { + if (enable != personalSpaceEnabled) { + personalSpaceEnabled = enable; + for (final LayerObjectActor layer : getLayerObjects()) { + final AnimatedLayerItem item = (AnimatedLayerItem)layer.getLayerItem(ViewerConstants.ITEM_REAL); + if (item.getAnimation() instanceof ActorAnimationProvider) { + ActorAnimationProvider aap = (ActorAnimationProvider)item.getAnimation(); + aap.setPersonalSpaceEnabled(personalSpaceEnabled); + } + } + } + } + + /** + * Returns the frame rate used for playing back actor sprites. + * @return Frame rate in frames/second. + */ + public double getRealActorFrameRate() + { + return frameRate; + } + + /** + * Specify a new frame rate for real actors. + * @param frameRate Frame rate in frames/second. + */ + public void setRealActorFrameRate(double frameRate) + { + frameRate = Math.min(Math.max(frameRate, 1.0), 30.0); + if (frameRate != this.frameRate) { + this.frameRate = frameRate; + for (final LayerObjectActor layer : getLayerObjects()) { + final AnimatedLayerItem item = (AnimatedLayerItem)layer.getLayerItem(ViewerConstants.ITEM_REAL); + if (item != null) { + item.setFrameRate(frameRate); + } + } + } + } + + private void updateFrameState() + { + for (final LayerObjectActor layer : getLayerObjects()) { + final AnimatedLayerItem item = (AnimatedLayerItem)layer.getLayerItem(ViewerConstants.ITEM_REAL); + if (item != null) { + switch (frameState) { + case ViewerConstants.FRAME_NEVER: + item.setFrameEnabled(AbstractLayerItem.ItemState.NORMAL, false); + item.setFrameEnabled(AbstractLayerItem.ItemState.HIGHLIGHTED, false); + break; + case ViewerConstants.FRAME_AUTO: + item.setFrameEnabled(AbstractLayerItem.ItemState.NORMAL, false); + item.setFrameEnabled(AbstractLayerItem.ItemState.HIGHLIGHTED, true); + break; + case ViewerConstants.FRAME_ALWAYS: + item.setFrameEnabled(AbstractLayerItem.ItemState.NORMAL, true); + item.setFrameEnabled(AbstractLayerItem.ItemState.HIGHLIGHTED, true); + break; + } + } + } + } } diff --git a/src/org/infinity/resource/are/viewer/LayerAnimation.java b/src/org/infinity/resource/are/viewer/LayerAnimation.java index 7f0ee4bda..cde7e7352 100644 --- a/src/org/infinity/resource/are/viewer/LayerAnimation.java +++ b/src/org/infinity/resource/are/viewer/LayerAnimation.java @@ -38,6 +38,7 @@ public LayerAnimation(AreResource are, AreaViewer viewer) @Override protected void loadLayer() { + // loading animations from ARE loadLayerItems(ARE_OFFSET_ANIMATIONS, ARE_NUM_ANIMATIONS, Animation.class, a -> new LayerObjectAnimation(parent, a)); @@ -83,11 +84,11 @@ public void setLayerVisible(boolean visible) for (int i = 0, size = list.size(); i < size; i++) { boolean state = isLayerVisible() && (!isScheduleEnabled() || isScheduled(i)); LayerObjectAnimation obj = list.get(i); - IconLayerItem iconItem = (IconLayerItem)obj.getLayerItem(ViewerConstants.ANIM_ITEM_ICON); + IconLayerItem iconItem = (IconLayerItem)obj.getLayerItem(ViewerConstants.ITEM_ICON); if (iconItem != null) { iconItem.setVisible(state && !realEnabled); } - AnimatedLayerItem animItem = (AnimatedLayerItem)obj.getLayerItem(ViewerConstants.ANIM_ITEM_REAL); + AnimatedLayerItem animItem = (AnimatedLayerItem)obj.getLayerItem(ViewerConstants.ITEM_REAL); if (animItem != null) { animItem.setVisible(state && realEnabled); if (isRealAnimationEnabled() && isRealAnimationPlaying()) { @@ -110,7 +111,7 @@ public Object getRealAnimationInterpolation() } /** - * Sets the interpolation type for real animations + * Sets the interpolation type for real animations. * @param interpolationType Either one of ViewerConstants.TYPE_NEAREST_NEIGHBOR, * ViewerConstants.TYPE_NEAREST_BILINEAR or ViewerConstants.TYPE_BICUBIC. */ @@ -119,7 +120,7 @@ public void setRealAnimationInterpolation(Object interpolationType) if (interpolationType != this.interpolationType) { this.interpolationType = interpolationType; for (final LayerObjectAnimation layer : getLayerObjects()) { - final AnimatedLayerItem item = (AnimatedLayerItem)layer.getLayerItem(ViewerConstants.ANIM_ITEM_REAL); + final AnimatedLayerItem item = (AnimatedLayerItem)layer.getLayerItem(ViewerConstants.ITEM_REAL); if (item != null) { item.setInterpolationType(interpolationType); } @@ -145,7 +146,7 @@ public void setRealAnimationForcedInterpolation(boolean forced) if (forced != forcedInterpolation) { forcedInterpolation = forced; for (final LayerObjectAnimation layer : getLayerObjects()) { - final AnimatedLayerItem item = (AnimatedLayerItem)layer.getLayerItem(ViewerConstants.ANIM_ITEM_REAL); + final AnimatedLayerItem item = (AnimatedLayerItem)layer.getLayerItem(ViewerConstants.ITEM_REAL); if (item != null) { item.setForcedInterpolation(forced); } @@ -249,7 +250,7 @@ public void setRealAnimationFrameRate(double frameRate) if (frameRate != this.frameRate) { this.frameRate = frameRate; for (final LayerObjectAnimation layer : getLayerObjects()) { - final AnimatedLayerItem item = (AnimatedLayerItem)layer.getLayerItem(ViewerConstants.ANIM_ITEM_REAL); + final AnimatedLayerItem item = (AnimatedLayerItem)layer.getLayerItem(ViewerConstants.ITEM_REAL); if (item != null) { item.setFrameRate(frameRate); } @@ -274,10 +275,10 @@ public void setRealAnimationActiveIgnored(boolean set) { isAnimActiveIgnored = set; for (final LayerObjectAnimation layer : getLayerObjects()) { - final AnimatedLayerItem item = (AnimatedLayerItem)layer.getLayerItem(ViewerConstants.ANIM_ITEM_REAL); + final AnimatedLayerItem item = (AnimatedLayerItem)layer.getLayerItem(ViewerConstants.ITEM_REAL); if (item != null) { - if (item.getAnimation() instanceof BackgroundAnimationProvider) { - ((BackgroundAnimationProvider)item.getAnimation()).setActiveIgnored(set); + if (item.getAnimation() instanceof AbstractAnimationProvider) { + ((AbstractAnimationProvider)item.getAnimation()).setActiveIgnored(set); } } } @@ -287,7 +288,7 @@ public void setRealAnimationActiveIgnored(boolean set) private void updateFrameState() { for (final LayerObjectAnimation layer : getLayerObjects()) { - final AnimatedLayerItem item = (AnimatedLayerItem)layer.getLayerItem(ViewerConstants.ANIM_ITEM_REAL); + final AnimatedLayerItem item = (AnimatedLayerItem)layer.getLayerItem(ViewerConstants.ITEM_REAL); if (item != null) { switch (frameState) { case ViewerConstants.FRAME_NEVER: diff --git a/src/org/infinity/resource/are/viewer/LayerManager.java b/src/org/infinity/resource/are/viewer/LayerManager.java index 240a1dbca..9d281b3b6 100644 --- a/src/org/infinity/resource/are/viewer/LayerManager.java +++ b/src/org/infinity/resource/are/viewer/LayerManager.java @@ -33,7 +33,7 @@ public final class LayerManager LayerType.WALL_POLY }; - private static final EnumMap LayerLabels = new EnumMap(LayerType.class); + private static final EnumMap LayerLabels = new EnumMap<>(LayerType.class); static { LayerLabels.put(LayerType.ACTOR, "Actors"); LayerLabels.put(LayerType.REGION, "Regions"); @@ -391,6 +391,29 @@ public void setDoorState(int state) ((LayerDoorPoly)getLayer(LayerType.DOOR_POLY)).setDoorState(state); } + /** + * Returns whether to show iconic representations of actors or the real thing. + */ + public boolean isRealActorEnabled() + { + LayerActor layer = (LayerActor)getLayer(ViewerConstants.LayerType.ACTOR); + if (layer != null) { + return layer.isRealActorEnabled(); + } + return false; + } + + /** + * Specify whether to show iconic representations of actors or the real thing. + */ + public void setRealActorEnabled(boolean enable) + { + LayerActor layer = (LayerActor)getLayer(ViewerConstants.LayerType.ACTOR); + if (layer != null) { + layer.setRealActorEnabled(enable); + } + } + /** * Returns whether to show iconic representations of background animations or the real thing. */ @@ -414,6 +437,30 @@ public void setRealAnimationEnabled(boolean enable) } } + /** + * Returns whether real actor sprites are enabled and animated. + */ + public boolean isRealActorPlaying() + { + LayerActor layer = (LayerActor)getLayer(ViewerConstants.LayerType.ACTOR); + if (layer != null) { + return layer.isRealActorPlaying(); + } + return false; + } + + /** + * Specify whether to animate real actor sprites. Setting to {@code true} implicitly + * enables real actors. + */ + public void setRealActorPlaying(boolean play) + { + LayerActor layer = (LayerActor)getLayer(ViewerConstants.LayerType.ACTOR); + if (layer != null) { + layer.setRealActorPlaying(play); + } + } + /** * Returns whether real animations are enabled and animated. */ @@ -449,7 +496,7 @@ public Object getRealAnimationInterpolation() } /** - * Sets the interpolation type for real animations + * Sets the interpolation type for real animations. * @param interpolationType Either one of ViewerConstants.TYPE_NEAREST_NEIGHBOR, * ViewerConstants.TYPE_NEAREST_BILINEAR or ViewerConstants.TYPE_BICUBIC. */ @@ -459,9 +506,13 @@ public void setRealAnimationInterpolation(Object interpolationType) interpolationType == ViewerConstants.TYPE_BILINEAR || interpolationType == ViewerConstants.TYPE_BICUBIC) { animInterpolationType = interpolationType; - LayerAnimation layer = (LayerAnimation)getLayer(LayerType.ANIMATION); - if (layer != null) { - layer.setRealAnimationInterpolation(animInterpolationType); + LayerAnimation layerAnim = (LayerAnimation)getLayer(LayerType.ANIMATION); + if (layerAnim != null) { + layerAnim.setRealAnimationInterpolation(animInterpolationType); + } + LayerActor layerActor = (LayerActor)getLayer(LayerType.ACTOR); + if (layerActor != null) { + layerActor.setRealActorInterpolation(animInterpolationType); } } } @@ -482,9 +533,13 @@ public boolean isRealAnimationForcedInterpolation() public void setRealAnimationForcedInterpolation(boolean forced) { animForcedInterpolation = forced; - LayerAnimation layer = (LayerAnimation)getLayer(LayerType.ANIMATION); - if (layer != null) { - layer.setRealAnimationForcedInterpolation(animForcedInterpolation); + LayerAnimation layerAnim = (LayerAnimation)getLayer(LayerType.ANIMATION); + if (layerAnim != null) { + layerAnim.setRealAnimationForcedInterpolation(animForcedInterpolation); + } + LayerActor layerActor = (LayerActor)getLayer(LayerType.ACTOR); + if (layerActor != null) { + layerActor.setRealActorForcedInterpolation(animForcedInterpolation); } } @@ -506,9 +561,13 @@ public void setRealAnimationFrameRate(double frameRate) frameRate = Math.min(Math.max(frameRate, 1.0), 30.0); if (frameRate != this.animFrameRate) { animFrameRate = frameRate; - LayerAnimation layer = (LayerAnimation)getLayer(LayerType.ANIMATION); - if (layer != null) { - layer.setRealAnimationFrameRate(animFrameRate); + LayerAnimation layerAnim = (LayerAnimation)getLayer(LayerType.ANIMATION); + if (layerAnim != null) { + layerAnim.setRealAnimationFrameRate(animFrameRate); + } + LayerActor layerActor = (LayerActor)getLayer(LayerType.ACTOR); + if (layerActor != null) { + layerActor.setRealActorFrameRate(animFrameRate); } } } diff --git a/src/org/infinity/resource/are/viewer/LayerObject.java b/src/org/infinity/resource/are/viewer/LayerObject.java index 018ca0fb7..144b1a5dc 100644 --- a/src/org/infinity/resource/are/viewer/LayerObject.java +++ b/src/org/infinity/resource/are/viewer/LayerObject.java @@ -13,7 +13,6 @@ import java.awt.event.MouseMotionListener; import java.util.EventListener; -import org.infinity.datatype.DecNumber; import org.infinity.datatype.Flag; import org.infinity.datatype.IsNumeric; import org.infinity.datatype.ResourceRef; @@ -190,8 +189,8 @@ protected static Point[] loadVertices(AbstractStruct superStruct, int baseOfs, i if (e.getOffset() >= baseOfs && type.isAssignableFrom(e.getClass())) { if (idx >= index) { final Vertex vertex = (Vertex)e; - coords[cnt] = new Point(((DecNumber)vertex.getAttribute(Vertex.VERTEX_X)).getValue(), - ((DecNumber)vertex.getAttribute(Vertex.VERTEX_Y)).getValue()); + coords[cnt] = new Point(((IsNumeric)vertex.getAttribute(Vertex.VERTEX_X)).getValue(), + ((IsNumeric)vertex.getAttribute(Vertex.VERTEX_Y)).getValue()); cnt++; if (cnt >= count) { break; diff --git a/src/org/infinity/resource/are/viewer/LayerObjectActor.java b/src/org/infinity/resource/are/viewer/LayerObjectActor.java index e8cb233ec..1bfcaa24c 100644 --- a/src/org/infinity/resource/are/viewer/LayerObjectActor.java +++ b/src/org/infinity/resource/are/viewer/LayerObjectActor.java @@ -4,20 +4,86 @@ package org.infinity.resource.are.viewer; +import java.awt.Color; import java.awt.Point; +import java.util.Objects; +import org.infinity.datatype.IsNumeric; import org.infinity.gui.layeritem.AbstractLayerItem; -import org.infinity.gui.layeritem.IconLayerItem; +import org.infinity.gui.layeritem.AnimatedLayerItem; +import org.infinity.gui.layeritem.BasicAnimationProvider; import org.infinity.resource.AbstractStruct; +import org.infinity.resource.cre.CreResource; +import org.infinity.resource.cre.decoder.SpriteDecoder; +import org.infinity.resource.cre.decoder.util.AnimationInfo; +import org.infinity.resource.cre.decoder.util.Sequence; +import org.infinity.resource.graphics.BamDecoder; /** * Base class for layer type: Actor */ public abstract class LayerObjectActor extends LayerObject { + /** Available creature allegiance types. */ + protected enum Allegiance { + GOOD, + NEUTRAL, + ENEMY, + } + + protected static final Color COLOR_FRAME_NORMAL = new Color(0xA02020FF, true); + protected static final Color COLOR_FRAME_HIGHLIGHTED = new Color(0xFF2020FF, false); + + // Default animation sequence to load if available; fall back to the first available sequence if no default is available + private static final Sequence[] DEFAULT_SEQUENCE = { + Sequence.STAND, + Sequence.STAND2, + Sequence.STAND3, + Sequence.STAND_EMERGED, + Sequence.PST_STAND, + Sequence.STANCE, + Sequence.STANCE2, + Sequence.PST_STANCE, + Sequence.WALK, + Sequence.PST_WALK, + }; + // Potential sequences for "death" state + private static final Sequence[] DEATH_SEQUENCE = { + Sequence.TWITCH, + Sequence.DIE, + Sequence.PST_DIE_FORWARD, + Sequence.PST_DIE_BACKWARD, + Sequence.PST_DIE_COLLAPSE, + }; + // Potential sequences for "frozen death" state + private static final Sequence[] FROZEN_DEATH_SEQUENCE = { + Sequence.STANCE, + Sequence.STANCE2, + Sequence.PST_STANCE, + Sequence.STAND, + Sequence.STAND2, + Sequence.STAND3, + Sequence.STAND_EMERGED, + Sequence.TWITCH, + Sequence.DIE, + Sequence.PST_DIE_FORWARD, + Sequence.PST_DIE_BACKWARD, + Sequence.PST_DIE_COLLAPSE, + }; + // Potential sequences for "unconscious" state + private static final Sequence[] SLEEP_SEQUENCE = { + Sequence.SLEEP, + Sequence.SLEEP2, + Sequence.TWITCH, + Sequence.DIE, + Sequence.PST_DIE_FORWARD, + Sequence.PST_DIE_BACKWARD, + Sequence.PST_DIE_COLLAPSE, + }; + protected final Point location = new Point(); + protected final AbstractLayerItem[] items = new AbstractLayerItem[2]; - protected IconLayerItem item; protected LayerObjectActor(Class classType, AbstractStruct parent) @@ -25,33 +91,234 @@ protected LayerObjectActor(Class classType, AbstractSt super("Actor", classType, parent); } - // @Override public void close() { - // TODO: implement method super.close(); + // removing cached references + for (int i = 0; i < items.length; i++) { + Object key = items[i].getData(); + if (key != null) { + switch (i) { + case ViewerConstants.ITEM_ICON: + SharedResourceCache.remove(SharedResourceCache.Type.ICON, key); + break; + case ViewerConstants.ITEM_REAL: + SharedResourceCache.remove(SharedResourceCache.Type.ACTOR, key); + break; + } + } + } } + /** + * Returns the layer item of the specific state. (either ACTOR_ITEM_ICON or ACTOR_ITEM_REAL). + * @param type The state of the item to be returned. + * @return The desired layer item, or {@code null} if not available. + */ @Override public AbstractLayerItem getLayerItem(int type) { - return (type == 0) ? item : null; + type = (type == ViewerConstants.ITEM_REAL) ? ViewerConstants.ITEM_REAL : ViewerConstants.ITEM_ICON; + return items[type]; } @Override public AbstractLayerItem[] getLayerItems() { - return new AbstractLayerItem[]{item}; + return items; } @Override public void update(double zoomFactor) { - if (item != null) { - item.setItemLocation((int)(location.x*zoomFactor + (zoomFactor / 2.0)), - (int)(location.y*zoomFactor + (zoomFactor / 2.0))); + for (int i = 0; i < items.length; i++) { + items[i].setItemLocation((int)(location.x*zoomFactor + (zoomFactor / 2.0)), + (int)(location.y*zoomFactor + (zoomFactor / 2.0))); + if (i == ViewerConstants.ITEM_REAL) { + ((AnimatedLayerItem)items[i]).setZoomFactor(zoomFactor); + } + } + } + + /** + * Loads animation data if it hasn't been loaded yet. + */ + public abstract void loadAnimation(); + + /** + * Sets the lighting condition of the actor. Does nothing if the actor is flagged as + * self-illuminating. + * @param dayTime One of the constants: {@code TilesetRenderer.LIGHTING_DAY}, + * {@code TilesetRenderer.LIGHTING_TWILIGHT}, {@code TilesetRenderer.LIGHTING_NIGHT}. + */ + public void setLighting(int dayTime) + { + AnimatedLayerItem item = (AnimatedLayerItem)items[ViewerConstants.ITEM_REAL]; + BasicAnimationProvider provider = item.getAnimation(); + if (provider instanceof ActorAnimationProvider) { + ActorAnimationProvider anim = (ActorAnimationProvider)provider; + anim.setLighting(dayTime); + } + item.repaint(); + } + + /** Returns the allegiance of the specified EA value. */ + protected static Allegiance getAllegiance(int ea) + { + if (ea >= 2 && ea <= 30) { + return Allegiance.GOOD; + } else if (ea >= 200) { + return Allegiance.ENEMY; + } else { + return Allegiance.NEUTRAL; + } + } + + /** + * Creates an {@code AnimationProvider} object and initializes it with the creature animation defined by the + * specified CRE resource. + * @param cre the CRE resource + * @return a initialized {@link ActorAnimationProvider} instance + * @throws Exception if animation provider could not be created or initialized. + */ + protected static ActorAnimationProvider createAnimationProvider(CreResource cre) throws Exception + { + Objects.requireNonNull(cre); + ActorAnimationProvider retVal = null; + SpriteDecoder decoder = null; + + final int maskDeath = 0xf00; // actor uses regular death sequence? + final int maskFrozenDeath = 0x40; // actor status is "frozen death"? + final int maskStoneDeath = 0x80; // actor status is "stone death"? + final int maskSleep = 0x01; // actor uses sleep sequence? + int status = ((IsNumeric)cre.getAttribute(CreResource.CRE_STATUS)).getValue(); + boolean isDead = (status & maskDeath) != 0; + boolean isFrozenDeath = (status & maskFrozenDeath) != 0; + boolean isStoneDeath = (status & maskStoneDeath) != 0; + boolean isUnconscious = (status & maskSleep) != 0; + boolean isStillFrame = isDead || isFrozenDeath || isStoneDeath || isUnconscious; + + // loading SpriteDecoder instance from cache if available + String key = createKey(cre); + if (!SharedResourceCache.contains(SharedResourceCache.Type.ACTOR, key)) { + // create new + decoder = SpriteDecoder.importSprite(cre); + decoder.setSelectionCircleEnabled(Settings.ShowActorSelectionCircle); + decoder.setPersonalSpaceVisible(Settings.ShowActorPersonalSpace); + + Sequence sequence = null; + + // check for special animation sequence + if (isDead) { + sequence = getMatchingSequence(decoder, DEATH_SEQUENCE); + } else if (isFrozenDeath) { + sequence = getMatchingSequence(decoder, FROZEN_DEATH_SEQUENCE); + } else if (isUnconscious) { + sequence = getMatchingSequence(decoder, SLEEP_SEQUENCE); + } + + if (sequence == null) { + // improve visualization of flying creatures + if (decoder.getAnimationType() == AnimationInfo.Type.FLYING && + decoder.isSequenceAvailable(Sequence.WALK)) { + sequence = Sequence.WALK; + } + } + + if (sequence == null) { + // determine default animation sequence to load + sequence = getMatchingSequence(decoder, DEFAULT_SEQUENCE); + } + + if (sequence == null) { + // use first animation sequence if no default sequence is available + sequence = getMatchingSequence(decoder, null); + } + + if (sequence != null) { + decoder.loadSequence(sequence); + } else { + String creName = ""; + if (cre.getResourceEntry() != null) { + creName = cre.getResourceEntry().getResourceName(); + } else if (cre.getName() != null) { + creName = cre.getName(); + } + throw new UnsupportedOperationException("Could not find animation sequence for CRE: " + creName); + } + + SharedResourceCache.add(SharedResourceCache.Type.ACTOR, key, new ResourceAnimation(key, decoder)); + } else { + // use existing + SharedResourceCache.add(SharedResourceCache.Type.ACTOR, key); + BamDecoder bam = ((ResourceAnimation)SharedResourceCache.get(SharedResourceCache.Type.ACTOR, key)).getData(); + if (bam instanceof SpriteDecoder) { + decoder = (SpriteDecoder)bam; + } else { + throw new Exception("Could not load actor animation"); + } + } + + // initial settings + retVal = new ActorAnimationProvider(decoder); + retVal.setActive(true); + + if (isStillFrame) { + // using second last frame to avoid glitches for selected creature animations + retVal.setStartFrame(-2); + retVal.setFrameCap(-2); + retVal.setLooping(false); + } else { + retVal.setLooping(true); + } + + return retVal; + } + + /** Returns a key which is identical for all actors based on the same CRE resource. */ + protected static String createKey(CreResource cre) + { + String retVal; + Objects.requireNonNull(cre); + + if (cre.getResourceEntry() != null) { + // regular CRE resource + retVal = cre.getResourceEntry().getResourceName(); + } else if (cre.getParent() != null) { + // CRE attached to ARE > Actor + retVal = cre.getParent().getName(); + } else { + // failsafe + retVal = Integer.toString(cre.hashCode()); + } + + int status = ((IsNumeric)cre.getAttribute(CreResource.CRE_STATUS)).getValue(); + retVal += "?status=" + status; + + return retVal; + } + + /** Returns the first matching animation sequence listed in {@code sequences} that is available in the {@code SpriteDecoder} instance. */ + protected static Sequence getMatchingSequence(SpriteDecoder decoder, Sequence[] sequences) + { + Sequence retVal = null; + if (sequences == null) { + sequences = Sequence.values(); } + + if (decoder == null || sequences.length == 0) { + return retVal; + } + + for (final Sequence seq : sequences) { + if (decoder.isSequenceAvailable(seq)) { + retVal = seq; + break; + } + } + + return retVal; } - // + } diff --git a/src/org/infinity/resource/are/viewer/LayerObjectAmbient.java b/src/org/infinity/resource/are/viewer/LayerObjectAmbient.java index f07fb881a..e738409ae 100644 --- a/src/org/infinity/resource/are/viewer/LayerObjectAmbient.java +++ b/src/org/infinity/resource/are/viewer/LayerObjectAmbient.java @@ -107,7 +107,6 @@ public LayerObjectAmbient(AreResource parent, Ambient ambient) } } - // @Override public Viewable getViewable() { @@ -168,7 +167,6 @@ public boolean isScheduled(int schedule) return false; } } - // /** * Returns whether the ambient sound uses a local sound radius. diff --git a/src/org/infinity/resource/are/viewer/LayerObjectAnimation.java b/src/org/infinity/resource/are/viewer/LayerObjectAnimation.java index 79a6160c2..3f692080d 100644 --- a/src/org/infinity/resource/are/viewer/LayerObjectAnimation.java +++ b/src/org/infinity/resource/are/viewer/LayerObjectAnimation.java @@ -198,7 +198,6 @@ public LayerObjectAnimation(AreResource parent, Animation anim) items[1] = item2; } - // @Override public void close() { @@ -208,10 +207,10 @@ public void close() Object key = items[i].getData(); if (key != null) { switch (i) { - case ViewerConstants.ANIM_ITEM_ICON: + case ViewerConstants.ITEM_ICON: SharedResourceCache.remove(SharedResourceCache.Type.ICON, key); break; - case ViewerConstants.ANIM_ITEM_REAL: + case ViewerConstants.ITEM_REAL: SharedResourceCache.remove(SharedResourceCache.Type.ANIMATION, key); break; } @@ -233,7 +232,7 @@ public Viewable getViewable() @Override public AbstractLayerItem getLayerItem(int type) { - type = (type == ViewerConstants.ANIM_ITEM_REAL) ? ViewerConstants.ANIM_ITEM_REAL : ViewerConstants.ANIM_ITEM_ICON; + type = (type == ViewerConstants.ITEM_REAL) ? ViewerConstants.ITEM_REAL : ViewerConstants.ITEM_ICON; return items[type]; } @@ -249,7 +248,7 @@ public void update(double zoomFactor) for (int i = 0; i < items.length; i++) { items[i].setItemLocation((int)(location.x*zoomFactor + (zoomFactor / 2.0)), (int)(location.y*zoomFactor + (zoomFactor / 2.0))); - if (i == ViewerConstants.ANIM_ITEM_REAL) { + if (i == ViewerConstants.ITEM_REAL) { ((AnimatedLayerItem)items[i]).setZoomFactor(zoomFactor); } } @@ -264,7 +263,6 @@ public boolean isScheduled(int schedule) return false; } } - // /** * Sets the lighting condition of the animation. Does nothing if the animation is flagged as @@ -274,7 +272,7 @@ public boolean isScheduled(int schedule) */ public void setLighting(int dayTime) { - AnimatedLayerItem item = (AnimatedLayerItem)items[ViewerConstants.ANIM_ITEM_REAL]; + AnimatedLayerItem item = (AnimatedLayerItem)items[ViewerConstants.ITEM_REAL]; BasicAnimationProvider provider = item.getAnimation(); if (provider instanceof BackgroundAnimationProvider) { BackgroundAnimationProvider anim = (BackgroundAnimationProvider)provider; diff --git a/src/org/infinity/resource/are/viewer/LayerObjectAreActor.java b/src/org/infinity/resource/are/viewer/LayerObjectAreActor.java index c17c560fc..f2085de41 100644 --- a/src/org/infinity/resource/are/viewer/LayerObjectAreActor.java +++ b/src/org/infinity/resource/are/viewer/LayerObjectAreActor.java @@ -6,95 +6,110 @@ import java.awt.Image; import java.awt.Point; +import java.util.EnumMap; import org.infinity.datatype.Flag; import org.infinity.datatype.IsNumeric; +import org.infinity.datatype.IsReference; import org.infinity.datatype.IsTextual; -import org.infinity.datatype.ResourceRef; -import org.infinity.datatype.TextString; import org.infinity.gui.layeritem.AbstractLayerItem; +import org.infinity.gui.layeritem.AnimatedLayerItem; import org.infinity.gui.layeritem.IconLayerItem; import org.infinity.icon.Icons; +import org.infinity.resource.Resource; import org.infinity.resource.ResourceFactory; -import org.infinity.resource.StructEntry; import org.infinity.resource.Viewable; import org.infinity.resource.are.Actor; import org.infinity.resource.are.AreResource; import org.infinity.resource.are.viewer.icon.ViewerIcons; import org.infinity.resource.cre.CreResource; +import org.infinity.resource.key.ResourceEntry; /** * Handles specific layer type: ARE/Actor */ public class LayerObjectAreActor extends LayerObjectActor { - private static final Image[] ICONS_GOOD = {Icons.getImage(ViewerIcons.class, ViewerIcons.ICON_ITM_ARE_ACTOR_G_1), - Icons.getImage(ViewerIcons.class, ViewerIcons.ICON_ITM_ARE_ACTOR_G_2)}; - private static final Image[] ICONS_NEUTRAL = {Icons.getImage(ViewerIcons.class, ViewerIcons.ICON_ITM_ARE_ACTOR_B_1), - Icons.getImage(ViewerIcons.class, ViewerIcons.ICON_ITM_ARE_ACTOR_B_2)}; - private static final Image[] ICONS_EVIL = {Icons.getImage(ViewerIcons.class, ViewerIcons.ICON_ITM_ARE_ACTOR_R_1), - Icons.getImage(ViewerIcons.class, ViewerIcons.ICON_ITM_ARE_ACTOR_R_2)}; + private static final EnumMap ICONS = new EnumMap(Allegiance.class) {{ + put(Allegiance.GOOD, new Image[] {Icons.getImage(ViewerIcons.class, ViewerIcons.ICON_ITM_ARE_ACTOR_G_1), + Icons.getImage(ViewerIcons.class, ViewerIcons.ICON_ITM_ARE_ACTOR_G_2)}); + put(Allegiance.NEUTRAL, new Image[] {Icons.getImage(ViewerIcons.class, ViewerIcons.ICON_ITM_ARE_ACTOR_B_1), + Icons.getImage(ViewerIcons.class, ViewerIcons.ICON_ITM_ARE_ACTOR_B_2)}); + put(Allegiance.ENEMY, new Image[] {Icons.getImage(ViewerIcons.class, ViewerIcons.ICON_ITM_ARE_ACTOR_R_1), + Icons.getImage(ViewerIcons.class, ViewerIcons.ICON_ITM_ARE_ACTOR_R_2)}); + }}; private static final Point CENTER = new Point(12, 40); private final Actor actor; + private final CreResource cre; private Flag scheduleFlags; public LayerObjectAreActor(AreResource parent, Actor actor) { super(Actor.class, parent); this.actor = actor; - String actorName = null; - String actorCreName = null; - Image[] icons = ICONS_NEUTRAL; + int ea = 128; // default: neutral + CreResource cre = null; try { - actorName = ((IsTextual)actor.getAttribute(Actor.ARE_ACTOR_NAME)).getText(); + // initializations location.x = ((IsNumeric)actor.getAttribute(Actor.ARE_ACTOR_POS_X)).getValue(); location.y = ((IsNumeric)actor.getAttribute(Actor.ARE_ACTOR_POS_Y)).getValue(); - scheduleFlags = ((Flag)actor.getAttribute(Actor.ARE_ACTOR_PRESENT_AT)); - StructEntry obj = actor.getAttribute(Actor.ARE_ACTOR_CHARACTER); - CreResource cre = null; - if (obj instanceof TextString) { - // ARE in saved game - cre = (CreResource)actor.getAttribute(Actor.ARE_ACTOR_CRE_FILE); - } else - if (obj instanceof ResourceRef) { - final ResourceRef creRef = (ResourceRef)obj; - if (!creRef.isEmpty()) { - cre = new CreResource(ResourceFactory.getResourceEntry(creRef.getResourceName())); + boolean isReference = ((Flag)actor.getAttribute(Actor.ARE_ACTOR_FLAGS)).isFlagSet(0); + if (isReference) { + // external CRE resource? + ResourceEntry creEntry = ResourceFactory.getResourceEntry(((IsReference)actor.getAttribute(Actor.ARE_ACTOR_CHARACTER)).getResourceName()); + if (creEntry != null) { + Resource res = ResourceFactory.getResource(creEntry); + if (res instanceof CreResource) { + cre = (CreResource)res; + } } + } else { + // attached CRE resource? + cre = (CreResource)actor.getAttribute(Actor.ARE_ACTOR_CRE_FILE); } + if (cre != null) { - actorCreName = cre.getAttribute(CreResource.CRE_NAME).toString(); ea = ((IsNumeric)cre.getAttribute(CreResource.CRE_ALLEGIANCE)).getValue(); } - if (ea >= 2 && ea <= 30) { - icons = ICONS_GOOD; - } else if (ea >= 200) { - icons = ICONS_EVIL; - } else { - icons = ICONS_NEUTRAL; - } } catch (Exception e) { e.printStackTrace(); } + this.cre = cre; + // Using cached icons + Image[] icons = ICONS.get(getAllegiance(ea)); icons = getIcons(icons); - final String msg = actorCreName == null - ? actorName - : actorCreName + " (" + actorName + ')'; - item = new IconLayerItem(actor, msg, icons[0], CENTER); - item.setLabelEnabled(Settings.ShowLabelActorsAre); - item.setName(getCategory()); - item.setImage(AbstractLayerItem.ItemState.HIGHLIGHTED, icons[1]); - item.setVisible(isVisible()); + String tooltip = getTooltip(); + IconLayerItem item1 = new IconLayerItem(actor, tooltip, icons[0], CENTER); + item1.setLabelEnabled(Settings.ShowLabelActorsAre); + item1.setName(getCategory()); + item1.setToolTipText(tooltip); + item1.setImage(AbstractLayerItem.ItemState.HIGHLIGHTED, icons[1]); + item1.setVisible(isVisible()); + items[0] = item1; + + // payload is initialized on demand + AnimatedLayerItem item2 = new AnimatedLayerItem(actor, tooltip, AbstractAnimationProvider.DEFAULT_ANIMATION_PROVIDER); + item2.setName(getCategory()); + item2.setToolTipText(tooltip); + item2.setVisible(false); + item2.setFrameRate(Settings.getDefaultFrameRateAnimations()); + item2.setAutoPlay(false); + item2.setFrameColor(AbstractLayerItem.ItemState.NORMAL, COLOR_FRAME_NORMAL); + item2.setFrameWidth(AbstractLayerItem.ItemState.NORMAL, 2); + item2.setFrameEnabled(AbstractLayerItem.ItemState.NORMAL, false); + item2.setFrameColor(AbstractLayerItem.ItemState.HIGHLIGHTED, COLOR_FRAME_HIGHLIGHTED); + item2.setFrameWidth(AbstractLayerItem.ItemState.HIGHLIGHTED, 2); + item2.setFrameEnabled(AbstractLayerItem.ItemState.HIGHLIGHTED, true); + items[1] = item2; } - // @Override public Viewable getViewable() { @@ -110,5 +125,38 @@ public boolean isScheduled(int schedule) return false; } } - // + + @Override + public synchronized void loadAnimation() + { + if (items[1] instanceof AnimatedLayerItem) { + AnimatedLayerItem item = (AnimatedLayerItem)items[1]; + if (item.getAnimation() == AbstractAnimationProvider.DEFAULT_ANIMATION_PROVIDER) { + if (cre != null) { + try { + int orientation = ((IsNumeric)actor.getAttribute(Actor.ARE_ACTOR_ORIENTATION)).getValue(); + ActorAnimationProvider sprite = createAnimationProvider(cre); + sprite.setOrientation(orientation); + + item.setAnimation(sprite); + item.setComposite(Settings.UseActorAccurateBlending ? sprite.getDecoder().getComposite() : null); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + } + + /** Tooltip for actor object. */ + private String getTooltip() + { + String retVal = null; + if (cre != null) { + retVal = ((IsTextual)cre.getAttribute(CreResource.CRE_NAME)).getText(); + } else { + retVal = ((IsTextual)actor.getAttribute(Actor.ARE_ACTOR_NAME)).getText(); + } + return retVal; + } } diff --git a/src/org/infinity/resource/are/viewer/LayerObjectAutomap.java b/src/org/infinity/resource/are/viewer/LayerObjectAutomap.java index 0ec147c1d..744785118 100644 --- a/src/org/infinity/resource/are/viewer/LayerObjectAutomap.java +++ b/src/org/infinity/resource/are/viewer/LayerObjectAutomap.java @@ -9,8 +9,6 @@ import java.nio.file.Path; import org.infinity.datatype.IsNumeric; -import org.infinity.datatype.SectionCount; -import org.infinity.datatype.SectionOffset; import org.infinity.gui.layeritem.AbstractLayerItem; import org.infinity.gui.layeritem.IconLayerItem; import org.infinity.icon.Icons; @@ -68,8 +66,8 @@ public LayerObjectAutomap(AreResource parent, AutomapNote note) if (FileEx.create(tohFile).exists()) { FileResourceEntry tohEntry = new FileResourceEntry(tohFile); TohResource toh = new TohResource(tohEntry); - SectionOffset so = (SectionOffset)toh.getAttribute(TohResource.TOH_OFFSET_ENTRIES); - SectionCount sc = (SectionCount)toh.getAttribute(TohResource.TOH_NUM_ENTRIES); + IsNumeric so = (IsNumeric)toh.getAttribute(TohResource.TOH_OFFSET_ENTRIES); + IsNumeric sc = (IsNumeric)toh.getAttribute(TohResource.TOH_NUM_ENTRIES); if (so != null && sc != null && sc.getValue() > 0) { for (int i = 0, count = sc.getValue(), curOfs = so.getValue(); i < count; i++) { StrRefEntry2 strref = (StrRefEntry2)toh.getAttribute(curOfs, false); @@ -97,7 +95,7 @@ public LayerObjectAutomap(AreResource parent, AutomapNote note) FileResourceEntry totEntry = new FileResourceEntry(totFile); TohResource toh = new TohResource(tohEntry); TotResource tot = new TotResource(totEntry); - SectionCount sc = (SectionCount)toh.getAttribute(TohResource.TOH_NUM_ENTRIES); + IsNumeric sc = (IsNumeric)toh.getAttribute(TohResource.TOH_NUM_ENTRIES); if (sc != null && sc.getValue() > 0) { for (int i = 0, count = sc.getValue(), curOfs = 0x14; i < count; i++) { StrRefEntry strref = (StrRefEntry)toh.getAttribute(curOfs, false); @@ -133,7 +131,6 @@ public LayerObjectAutomap(AreResource parent, AutomapNote note) item.setVisible(isVisible()); } - // @Override public Viewable getViewable() { @@ -160,5 +157,4 @@ public void update(double zoomFactor) (int)(location.y*zoomFactor + (zoomFactor / 2.0))); } } - // } diff --git a/src/org/infinity/resource/are/viewer/LayerObjectAutomapPST.java b/src/org/infinity/resource/are/viewer/LayerObjectAutomapPST.java index 2fb8eacad..7f11b72d3 100644 --- a/src/org/infinity/resource/are/viewer/LayerObjectAutomapPST.java +++ b/src/org/infinity/resource/are/viewer/LayerObjectAutomapPST.java @@ -57,7 +57,6 @@ public LayerObjectAutomapPST(AreResource parent, AutomapNotePST note) item.setVisible(isVisible()); } - // @Override public Viewable getViewable() { @@ -84,5 +83,4 @@ public void update(double zoomFactor) (int)(location.y*zoomFactor + (zoomFactor / 2.0))); } } - // } diff --git a/src/org/infinity/resource/are/viewer/LayerObjectContainer.java b/src/org/infinity/resource/are/viewer/LayerObjectContainer.java index e1296434f..138e2a627 100644 --- a/src/org/infinity/resource/are/viewer/LayerObjectContainer.java +++ b/src/org/infinity/resource/are/viewer/LayerObjectContainer.java @@ -64,7 +64,6 @@ public LayerObjectContainer(AreResource parent, Container container) item.setVisible(isVisible()); } - // @Override public Viewable getViewable() { @@ -95,7 +94,6 @@ public void update(double zoomFactor) item.setShape(poly); } } - // private String getAttributes() { diff --git a/src/org/infinity/resource/are/viewer/LayerObjectDoor.java b/src/org/infinity/resource/are/viewer/LayerObjectDoor.java index 7e5b7ed3c..c3cb797e2 100644 --- a/src/org/infinity/resource/are/viewer/LayerObjectDoor.java +++ b/src/org/infinity/resource/are/viewer/LayerObjectDoor.java @@ -73,7 +73,6 @@ public LayerObjectDoor(AreResource parent, Door door) } } - // @Override public Viewable getViewable() { @@ -114,7 +113,6 @@ public void update(double zoomFactor) items[i].setShape(poly); } } - // private String getAttributes() { diff --git a/src/org/infinity/resource/are/viewer/LayerObjectDoorPoly.java b/src/org/infinity/resource/are/viewer/LayerObjectDoorPoly.java index f423f7b46..720561379 100644 --- a/src/org/infinity/resource/are/viewer/LayerObjectDoorPoly.java +++ b/src/org/infinity/resource/are/viewer/LayerObjectDoorPoly.java @@ -89,7 +89,6 @@ public LayerObjectDoorPoly(WedResource parent, Door doorPoly) } } - // @Override public Viewable getViewable() { @@ -130,7 +129,6 @@ public void update(double zoomFactor) items[i].setShape(poly); } } - // /** * Returns an array of layer items of the specified state. diff --git a/src/org/infinity/resource/are/viewer/LayerObjectEntrance.java b/src/org/infinity/resource/are/viewer/LayerObjectEntrance.java index 92f4b614c..9a3871687 100644 --- a/src/org/infinity/resource/are/viewer/LayerObjectEntrance.java +++ b/src/org/infinity/resource/are/viewer/LayerObjectEntrance.java @@ -57,7 +57,6 @@ public LayerObjectEntrance(AreResource parent, Entrance entrance) item.setVisible(isVisible()); } - // @Override public Viewable getViewable() { @@ -84,5 +83,4 @@ public void update(double zoomFactor) (int)(location.y*zoomFactor + (zoomFactor / 2.0))); } } - // } diff --git a/src/org/infinity/resource/are/viewer/LayerObjectGlobalActor.java b/src/org/infinity/resource/are/viewer/LayerObjectGlobalActor.java new file mode 100644 index 000000000..f39b02865 --- /dev/null +++ b/src/org/infinity/resource/are/viewer/LayerObjectGlobalActor.java @@ -0,0 +1,160 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.are.viewer; + +import java.awt.Image; +import java.awt.Point; +import java.util.EnumMap; +import java.util.Objects; + +import org.infinity.datatype.IsNumeric; +import org.infinity.datatype.IsTextual; +import org.infinity.gui.layeritem.AbstractLayerItem; +import org.infinity.gui.layeritem.AnimatedLayerItem; +import org.infinity.gui.layeritem.IconLayerItem; +import org.infinity.icon.Icons; +import org.infinity.resource.Resource; +import org.infinity.resource.ResourceFactory; +import org.infinity.resource.StructEntry; +import org.infinity.resource.Viewable; +import org.infinity.resource.are.viewer.icon.ViewerIcons; +import org.infinity.resource.cre.CreResource; +import org.infinity.resource.gam.GamResource; +import org.infinity.resource.gam.PartyNPC; +import org.infinity.resource.key.ResourceEntry; + +/** + * Handles specific layer type: global GAM/Actor + */ +public class LayerObjectGlobalActor extends LayerObjectActor +{ + private static final EnumMap ICONS = new EnumMap(Allegiance.class) {{ + put(Allegiance.GOOD, new Image[] {Icons.getImage(ViewerIcons.class, ViewerIcons.ICON_ITM_GAM_ACTOR_G_1), + Icons.getImage(ViewerIcons.class, ViewerIcons.ICON_ITM_GAM_ACTOR_G_2)}); + put(Allegiance.NEUTRAL, new Image[] {Icons.getImage(ViewerIcons.class, ViewerIcons.ICON_ITM_GAM_ACTOR_B_1), + Icons.getImage(ViewerIcons.class, ViewerIcons.ICON_ITM_GAM_ACTOR_B_2)}); + put(Allegiance.ENEMY, new Image[] {Icons.getImage(ViewerIcons.class, ViewerIcons.ICON_ITM_GAM_ACTOR_R_1), + Icons.getImage(ViewerIcons.class, ViewerIcons.ICON_ITM_GAM_ACTOR_R_2)}); + }}; + private static final Point CENTER = new Point(12, 40); + + private final PartyNPC npc; + + private CreResource cre; + + public LayerObjectGlobalActor(GamResource parent, PartyNPC npc) + { + super(PartyNPC.class, parent); + this.npc = Objects.requireNonNull(npc); + + int creOfs = ((IsNumeric)this.npc.getAttribute(PartyNPC.GAM_NPC_OFFSET_CRE)).getValue(); + int creSize = ((IsNumeric)this.npc.getAttribute(PartyNPC.GAM_NPC_CRE_SIZE)).getValue(); + if (creOfs > 0 && creSize > 0) { + // attached resource? + StructEntry se = this.npc.getAttribute(PartyNPC.GAM_NPC_CRE_RESOURCE); + if (se instanceof CreResource) { + this.cre = (CreResource)se; + } + } else { + // external resource? + String creRes = ((IsTextual)this.npc.getAttribute(PartyNPC.GAM_NPC_CHARACTER)).getText(); + ResourceEntry entry = ResourceFactory.getResourceEntry(creRes + ".CRE"); + if (entry != null) { + Resource res = ResourceFactory.getResource(entry); + if (res instanceof CreResource) { + this.cre = (CreResource)res; + } + } + } + + if (this.cre == null) { + throw new NullPointerException("Could not determine CRE resource: " + this.npc.getName()); + } + + location.x = ((IsNumeric)this.npc.getAttribute(PartyNPC.GAM_NPC_LOCATION_X)).getValue(); + location.y = ((IsNumeric)this.npc.getAttribute(PartyNPC.GAM_NPC_LOCATION_Y)).getValue(); + + int ea = ((IsNumeric)this.cre.getAttribute(CreResource.CRE_ALLEGIANCE)).getValue(); + Image[] icons = ICONS.get(getAllegiance(ea)); + icons = getIcons(icons); + + String tooltip = getTooltip(); + IconLayerItem item1 = new IconLayerItem(npc, tooltip, icons[0], CENTER); + item1.setLabelEnabled(Settings.ShowLabelActorsAre); + item1.setName(getCategory()); + item1.setToolTipText(tooltip); + item1.setImage(AbstractLayerItem.ItemState.HIGHLIGHTED, icons[1]); + item1.setVisible(isVisible()); + items[0] = item1; + + // payload is initialized on demand + AnimatedLayerItem item2 = new AnimatedLayerItem(npc, tooltip, AbstractAnimationProvider.DEFAULT_ANIMATION_PROVIDER); + item2.setName(getCategory()); + item2.setToolTipText(tooltip); + item2.setVisible(false); + item2.setFrameRate(Settings.getDefaultFrameRateAnimations()); + item2.setAutoPlay(false); + item2.setFrameColor(AbstractLayerItem.ItemState.NORMAL, COLOR_FRAME_NORMAL); + item2.setFrameWidth(AbstractLayerItem.ItemState.NORMAL, 2); + item2.setFrameEnabled(AbstractLayerItem.ItemState.NORMAL, false); + item2.setFrameColor(AbstractLayerItem.ItemState.HIGHLIGHTED, COLOR_FRAME_HIGHLIGHTED); + item2.setFrameWidth(AbstractLayerItem.ItemState.HIGHLIGHTED, 2); + item2.setFrameEnabled(AbstractLayerItem.ItemState.HIGHLIGHTED, true); + items[1] = item2; + } + + @Override + public void loadAnimation() + { + if (items[1] instanceof AnimatedLayerItem) { + AnimatedLayerItem item = (AnimatedLayerItem)items[1]; + if (item.getAnimation() == AbstractAnimationProvider.DEFAULT_ANIMATION_PROVIDER) { + if (cre != null) { + try { + int orientation = ((IsNumeric)npc.getAttribute(PartyNPC.GAM_NPC_ORIENTATION)).getValue(); + ActorAnimationProvider sprite = createAnimationProvider(cre); + sprite.setOrientation(orientation); + + item.setAnimation(sprite); + item.setComposite(Settings.UseActorAccurateBlending ? sprite.getDecoder().getComposite() : null); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + } + + @Override + public Viewable getViewable() + { + return npc; + } + + @Override + public boolean isScheduled(int schedule) + { + return true; // always active + } + + /** Tooltip for actor object. */ + private String getTooltip() + { + String retVal = null; + + retVal = ((IsTextual)npc.getAttribute(PartyNPC.GAM_NPC_NAME)).getText().trim(); + if (retVal.isEmpty()) { + retVal = ((IsTextual)cre.getAttribute(CreResource.CRE_NAME)).getText().trim(); + } + if (retVal.isEmpty()) { + retVal = ((IsTextual)cre.getAttribute(CreResource.CRE_TOOLTIP)).getText().trim(); + } + if (retVal.isEmpty()) { + retVal = "(no name)"; + } + + return retVal; + } +} diff --git a/src/org/infinity/resource/are/viewer/LayerObjectIniActor.java b/src/org/infinity/resource/are/viewer/LayerObjectIniActor.java index d3aa792fd..c0bfe8a6d 100644 --- a/src/org/infinity/resource/are/viewer/LayerObjectIniActor.java +++ b/src/org/infinity/resource/are/viewer/LayerObjectIniActor.java @@ -6,9 +6,12 @@ import java.awt.Image; import java.awt.Point; +import java.util.EnumMap; +import java.util.Objects; import org.infinity.datatype.IsNumeric; import org.infinity.gui.layeritem.AbstractLayerItem; +import org.infinity.gui.layeritem.AnimatedLayerItem; import org.infinity.gui.layeritem.IconLayerItem; import org.infinity.icon.Icons; import org.infinity.resource.ResourceFactory; @@ -25,44 +28,43 @@ */ public class LayerObjectIniActor extends LayerObjectActor { - private static final Image[] ICONS_GOOD = {Icons.getImage(ViewerIcons.class, ViewerIcons.ICON_ITM_INI_ACTOR_G_1), - Icons.getImage(ViewerIcons.class, ViewerIcons.ICON_ITM_INI_ACTOR_G_2)}; - private static final Image[] ICONS_NEUTRAL = {Icons.getImage(ViewerIcons.class, ViewerIcons.ICON_ITM_INI_ACTOR_B_1), - Icons.getImage(ViewerIcons.class, ViewerIcons.ICON_ITM_INI_ACTOR_B_2)}; - private static final Image[] ICONS_EVIL = {Icons.getImage(ViewerIcons.class, ViewerIcons.ICON_ITM_INI_ACTOR_R_1), - Icons.getImage(ViewerIcons.class, ViewerIcons.ICON_ITM_INI_ACTOR_R_2)}; + private static final EnumMap ICONS = new EnumMap(Allegiance.class) {{ + put(Allegiance.GOOD, new Image[] {Icons.getImage(ViewerIcons.class, ViewerIcons.ICON_ITM_INI_ACTOR_G_1), + Icons.getImage(ViewerIcons.class, ViewerIcons.ICON_ITM_INI_ACTOR_G_2)}); + put(Allegiance.NEUTRAL, new Image[] {Icons.getImage(ViewerIcons.class, ViewerIcons.ICON_ITM_INI_ACTOR_B_1), + Icons.getImage(ViewerIcons.class, ViewerIcons.ICON_ITM_INI_ACTOR_B_2)}); + put(Allegiance.ENEMY, new Image[] {Icons.getImage(ViewerIcons.class, ViewerIcons.ICON_ITM_INI_ACTOR_R_1), + Icons.getImage(ViewerIcons.class, ViewerIcons.ICON_ITM_INI_ACTOR_R_2)}); + }}; private static final Point CENTER = new Point(12, 40); private final PlainTextResource ini; + private final IniMapSection creData; + private final int creIndex; + private final CreResource cre; + /** + * Creates a new {@code LayerObjectIniActor} instance. + * @param ini INI resource containing actor definitiuons + * @param creData the INI section relevant for this actor definition + * @param creIndex the spawn point location index for this creature + * @throws IllegalArgumentException + */ public LayerObjectIniActor(PlainTextResource ini, IniMapSection creData, int creIndex) throws IllegalArgumentException { super(CreResource.class, null); - this.ini = ini; - // preparations - IniMapEntry entrySpec = creData.getEntry("spec"); - int[] object = (entrySpec != null) ? IniMapEntry.splitObjectValue(entrySpec.getValue()) : null; + this.ini = Objects.requireNonNull(ini); + this.creData = Objects.requireNonNull(creData); + this.creIndex = creIndex; - IniMapEntry entryPoint = creData.getEntry("spawn_point"); - if (entryPoint == null) { - throw new IllegalArgumentException(creData.getName() + ": Invalid spawn point - entry \"spawn_point\" not found in .INI"); - } - String[] position = IniMapEntry.splitValues(entryPoint.getValue(), IniMapEntry.REGEX_POSITION); - if (position == null || creIndex >= position.length) { - throw new IllegalArgumentException(creData.getName() + ": Invalid spawn point index (" + creIndex + ")"); - } - int[] pos = IniMapEntry.splitPositionValue(position[creIndex]); - if (pos == null || pos.length < 2) { - throw new IllegalArgumentException(creData.getName() + ": Invalid spawn point value #" + creIndex); - } - - String sectionName = creData.getName(); + // preparations String[] creNames = IniMapEntry.splitValues(creData.getEntry("cre_file").getValue()); String creName = (creNames.length > 0) ? (creNames[0] + ".cre") : null; ResourceEntry creEntry = ResourceFactory.getResourceEntry(creName); if (creEntry == null) { throw new IllegalArgumentException(creData.getName() + ": Invalid CRE resref (" + creName + ")"); } + CreResource cre = null; try { cre = new CreResource(creEntry); @@ -71,38 +73,63 @@ public LayerObjectIniActor(PlainTextResource ini, IniMapSection creData, int cre throw new IllegalArgumentException(creData.getName() + ": Invalid CRE resource", e); } + this.cre = cre; + // initializations - final String msg = cre.getAttribute(CreResource.CRE_NAME).toString() + " [" + sectionName + "]"; - int ea = ((IsNumeric)cre.getAttribute(CreResource.CRE_ALLEGIANCE)).getValue(); + int[] pos = getCreatureLocation(); location.x = pos[0]; location.y = pos[1]; - // checking for overridden allegiance + // setting creature allegiance + int ea = ((IsNumeric)cre.getAttribute(CreResource.CRE_ALLEGIANCE)).getValue(); + + IniMapEntry entrySpec = creData.getEntry("spec"); + int[] object = (entrySpec != null) ? IniMapEntry.splitObjectValue(entrySpec.getValue()) : null; if (object != null && object.length > 0 && object[0] != 0) { ea = object[0]; } - Image[] icons; - if (ea >= 2 && ea <= 30) { - icons = ICONS_GOOD; - } else if (ea >= 200) { - icons = ICONS_EVIL; - } else { - icons = ICONS_NEUTRAL; - } - // Using cached icons + Image[] icons = ICONS.get(getAllegiance(ea)); icons = getIcons(icons); + String tooltip = getTooltip(); ini.setHighlightedLine(creData.getLine() + 1); - item = new IconLayerItem(ini, msg, icons[0], CENTER); - item.setLabelEnabled(Settings.ShowLabelActorsIni); - item.setName(getCategory()); - item.setImage(AbstractLayerItem.ItemState.HIGHLIGHTED, icons[1]); - item.setVisible(isVisible()); + + IconLayerItem item1 = new IconLayerItem(ini, tooltip, icons[0], CENTER); + item1.setLabelEnabled(Settings.ShowLabelActorsIni); + item1.setName(getCategory()); + item1.setToolTipText(tooltip); + item1.setImage(AbstractLayerItem.ItemState.HIGHLIGHTED, icons[1]); + item1.setVisible(isVisible()); + items[0] = item1; + + // payload is initialized on demand + AnimatedLayerItem item2 = new AnimatedLayerItem(ini, tooltip, AbstractAnimationProvider.DEFAULT_ANIMATION_PROVIDER); + item2.setName(getCategory()); + item2.setToolTipText(tooltip); + item2.setVisible(false); + item2.setFrameRate(Settings.getDefaultFrameRateAnimations()); + item2.setAutoPlay(false); + item2.setFrameColor(AbstractLayerItem.ItemState.NORMAL, COLOR_FRAME_NORMAL); + item2.setFrameWidth(AbstractLayerItem.ItemState.NORMAL, 2); + item2.setFrameEnabled(AbstractLayerItem.ItemState.NORMAL, false); + item2.setFrameColor(AbstractLayerItem.ItemState.HIGHLIGHTED, COLOR_FRAME_HIGHLIGHTED); + item2.setFrameWidth(AbstractLayerItem.ItemState.HIGHLIGHTED, 2); + item2.setFrameEnabled(AbstractLayerItem.ItemState.HIGHLIGHTED, true); + items[1] = item2; + } + + public IniMapSection getCreatureData() + { + return creData; + } + + public int getCreatureIndex() + { + return creIndex; } - // @Override public Viewable getViewable() { @@ -114,5 +141,57 @@ public boolean isScheduled(int schedule) { return true; // always active } - // + + // Returns position and orientation of the current creature + private int[] getCreatureLocation() throws IllegalArgumentException + { + int[] retVal = {0, 0, 0}; + + IniMapEntry entryPoint = creData.getEntry("spawn_point"); + if (entryPoint == null) { + throw new IllegalArgumentException(creData.getName() + ": Invalid spawn point - entry \"spawn_point\" not found in .INI"); + } + + String[] items = IniMapEntry.splitValues(entryPoint.getValue(), IniMapEntry.REGEX_POSITION); + if (items == null || creIndex >= items.length) { + throw new IllegalArgumentException(creData.getName() + ": Invalid spawn point index (" + creIndex + ")"); + } + + int[] pos = IniMapEntry.splitPositionValue(items[creIndex]); + if (pos == null || pos.length < 2) { + throw new IllegalArgumentException(creData.getName() + ": Invalid spawn point value #" + creIndex); + } + + System.arraycopy(pos, 0, retVal, 0, Math.min(retVal.length, pos.length)); + + return retVal; + } + + @Override + public synchronized void loadAnimation() + { + if (items[1] instanceof AnimatedLayerItem) { + AnimatedLayerItem item = (AnimatedLayerItem)items[1]; + if (item.getAnimation() == AbstractAnimationProvider.DEFAULT_ANIMATION_PROVIDER) { + try { + int[] pos = getCreatureLocation(); + int orientation = (pos.length > 2) ? pos[2] : 0; + ActorAnimationProvider sprite = createAnimationProvider(cre); + sprite.setOrientation(orientation); + + item.setAnimation(sprite); + item.setComposite(Settings.UseActorAccurateBlending ? sprite.getDecoder().getComposite() : null); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + + /** Tooltip for actor object. */ + private String getTooltip() + { + String sectionName = creData.getName(); + return cre.getAttribute(CreResource.CRE_NAME).toString() + " [" + sectionName + "]"; + } } diff --git a/src/org/infinity/resource/are/viewer/LayerObjectProTrap.java b/src/org/infinity/resource/are/viewer/LayerObjectProTrap.java index bf4c8c3d6..73fa304c2 100644 --- a/src/org/infinity/resource/are/viewer/LayerObjectProTrap.java +++ b/src/org/infinity/resource/are/viewer/LayerObjectProTrap.java @@ -59,7 +59,6 @@ public LayerObjectProTrap(AreResource parent, ProTrap trap) item.setVisible(isVisible()); } - // @Override public Viewable getViewable() { @@ -86,5 +85,4 @@ public void update(double zoomFactor) (int)(location.y*zoomFactor + (zoomFactor / 2.0))); } } - // } diff --git a/src/org/infinity/resource/are/viewer/LayerObjectRegion.java b/src/org/infinity/resource/are/viewer/LayerObjectRegion.java index 9cc759bd3..ad2c3bda2 100644 --- a/src/org/infinity/resource/are/viewer/LayerObjectRegion.java +++ b/src/org/infinity/resource/are/viewer/LayerObjectRegion.java @@ -74,7 +74,6 @@ ITEPoint.s_type[type], getAttributes(), item.setVisible(isVisible()); } - // @Override public Viewable getViewable() { @@ -104,7 +103,6 @@ public void update(double zoomFactor) item.setShape(poly); } } - // private String getAttributes() { diff --git a/src/org/infinity/resource/are/viewer/LayerObjectSpawnPoint.java b/src/org/infinity/resource/are/viewer/LayerObjectSpawnPoint.java index d1b5bbaab..68982c98b 100644 --- a/src/org/infinity/resource/are/viewer/LayerObjectSpawnPoint.java +++ b/src/org/infinity/resource/are/viewer/LayerObjectSpawnPoint.java @@ -59,7 +59,6 @@ public LayerObjectSpawnPoint(AreResource parent, SpawnPoint sp) item.setVisible(isVisible()); } - // @Override public Viewable getViewable() { @@ -96,5 +95,4 @@ public boolean isScheduled(int schedule) return false; } } - // } diff --git a/src/org/infinity/resource/are/viewer/LayerObjectTransition.java b/src/org/infinity/resource/are/viewer/LayerObjectTransition.java index 051768146..b293f4136 100644 --- a/src/org/infinity/resource/are/viewer/LayerObjectTransition.java +++ b/src/org/infinity/resource/are/viewer/LayerObjectTransition.java @@ -66,7 +66,6 @@ public LayerObjectTransition(AreResource parent, AreResource destination, int ed item.setVisible(isVisible()); } - // @Override public Viewable getViewable() { @@ -125,5 +124,4 @@ public void update(double zoomFactor) item.setShape(poly); } } - // } diff --git a/src/org/infinity/resource/are/viewer/LayerObjectWallPoly.java b/src/org/infinity/resource/are/viewer/LayerObjectWallPoly.java index c61b057cd..3530f65a9 100644 --- a/src/org/infinity/resource/are/viewer/LayerObjectWallPoly.java +++ b/src/org/infinity/resource/are/viewer/LayerObjectWallPoly.java @@ -63,7 +63,6 @@ public LayerObjectWallPoly(WedResource parent, WallPolygon wall) item.setVisible(isVisible()); } - // @Override public Viewable getViewable() { @@ -94,5 +93,4 @@ public void update(double zoomFactor) item.setShape(poly); } } - // } diff --git a/src/org/infinity/resource/are/viewer/Settings.java b/src/org/infinity/resource/are/viewer/Settings.java index 834f33671..a9909170d 100644 --- a/src/org/infinity/resource/are/viewer/Settings.java +++ b/src/org/infinity/resource/are/viewer/Settings.java @@ -67,16 +67,26 @@ public class Settings public static boolean EnableSchedules = getDefaultEnableSchedules(); // Defines whether to ignore the "Is shown" flag of background animations public static boolean OverrideAnimVisibility = getDefaultOverrideAnimVisibility(); + // Defines whether to draw the selection circle underneath actor sprites + public static boolean ShowActorSelectionCircle = getDefaultActorSelectionCircle(); + // Defines whether to draw the personal space indicator underneath actor sprites + public static boolean ShowActorPersonalSpace = getDefaultActorPersonalSpace(); + // Defines whether accurate sprite filtering is used for actors with advanced blending modes + public static boolean UseActorAccurateBlending = getDefaultActorAccurateBlending(); // Bitmask that controls the collapsed/expanded state of the sidebar controls public static int SidebarControls = getDefaultSidebarControls(); + // Indicates whether to show frames around real actor sprites all the time + public static int ShowActorFrame = getDefaultShowActorFrame(); // Indicates whether to show frames around real background animations all the time - public static int ShowFrame = getDefaultShowFrame(); + public static int ShowAnimationFrame = getDefaultShowAnimationFrame(); // Interpolation state of map tileset public static int InterpolationMap = getDefaultInterpolationMap(); // Interpolation state of real background animations public static int InterpolationAnim = getDefaultInterpolationAnim(); // Bitmask defining the enabled state of layer items public static int LayerFlags = getDefaultLayerFlags(); + // The visibility state of real actor sprites (icons/still/animated) + public static int ShowActorSprites = getDefaultShowRealActors(); // The visibility state of real background animations (icons/still/animated) public static int ShowRealAnimations = getDefaultShowRealAnimations(); // The current time of day (in hours) @@ -112,11 +122,16 @@ public class Settings private static final String PREFS_DRAWOVERLAYS = "DrawOverlays"; private static final String PREFS_DRAWGRID = "DrawGrid"; private static final String PREFS_SIDEBARCONTROLS = "SidebarControls"; - private static final String PREFS_SHOWFRAME = "ShowFrame"; + private static final String PREFS_SHOWACTORFRAME = "ShowActorFrame"; + private static final String PREFS_SHOWACTORSELECTION = "ShowActorSelectionCircle"; + private static final String PREFS_SHOWACTORSPACE = "ShowActorPersonalSpace"; + private static final String PREFS_USEACTORBLENDING = "UseActorAccurateBlending"; + private static final String PREFS_SHOWANIMFRAME = "ShowFrame"; private static final String PREFS_SHOWAMBIENT = "ShowAmbientRanges"; private static final String PREFS_ENABLESCHEDULES = "EnableSchedules"; private static final String PREFS_OVERRIDEANIMVISIBILITY = "OverrideAnimVisibility"; private static final String PREFS_LAYERFLAGS = "LayerFlags"; + private static final String PREFS_SHOWREALACTORS = "ShowRealActors"; private static final String PREFS_SHOWREALANIMS = "ShowRealAnimations"; private static final String PREFS_TIMEOFDAY = "TimeOfDay"; private static final String PREFS_ZOOMFACTOR = "ZoomFactor"; @@ -155,7 +170,11 @@ public static void loadSettings(boolean force) ExportLayers = prefs.getBoolean(PREFS_EXPORTLAYERS,getDefaultExportLayers()); MouseWheelZoom = prefs.getBoolean(PREFS_MOUSEWHEELZOOM,getDefaultMouseWheelZoom()); OverrideAnimVisibility = prefs.getBoolean(PREFS_OVERRIDEANIMVISIBILITY, getDefaultOverrideAnimVisibility()); - ShowFrame = prefs.getInt(PREFS_SHOWFRAME, getDefaultShowFrame()); + ShowActorFrame = prefs.getInt(PREFS_SHOWACTORFRAME, getDefaultShowActorFrame()); + ShowActorSelectionCircle = prefs.getBoolean(PREFS_SHOWACTORSELECTION, getDefaultActorSelectionCircle()); + ShowActorPersonalSpace = prefs.getBoolean(PREFS_SHOWACTORSPACE, getDefaultActorPersonalSpace()); + UseActorAccurateBlending = prefs.getBoolean(PREFS_USEACTORBLENDING, getDefaultActorAccurateBlending()); + ShowAnimationFrame = prefs.getInt(PREFS_SHOWANIMFRAME, getDefaultShowAnimationFrame()); InterpolationMap = prefs.getInt(PREFS_INTERPOLATION_MAP, getDefaultInterpolationMap()); InterpolationAnim = prefs.getInt(PREFS_INTERPOLATION_ANIMS, getDefaultInterpolationAnim()); FrameRateOverlays = prefs.getDouble(PREFS_FRAMERATE_OVERLAYS, getDefaultFrameRateOverlays()); @@ -192,6 +211,7 @@ public static void loadSettings(boolean force) ShowAmbientRanges = prefs.getBoolean(PREFS_SHOWAMBIENT, getDefaultAmbientRanges()); SidebarControls = prefs.getInt(PREFS_SIDEBARCONTROLS, getDefaultSidebarControls()); LayerFlags = prefs.getInt(PREFS_LAYERFLAGS, getDefaultLayerFlags()); + ShowActorSprites = prefs.getInt(PREFS_SHOWREALACTORS, getDefaultShowRealActors()); ShowRealAnimations = prefs.getInt(PREFS_SHOWREALANIMS, getDefaultShowRealAnimations()); TimeOfDay = prefs.getInt(PREFS_TIMEOFDAY, getDefaultTimeOfDay()); ZoomFactor = prefs.getDouble(PREFS_ZOOMFACTOR, getDefaultZoomFactor()); @@ -217,7 +237,11 @@ public static void storeSettings(boolean force) prefs.putBoolean(PREFS_EXPORTLAYERS, ExportLayers); prefs.putBoolean(PREFS_MOUSEWHEELZOOM, MouseWheelZoom); prefs.putBoolean(PREFS_OVERRIDEANIMVISIBILITY, OverrideAnimVisibility); - prefs.putInt(PREFS_SHOWFRAME, ShowFrame); + prefs.putInt(PREFS_SHOWACTORFRAME, ShowActorFrame); + prefs.putBoolean(PREFS_SHOWACTORSELECTION, ShowActorSelectionCircle); + prefs.putBoolean(PREFS_SHOWACTORSPACE, ShowActorPersonalSpace); + prefs.putBoolean(PREFS_USEACTORBLENDING, UseActorAccurateBlending); + prefs.putInt(PREFS_SHOWANIMFRAME, ShowAnimationFrame); prefs.putInt(PREFS_INTERPOLATION_MAP, InterpolationMap); prefs.putInt(PREFS_INTERPOLATION_ANIMS, InterpolationAnim); prefs.putDouble(PREFS_FRAMERATE_OVERLAYS, FrameRateOverlays); @@ -248,6 +272,7 @@ public static void storeSettings(boolean force) prefs.putBoolean(PREFS_SHOWAMBIENT, ShowAmbientRanges); prefs.putInt(PREFS_SIDEBARCONTROLS, SidebarControls); prefs.putInt(PREFS_LAYERFLAGS, LayerFlags); + prefs.putInt(PREFS_SHOWREALACTORS, ShowActorSprites); prefs.putInt(PREFS_SHOWREALANIMS, ShowRealAnimations); prefs.putInt(PREFS_TIMEOFDAY, TimeOfDay); prefs.putDouble(PREFS_ZOOMFACTOR, ZoomFactor); @@ -269,6 +294,8 @@ private static void validateSettings() SidebarControls &= (ViewerConstants.SIDEBAR_VISUALSTATE | ViewerConstants.SIDEBAR_LAYERS | ViewerConstants.SIDEBAR_MINIMAPS); + ShowActorSprites = Math.min(Math.max(ShowActorSprites, ViewerConstants.ANIM_SHOW_NONE), + ViewerConstants.ANIM_SHOW_ANIMATED); ShowRealAnimations = Math.min(Math.max(ShowRealAnimations, ViewerConstants.ANIM_SHOW_NONE), ViewerConstants.ANIM_SHOW_ANIMATED); TimeOfDay = Math.min(Math.max(TimeOfDay, ViewerConstants.TIME_0), ViewerConstants.TIME_23); @@ -307,7 +334,7 @@ private static void validateSettings() public static List getDefaultLayerOrder() { - List list = new ArrayList(); + List list = new ArrayList<>(); Collections.addAll(list, DefaultLayerOrder); return list; } @@ -362,12 +389,32 @@ public static boolean getDefaultOverrideAnimVisibility() return false; } + public static boolean getDefaultActorSelectionCircle() + { + return true; + } + + public static boolean getDefaultActorPersonalSpace() + { + return false; + } + + public static boolean getDefaultActorAccurateBlending() + { + return true; + } + public static int getDefaultSidebarControls() { return ViewerConstants.SIDEBAR_VISUALSTATE | ViewerConstants.SIDEBAR_LAYERS | ViewerConstants.SIDEBAR_MINIMAPS; } - public static int getDefaultShowFrame() + public static int getDefaultShowActorFrame() + { + return ViewerConstants.FRAME_AUTO; + } + + public static int getDefaultShowAnimationFrame() { return ViewerConstants.FRAME_AUTO; } @@ -387,6 +434,11 @@ public static int getDefaultLayerFlags() return 0; } + public static int getDefaultShowRealActors() + { + return ViewerConstants.ANIM_SHOW_NONE; + } + public static int getDefaultShowRealAnimations() { return ViewerConstants.ANIM_SHOW_NONE; diff --git a/src/org/infinity/resource/are/viewer/SettingsDialog.java b/src/org/infinity/resource/are/viewer/SettingsDialog.java index 6b79fa7e0..36f3085a8 100644 --- a/src/org/infinity/resource/are/viewer/SettingsDialog.java +++ b/src/org/infinity/resource/are/viewer/SettingsDialog.java @@ -76,9 +76,9 @@ public class SettingsDialog extends JDialog private SimpleListModel modelLayers; private JList listLayers; private JButton bUp, bDown, bDefaultOrder; - private JComboBox cbFrames, cbQualityMap, cbQualityAnim; - private JCheckBox cbOverrideAnimVisibility, cbMouseWheelZoom, cbExportLayers, cbUseColorShades, - cbStoreSettings; + private JComboBox cbActorFrames, cbFrames, cbQualityMap, cbQualityAnim; + private JCheckBox cbShowActorSelectionCircle, cbShowActorPersonalSpace, cbActorAccurateBlending, + cbOverrideAnimVisibility, cbMouseWheelZoom, cbExportLayers, cbUseColorShades, cbStoreSettings; private JButton bDefaultSettings, bCancel, bOK; private JSpinner sOverlaysFps, sAnimationsFps; private JSlider sMiniMapAlpha; @@ -149,7 +149,12 @@ private void updateSettings() Settings.ShowLabelMapNotes = cbLabels[INDEX_LABEL_MAPNOTES].isSelected(); Settings.ShowLabelSpawnPoints = cbLabels[INDEX_LABEL_SPAWNPOINTS].isSelected(); - Settings.ShowFrame = cbFrames.getSelectedIndex(); + Settings.ShowActorFrame = cbActorFrames.getSelectedIndex(); + Settings.ShowActorSelectionCircle = cbShowActorSelectionCircle.isSelected(); + Settings.ShowActorPersonalSpace = cbShowActorPersonalSpace.isSelected(); + Settings.UseActorAccurateBlending = cbActorAccurateBlending.isSelected(); + + Settings.ShowAnimationFrame = cbFrames.getSelectedIndex(); Settings.OverrideAnimVisibility = cbOverrideAnimVisibility.isSelected(); Settings.InterpolationMap = cbQualityMap.getSelectedIndex(); @@ -202,7 +207,12 @@ private void resetDialogSettings() cbLabels[INDEX_LABEL_MAPNOTES].setSelected(Settings.getDefaultLabelMapNotes()); cbLabels[INDEX_LABEL_SPAWNPOINTS].setSelected(Settings.getDefaultLabelSpawnPoints()); - cbFrames.setSelectedIndex(Settings.getDefaultShowFrame()); + cbActorFrames.setSelectedIndex(Settings.getDefaultShowActorFrame()); + cbShowActorSelectionCircle.setSelected(Settings.getDefaultActorSelectionCircle()); + cbShowActorPersonalSpace.setSelected(Settings.getDefaultActorPersonalSpace()); + cbShowActorSelectionCircle.setSelected(Settings.getDefaultActorAccurateBlending()); + + cbFrames.setSelectedIndex(Settings.getDefaultShowAnimationFrame()); cbOverrideAnimVisibility.setSelected(Settings.getDefaultOverrideAnimVisibility()); cbQualityMap.setSelectedIndex(Settings.getDefaultInterpolationMap()); @@ -256,7 +266,7 @@ private void init() settingsChanged = false; // Initializing layer items order - modelLayers = new SimpleListModel(); + modelLayers = new SimpleListModel<>(); listLayers = new JList<>(modelLayers); listLayers.setCellRenderer(new IndexedCellRenderer(1)); listLayers.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); @@ -365,14 +375,43 @@ private void init() pShowLabels.add(cbLabels[idx], c); } + // Actor animation options + JPanel pShowActorFrame = new JPanel(new GridBagLayout()); + pShowActorFrame.setBorder(BorderFactory.createTitledBorder("Actor animations: ")); + JLabel lActorFrames = new JLabel("Show frame:"); + cbActorFrames = new JComboBox<>(AnimationFrames); + cbActorFrames.setSelectedIndex(Settings.ShowActorFrame); + cbShowActorSelectionCircle = new JCheckBox("Draw selection circle", Settings.ShowActorSelectionCircle); + cbShowActorSelectionCircle.setToolTipText("Requires a restart of the area viewer or a map update via toolbar button."); + cbShowActorPersonalSpace = new JCheckBox("Draw personal space indicator", Settings.ShowActorPersonalSpace); + cbShowActorPersonalSpace.setToolTipText("Requires a restart of the area viewer or a map update via toolbar button."); + cbActorAccurateBlending = new JCheckBox("Enable accurate color blending", Settings.UseActorAccurateBlending); + cbActorAccurateBlending.setToolTipText("Creature animations with special blending modes (such as movanic devas or wisps)
    " + + "can reduce overall performance of the area viewer. Disable to improve performance.
    " + + "Requires a restart of the area viewer or a map update via toolbar button."); + c = ViewerUtil.setGBC(c, 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.NONE, new Insets(4, 4, 4, 0), 0, 0); + pShowActorFrame.add(lActorFrames, c); + c = ViewerUtil.setGBC(c, 1, 0, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(4, 8, 0, 4), 0, 0); + pShowActorFrame.add(cbActorFrames, c); + c = ViewerUtil.setGBC(c, 0, 1, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.NONE, new Insets(4, 4, 0, 4), 0, 0); + pShowActorFrame.add(cbShowActorSelectionCircle, c); + c = ViewerUtil.setGBC(c, 0, 2, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.NONE, new Insets(4, 4, 0, 4), 0, 0); + pShowActorFrame.add(cbShowActorPersonalSpace, c); + c = ViewerUtil.setGBC(c, 0, 3, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.NONE, new Insets(4, 4, 4, 4), 0, 0); + pShowActorFrame.add(cbActorAccurateBlending, c); // Background animation frame JPanel pShowFrame = new JPanel(new GridBagLayout()); pShowFrame.setBorder(BorderFactory.createTitledBorder("Background animations: ")); JLabel lFrames = new JLabel("Show frame:"); cbFrames = new JComboBox<>(AnimationFrames); - cbFrames.setSelectedIndex(Settings.ShowFrame); - cbOverrideAnimVisibility = new JCheckBox("Show animations regardless of their active state", Settings.OverrideAnimVisibility); + cbFrames.setSelectedIndex(Settings.ShowAnimationFrame); + cbOverrideAnimVisibility = new JCheckBox("Show background animations regardless of their active state", Settings.OverrideAnimVisibility); cbOverrideAnimVisibility.setToolTipText("Requires a restart of the area viewer or a map update via toolbar button."); c = ViewerUtil.setGBC(c, 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(4, 4, 4, 0), 0, 0); @@ -451,7 +490,7 @@ private void init() JPanel pMiniMap = new JPanel(new GridBagLayout()); pMiniMap.setBorder(BorderFactory.createTitledBorder("Mini map opacity: ")); - Hashtable table = new Hashtable(); + Hashtable table = new Hashtable<>(); for (int i = 0; i <= 100; i+=25) { table.put(Integer.valueOf(i), new JLabel(String.format("%d%%", i))); } @@ -521,28 +560,28 @@ private void init() // putting options together JPanel pCol2 = new JPanel(new GridBagLayout()); -// c = ViewerUtil.setGBC(c, 0, 0, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, -// GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); -// pOptions.add(pShowLabels, c); c = ViewerUtil.setGBC(c, 0, 0, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); - pCol2.add(pShowFrame, c); + pCol2.add(pShowActorFrame, c); c = ViewerUtil.setGBC(c, 0, 1, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(4, 0, 0, 0), 0, 0); - pCol2.add(pQuality, c); + pCol2.add(pShowFrame, c); c = ViewerUtil.setGBC(c, 0, 2, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(4, 0, 0, 0), 0, 0); - pCol2.add(pFrameRates, c); + pCol2.add(pQuality, c); c = ViewerUtil.setGBC(c, 0, 3, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(4, 0, 0, 0), 0, 0); - pCol2.add(pMiniMap, c); + pCol2.add(pFrameRates, c); c = ViewerUtil.setGBC(c, 0, 4, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(4, 0, 0, 0), 0, 0); + pCol2.add(pMiniMap, c); + c = ViewerUtil.setGBC(c, 0, 5, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(4, 0, 0, 0), 0, 0); pCol2.add(pMisc, c); - c = ViewerUtil.setGBC(c, 0, 5, 1, 1, 1.0, 1.0, GridBagConstraints.LINE_START, + c = ViewerUtil.setGBC(c, 0, 6, 1, 1, 1.0, 1.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0); pCol2.add(new JPanel(), c); - c = ViewerUtil.setGBC(c, 0, 6, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, + c = ViewerUtil.setGBC(c, 0, 7, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); pCol2.add(pButtons, c); diff --git a/src/org/infinity/resource/are/viewer/SharedResourceCache.java b/src/org/infinity/resource/are/viewer/SharedResourceCache.java index 2a4e07fa0..49d471bcf 100644 --- a/src/org/infinity/resource/are/viewer/SharedResourceCache.java +++ b/src/org/infinity/resource/are/viewer/SharedResourceCache.java @@ -15,17 +15,26 @@ public class SharedResourceCache { // Identifies the type of cache object to retrieve public static enum Type { - ICON, ANIMATION/*, Creature*/ + ICON, + ANIMATION, + ACTOR } - private static EnumMap> tables = - new EnumMap>(Type.class); + private static EnumMap> tables = new EnumMap<>(Type.class); + static { - tables.put(Type.ICON, new HashMap()); - tables.put(Type.ANIMATION, new HashMap()); -// tables.put(Type.Creature, new HashMap()); + for (final Type type : Type.values()) { + tables.put(type, new HashMap()); + } } + /** Removes all entries from the cache. */ + public static void clearCache() + { + for (final Type type : Type.values()) { + tables.get(type).clear(); + } + } /** * Generates a simple key from the hash code of the specified object. diff --git a/src/org/infinity/resource/are/viewer/TilesetRenderer.java b/src/org/infinity/resource/are/viewer/TilesetRenderer.java index 256358ff6..830ef6aff 100644 --- a/src/org/infinity/resource/are/viewer/TilesetRenderer.java +++ b/src/org/infinity/resource/are/viewer/TilesetRenderer.java @@ -16,19 +16,14 @@ import java.util.List; import java.util.Locale; -import org.infinity.datatype.Bitmap; -import org.infinity.datatype.DecNumber; -import org.infinity.datatype.Flag; -import org.infinity.datatype.HexNumber; +import org.infinity.datatype.IsNumeric; +import org.infinity.datatype.IsTextual; import org.infinity.datatype.ResourceRef; -import org.infinity.datatype.SectionCount; -import org.infinity.datatype.SectionOffset; -import org.infinity.datatype.TextString; import org.infinity.gui.RenderCanvas; import org.infinity.resource.Profile; import org.infinity.resource.ResourceFactory; -import org.infinity.resource.graphics.GraphicsResource; import org.infinity.resource.graphics.ColorConvert; +import org.infinity.resource.graphics.GraphicsResource; import org.infinity.resource.graphics.TisDecoder; import org.infinity.resource.key.ResourceEntry; import org.infinity.resource.wed.Door; @@ -71,11 +66,11 @@ public enum RenderMode { public static final int LightingAdjustmentShift = 10; // use in place of division // keeps track of registered listener objects - private final List listChangeListener = new ArrayList(); + private final List listChangeListener = new ArrayList<>(); // graphics data for all tiles of each overlay - private final List listTilesets = new ArrayList(MaxOverlays); + private final List listTilesets = new ArrayList<>(MaxOverlays); // array of tile indices used for closed door states for each door structure - private final List listDoorTileIndices = new ArrayList(); + private final List listDoorTileIndices = new ArrayList<>(); private final BufferedImage workingTile = ColorConvert.createCompatibleImage(64, 64, true); // internally used for drawing tile graphics private WedResource wed; // current wed resource @@ -180,9 +175,12 @@ public WedResource getWed() /** * Removes the current map and all associated data from memory. */ - public void clear() + public void dispose() { release(true); + if (getImage() instanceof VolatileImage) { + ((VolatileImage)getImage()).flush(); + } } /** @@ -634,9 +632,12 @@ private void release(boolean forceUpdate) if (img != null) { if (forceUpdate) { Graphics2D g = (Graphics2D)img.getGraphics(); - g.setBackground(new Color(0, true)); - g.clearRect(0, 0, img.getWidth(null), img.getHeight(null)); - g.dispose(); + try { + g.setBackground(new Color(0, true)); + g.clearRect(0, 0, img.getWidth(null), img.getHeight(null)); + } finally { + g.dispose(); + } repaint(); } } @@ -658,11 +659,17 @@ private boolean initWed(WedResource wed) { if (wed != null) { // loading overlay structures - SectionOffset so = (SectionOffset)wed.getAttribute(WedResource.WED_OFFSET_OVERLAYS); - SectionCount sc = (SectionCount)wed.getAttribute(WedResource.WED_NUM_OVERLAYS); + IsNumeric so = (IsNumeric)wed.getAttribute(WedResource.WED_OFFSET_OVERLAYS); + IsNumeric sc = (IsNumeric)wed.getAttribute(WedResource.WED_NUM_OVERLAYS); if (so != null && sc != null) { for (int i = 0, count = sc.getValue(), curOfs = so.getValue(); i < count; i++) { Overlay ovl = (Overlay)wed.getAttribute(curOfs, false); + if (i == 0) { + if (Profile.getGame() == Profile.Game.BG1EE || Profile.getGame() == Profile.Game.BG1SoD) { + // updating overlay rendering mode (BG1-style or BG2-style) + blendedOverlays &= (((IsNumeric)ovl.getAttribute(Overlay.WED_OVERLAY_MOVEMENT_TYPE)).getValue() == 2); + } + } if (ovl != null) { listTilesets.add(new Tileset(wed, ovl)); curOfs += ovl.getSize(); @@ -677,22 +684,22 @@ private boolean initWed(WedResource wed) } // loading door structures - so = (SectionOffset)wed.getAttribute(WedResource.WED_OFFSET_DOORS); - sc = (SectionCount)wed.getAttribute(WedResource.WED_NUM_DOORS); - HexNumber lookupOfs = (HexNumber)wed.getAttribute(WedResource.WED_OFFSET_DOOR_TILEMAP_LOOKUP); + so = (IsNumeric)wed.getAttribute(WedResource.WED_OFFSET_DOORS); + sc = (IsNumeric)wed.getAttribute(WedResource.WED_NUM_DOORS); + IsNumeric lookupOfs = (IsNumeric)wed.getAttribute(WedResource.WED_OFFSET_DOOR_TILEMAP_LOOKUP); if (so != null && sc != null && lookupOfs != null) { for (int i = 0, count = sc.getValue(), curOfs = so.getValue(); i < count; i++) { Door door = (Door)wed.getAttribute(curOfs, false); if (door != null) { - String name = ((TextString)door.getAttribute(Door.WED_DOOR_NAME)).toString(); - boolean isClosed = ((Bitmap)door.getAttribute(Door.WED_DOOR_IS_DOOR)).getValue() == 1; + String name = ((IsTextual)door.getAttribute(Door.WED_DOOR_NAME)).getText(); + boolean isClosed = ((IsNumeric)door.getAttribute(Door.WED_DOOR_IS_DOOR)).getValue() == 1; final int tileSize = 2; - int tileIdx = ((DecNumber)door.getAttribute(Door.WED_DOOR_TILEMAP_LOOKUP_INDEX)).getValue(); - int tileCount = ((SectionCount)door.getAttribute(Door.WED_DOOR_NUM_TILEMAP_INDICES)).getValue(); + int tileIdx = ((IsNumeric)door.getAttribute(Door.WED_DOOR_TILEMAP_LOOKUP_INDEX)).getValue(); + int tileCount = ((IsNumeric)door.getAttribute(Door.WED_DOOR_NUM_TILEMAP_INDICES)).getValue(); if (tileCount < 0) tileCount = 0; int[] indices = new int[tileCount]; for (int j = 0; j < tileCount; j++) { - indices[j] = ((DecNumber)door.getAttribute(lookupOfs.getValue() + (tileIdx+j)*tileSize, false)).getValue(); + indices[j] = ((IsNumeric)door.getAttribute(lookupOfs.getValue() + (tileIdx+j)*tileSize, false)).getValue(); } listDoorTileIndices.add(new DoorInfo(name, isClosed, indices)); curOfs += door.getSize(); @@ -940,7 +947,11 @@ private synchronized void drawTile(Tile tile, boolean isDoorTile) int[] srcSec = null; tileIdx = tile.getSecondaryIndex(); if (tileIdx >= 0) { - srcSec = listTilesets.get(0).listTileData.get(tileIdx); + if (tileIdx < listTilesets.get(0).listTileData.size()) { + srcSec = listTilesets.get(0).listTileData.get(tileIdx); + } else { + System.err.println("Invalid tile index: " + tileIdx + " of " + listTilesets.get(0).listTileData.size()); + } } // determining correct rendering mode @@ -1073,11 +1084,11 @@ private void notifyChangeListeners() private static class Tileset { // graphics data for all tiles of this overlay (as int arrays of 64*64 pixels) - public final List listTileData = new ArrayList(); + public final List listTileData = new ArrayList<>(); // info structures for all tiles of this overlay - public final List listTiles = new ArrayList(); + public final List listTiles = new ArrayList<>(); // lists references to all tiles containing overlays from listTiles - public final List listOverlayTiles = new ArrayList(); + 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 @@ -1130,10 +1141,10 @@ private void init(WedResource wed, Overlay ovl) } // storing tile information - tilesX = ((DecNumber)ovl.getAttribute(Overlay.WED_OVERLAY_WIDTH)).getValue(); - tilesY = ((DecNumber)ovl.getAttribute(Overlay.WED_OVERLAY_HEIGHT)).getValue(); - int mapOfs = ((SectionOffset)ovl.getAttribute(Overlay.WED_OVERLAY_OFFSET_TILEMAP)).getValue(); - int idxOfs = ((DecNumber)ovl.getAttribute(Overlay.WED_OVERLAY_OFFSET_TILEMAP_LOOKUP)).getValue(); + tilesX = ((IsNumeric)ovl.getAttribute(Overlay.WED_OVERLAY_WIDTH)).getValue(); + tilesY = ((IsNumeric)ovl.getAttribute(Overlay.WED_OVERLAY_HEIGHT)).getValue(); + int mapOfs = ((IsNumeric)ovl.getAttribute(Overlay.WED_OVERLAY_OFFSET_TILEMAP)).getValue(); + int idxOfs = ((IsNumeric)ovl.getAttribute(Overlay.WED_OVERLAY_OFFSET_TILEMAP_LOOKUP)).getValue(); int tileCount = tilesX * tilesY; for (int i = 0, curOfs = mapOfs; i < tileCount; i++) { Tilemap tile = (Tilemap)ovl.getAttribute(curOfs, false); @@ -1144,13 +1155,13 @@ private void init(WedResource wed, Overlay ovl) if (tile != null) { // initializing list of primary tile indices final int idxSize = 2; - int index = ((DecNumber)tile.getAttribute(Tilemap.WED_TILEMAP_TILE_INDEX_PRI)).getValue(); - int count = ((DecNumber)tile.getAttribute(Tilemap.WED_TILEMAP_TILE_COUNT_PRI)).getValue(); + int index = ((IsNumeric)tile.getAttribute(Tilemap.WED_TILEMAP_TILE_INDEX_PRI)).getValue(); + int count = ((IsNumeric)tile.getAttribute(Tilemap.WED_TILEMAP_TILE_COUNT_PRI)).getValue(); if (count < 0) count = 0; int[] tileIdx = new int[count]; for (int j = 0; j < count; j++) { if (index >= 0) { - DecNumber dn = (DecNumber)ovl.getAttribute(idxOfs + (index+j)*idxSize, false); + IsNumeric dn = (IsNumeric)ovl.getAttribute(idxOfs + (index+j)*idxSize, false); if (dn != null) { tileIdx[j] = dn.getValue(); } else { @@ -1162,11 +1173,11 @@ private void init(WedResource wed, Overlay ovl) } // initializing secondary tile index - int tileIdx2 = ((DecNumber)tile.getAttribute(Tilemap.WED_TILEMAP_TILE_INDEX_SEC)).getValue(); + int tileIdx2 = ((IsNumeric)tile.getAttribute(Tilemap.WED_TILEMAP_TILE_INDEX_SEC)).getValue(); // initializing overlay flags - Flag drawOverlays = (Flag)tile.getAttribute(Tilemap.WED_TILEMAP_DRAW_OVERLAYS); - int flags = (int)drawOverlays.getValue() & 255; + IsNumeric drawOverlays = (IsNumeric)tile.getAttribute(Tilemap.WED_TILEMAP_DRAW_OVERLAYS); + int flags = drawOverlays.getValue() & 255; listTiles.add(new Tile(x, y, count, tileIdx, tileIdx2, flags, isTisPalette)); curOfs += tile.getSize(); diff --git a/src/org/infinity/resource/are/viewer/ViewerConstants.java b/src/org/infinity/resource/are/viewer/ViewerConstants.java index 3222fb988..34d4bde13 100644 --- a/src/org/infinity/resource/are/viewer/ViewerConstants.java +++ b/src/org/infinity/resource/are/viewer/ViewerConstants.java @@ -57,11 +57,11 @@ public static enum LayerStackingType {ACTOR, REGION, ENTRANCE, CONTAINER, AMBIEN public static final Object TYPE_BILINEAR = RenderingHints.VALUE_INTERPOLATION_BILINEAR; public static final Object TYPE_BICUBIC = RenderingHints.VALUE_INTERPOLATION_BICUBIC; - // Specifies the item type for animation objects (LayerObjectAnimation) - public static final int ANIM_ITEM_ICON = 0; - public static final int ANIM_ITEM_REAL = 1; + // Specifies the item type for objects with icons and real animations + public static final int ITEM_ICON = 0; + public static final int ITEM_REAL = 1; - // Different states of showing background animations (AreaViewer) + // Different states of showing real animations (AreaViewer) public static final int ANIM_SHOW_NONE = 0; public static final int ANIM_SHOW_STILL = 1; public static final int ANIM_SHOW_ANIMATED = 2; diff --git a/src/org/infinity/resource/are/viewer/icon/ViewerIcons.java b/src/org/infinity/resource/are/viewer/icon/ViewerIcons.java index 4cc822e6f..392bd1115 100644 --- a/src/org/infinity/resource/are/viewer/icon/ViewerIcons.java +++ b/src/org/infinity/resource/are/viewer/icon/ViewerIcons.java @@ -61,6 +61,12 @@ public class ViewerIcons extends Icons public static final String ICON_ITM_AUTOMAP_2 = "itm_Automap2.png"; public static final String ICON_ITM_ENTRANCE_1 = "itm_Entrance1.png"; public static final String ICON_ITM_ENTRANCE_2 = "itm_Entrance2.png"; + public static final String ICON_ITM_GAM_ACTOR_B_1 = "itm_GamActorB1.png"; + public static final String ICON_ITM_GAM_ACTOR_B_2 = "itm_GamActorB2.png"; + public static final String ICON_ITM_GAM_ACTOR_G_1 = "itm_GamActorG1.png"; + public static final String ICON_ITM_GAM_ACTOR_G_2 = "itm_GamActorG2.png"; + public static final String ICON_ITM_GAM_ACTOR_R_1 = "itm_GamActorR1.png"; + public static final String ICON_ITM_GAM_ACTOR_R_2 = "itm_GamActorR2.png"; public static final String ICON_ITM_INI_ACTOR_B_1 = "itm_IniActorB1.png"; public static final String ICON_ITM_INI_ACTOR_B_2 = "itm_IniActorB2.png"; public static final String ICON_ITM_INI_ACTOR_G_1 = "itm_IniActorG1.png"; diff --git a/src/org/infinity/resource/are/viewer/icon/itm_GamActorB1.png b/src/org/infinity/resource/are/viewer/icon/itm_GamActorB1.png new file mode 100644 index 000000000..89aa122d8 Binary files /dev/null and b/src/org/infinity/resource/are/viewer/icon/itm_GamActorB1.png differ diff --git a/src/org/infinity/resource/are/viewer/icon/itm_GamActorB2.png b/src/org/infinity/resource/are/viewer/icon/itm_GamActorB2.png new file mode 100644 index 000000000..e42ac90d2 Binary files /dev/null and b/src/org/infinity/resource/are/viewer/icon/itm_GamActorB2.png differ diff --git a/src/org/infinity/resource/are/viewer/icon/itm_GamActorG1.png b/src/org/infinity/resource/are/viewer/icon/itm_GamActorG1.png new file mode 100644 index 000000000..cff873e71 Binary files /dev/null and b/src/org/infinity/resource/are/viewer/icon/itm_GamActorG1.png differ diff --git a/src/org/infinity/resource/are/viewer/icon/itm_GamActorG2.png b/src/org/infinity/resource/are/viewer/icon/itm_GamActorG2.png new file mode 100644 index 000000000..2dd9ced6e Binary files /dev/null and b/src/org/infinity/resource/are/viewer/icon/itm_GamActorG2.png differ diff --git a/src/org/infinity/resource/are/viewer/icon/itm_GamActorR1.png b/src/org/infinity/resource/are/viewer/icon/itm_GamActorR1.png new file mode 100644 index 000000000..c99e477b0 Binary files /dev/null and b/src/org/infinity/resource/are/viewer/icon/itm_GamActorR1.png differ diff --git a/src/org/infinity/resource/are/viewer/icon/itm_GamActorR2.png b/src/org/infinity/resource/are/viewer/icon/itm_GamActorR2.png new file mode 100644 index 000000000..eb6d604da Binary files /dev/null and b/src/org/infinity/resource/are/viewer/icon/itm_GamActorR2.png differ diff --git a/src/org/infinity/resource/bcs/BafResource.java b/src/org/infinity/resource/bcs/BafResource.java index 7c3e4b39f..649cf0e9d 100644 --- a/src/org/infinity/resource/bcs/BafResource.java +++ b/src/org/infinity/resource/bcs/BafResource.java @@ -186,7 +186,7 @@ public void itemStateChanged(ItemEvent event) java.util.List files = ResourceFactory.getResources("BAF"); new TextResourceSearcher(files, panel.getTopLevelAncestor()); } else if (bpmFind.getSelectedItem() == ifindthis) { - java.util.List files = new ArrayList(1); + java.util.List files = new ArrayList<>(1); files.add(entry); new TextResourceSearcher(files, panel.getTopLevelAncestor()); } diff --git a/src/org/infinity/resource/bcs/BcsResource.java b/src/org/infinity/resource/bcs/BcsResource.java index 5a7c340d6..5e91444a5 100644 --- a/src/org/infinity/resource/bcs/BcsResource.java +++ b/src/org/infinity/resource/bcs/BcsResource.java @@ -60,8 +60,8 @@ import org.infinity.search.ScriptReferenceSearcher; import org.infinity.search.TextResourceSearcher; import org.infinity.util.IdsMap; -import org.infinity.util.StaticSimpleXorDecryptor; import org.infinity.util.Misc; +import org.infinity.util.StaticSimpleXorDecryptor; import org.infinity.util.io.StreamUtils; /** @@ -487,7 +487,7 @@ public void itemStateChanged(ItemEvent event) files.addAll(ResourceFactory.getResources("BS")); new TextResourceSearcher(files, panel.getTopLevelAncestor()); } else if (bpmFind.getSelectedItem() == ifindthis) { - List files = new ArrayList(1); + List files = new ArrayList<>(1); files.add(entry); new TextResourceSearcher(files, panel.getTopLevelAncestor()); } else if (bpmFind.getSelectedItem() == ifindusage) { diff --git a/src/org/infinity/resource/bcs/ScriptInfo.java b/src/org/infinity/resource/bcs/ScriptInfo.java index fa289bf07..1de4165f2 100644 --- a/src/org/infinity/resource/bcs/ScriptInfo.java +++ b/src/org/infinity/resource/bcs/ScriptInfo.java @@ -511,7 +511,7 @@ protected ScriptInfo(ScriptInfo obj, String[] objectSpecifierIds) for (final Map.Entry> entry : obj.FUNCTION_SIGNATURES.entrySet()) { List oldList = entry.getValue(); if (oldList != null) { - this.FUNCTION_SIGNATURES.put(entry.getKey(), new ArrayList(oldList)); + this.FUNCTION_SIGNATURES.put(entry.getKey(), new ArrayList<>(oldList)); } } this.FUNCTION_PARAM_COMMENT.putAll(obj.FUNCTION_PARAM_COMMENT); diff --git a/src/org/infinity/resource/bcs/Signatures.java b/src/org/infinity/resource/bcs/Signatures.java index c03c3ec8f..67b46d86f 100644 --- a/src/org/infinity/resource/bcs/Signatures.java +++ b/src/org/infinity/resource/bcs/Signatures.java @@ -328,6 +328,17 @@ public int countStrings() return count; } + @Override + public int hashCode() + { + int hash = 7; + hash = 31 * hash + id; + hash = 31 * hash + ((name == null) ? 0 : name.hashCode()); + hash = 31 * hash + ((param == null) ? 0 : param.hashCode()); + hash = 31 * hash + ((type == null) ? 0 : type.hashCode()); + return hash; + } + @Override public boolean equals(Object obj) { @@ -595,6 +606,19 @@ private void setColonSeparatedString(boolean b) colonSeparated = b; } + @Override + public int hashCode() + { + int hash = 7; + hash = 31 * hash + type; + hash = 31 * hash + ((name == null) ? 0 : name.hashCode()); + hash = 31 * hash + ((idsRef == null) ? 0 : idsRef.hashCode()); + hash = 31 * hash + ((resType == null) ? 0 : resType.hashCode()); + hash = 31 * hash + Boolean.hashCode(combinedString); + hash = 31 * hash + Boolean.hashCode(colonSeparated); + return hash; + } + @Override public boolean equals(Object obj) { diff --git a/src/org/infinity/resource/bcs/parser/BafNodeTransformer.java b/src/org/infinity/resource/bcs/parser/BafNodeTransformer.java index ae4e74a32..ec61117f9 100644 --- a/src/org/infinity/resource/bcs/parser/BafNodeTransformer.java +++ b/src/org/infinity/resource/bcs/parser/BafNodeTransformer.java @@ -326,7 +326,7 @@ private void processPARAM(ScriptNode funcNode, Signatures.Function function, Baf } } - private void processParamString(ScriptNode funcNode, Signatures.Function.Parameter param, BafNode baf) throws ParseException + private void processParamString(ScriptNode funcNode, Signatures.Function.Parameter param, BafNode baf) { if (baf.getId() != BafParser.JJTPARAM_STR) { errors.add(new ScriptMessage("Invalid string: " + baf.jjtGetFirstToken(), baf.jjtGetFirstToken(), baf.jjtGetLastToken())); @@ -337,7 +337,7 @@ private void processParamString(ScriptNode funcNode, Signatures.Function.Paramet funcNode.strings.push(s); } - private String processString(BafNode baf) throws ParseException + private String processString(BafNode baf) { if (baf.getId() != BafParser.JJTSTRING) { errors.add(new ScriptMessage("Invalid string parameter: " + baf.jjtGetFirstToken(), @@ -381,7 +381,7 @@ private String processString(BafNode baf) throws ParseException return s; } - private void processParamNumeric(ScriptNode funcNode, Signatures.Function.Parameter param, BafNode baf) throws ParseException + private void processParamNumeric(ScriptNode funcNode, Signatures.Function.Parameter param, BafNode baf) { if (baf.getId() != BafParser.JJTPARAM_NUM && baf.getId() != BafParser.JJTPARAM_SYM && @@ -410,7 +410,7 @@ private void processParamNumeric(ScriptNode funcNode, Signatures.Function.Parame funcNode.numbers.push(Long.valueOf(value)); } - private long processNumberExpression(String ids, BafNode baf) throws ParseException + private long processNumberExpression(String ids, BafNode baf) { if (baf.getId() != BafParser.JJTSINT && baf.getId() != BafParser.JJTUINT && @@ -484,7 +484,7 @@ private long processNumberExpression(String ids, BafNode baf) throws ParseExcept } // Processes numeric or symbolic OR expressions - private long processOrExpression(Signatures.Function.Parameter param, BafNode baf) throws ParseException + private long processOrExpression(Signatures.Function.Parameter param, BafNode baf) { if (baf.getId() != BafParser.JJTOR_EXPR) { errors.add(new ScriptMessage("Invalid numeric or symbolic expression: " + baf.jjtGetFirstToken(), @@ -502,7 +502,7 @@ private long processOrExpression(Signatures.Function.Parameter param, BafNode ba } } - private void processParamPoint(ScriptNode funcNode, Signatures.Function.Parameter param, BafNode baf) throws ParseException + private void processParamPoint(ScriptNode funcNode, Signatures.Function.Parameter param, BafNode baf) { if (baf.getId() != BafParser.JJTPARAM_TGT) { errors.add(new ScriptMessage("Invalid point: " + baf.jjtGetFirstToken(), @@ -589,7 +589,7 @@ private void processObject(ScriptNode objNode, BafNode baf) throws ParseExceptio } } - private void processIdsTarget(ScriptNode objNode, BafNode baf) throws ParseException + private void processIdsTarget(ScriptNode objNode, BafNode baf) { if (baf.getId() != BafParser.JJTTARGET) { errors.add(new ScriptMessage("Invalid IDS target: " + baf.jjtGetFirstToken(), @@ -669,7 +669,7 @@ private void processObjectIdentifierTRAC(ScriptNode objNode, BafNode baf) throws } } - private void processObjectName(ScriptNode objNode, BafNode baf) throws ParseException + private void processObjectName(ScriptNode objNode, BafNode baf) { if (baf.getId() != BafParser.JJTNAME) { errors.add(new ScriptMessage("Invalid object name: " + baf.jjtGetFirstToken(), @@ -688,7 +688,7 @@ private void processObjectName(ScriptNode objNode, BafNode baf) throws ParseExce } } - private void processRectangle(ScriptNode objNode, BafNode baf) throws ParseException + private void processRectangle(ScriptNode objNode, BafNode baf) { if (baf.getId() != BafParser.JJTRECT) { errors.add(new ScriptMessage("Invalid region: " + baf.jjtGetFirstToken(), diff --git a/src/org/infinity/resource/chu/ChuResource.java b/src/org/infinity/resource/chu/ChuResource.java index f65b74599..6b4d937c2 100644 --- a/src/org/infinity/resource/chu/ChuResource.java +++ b/src/org/infinity/resource/chu/ChuResource.java @@ -27,7 +27,7 @@ import org.infinity.resource.graphics.BamResource; import org.infinity.resource.graphics.MosResource; import org.infinity.resource.key.ResourceEntry; -import org.infinity.util.Pair; +import org.infinity.util.tuples.Couple; /** * This resource describes the layout of the GUI screens (the graphics for the @@ -43,7 +43,7 @@ public final class ChuResource extends AbstractStruct implements Resource, HasVi public static final String CHU_OFFSET_CONTROLS = "Controls offset"; public static final String CHU_OFFSET_PANELS = "Panels offset"; - private List> listControls; + private List> listControls; private int ofsPanels, numPanels, sizePanels, ofsControls, numControls; private Viewer detailViewer; private StructHexViewer hexViewer; @@ -163,7 +163,7 @@ public int getControlCount() public int getControlOffset(int index) { if (index >= 0 && index < listControls.size()) { - return listControls.get(index).getFirst(); + return listControls.get(index).getValue0(); } else { return 0; } @@ -173,7 +173,7 @@ public int getControlOffset(int index) public int getControlSize(int index) { if (index >= 0 && index < listControls.size()) { - return listControls.get(index).getSecond(); + return listControls.get(index).getValue1(); } else { return 0; } @@ -303,11 +303,11 @@ private void initData(ByteBuffer buffer, int offset) for (int i = 0; i < numControls; i++, curOfs += 8) { ofs = buffer.getInt(curOfs); len = buffer.getInt(curOfs + 4); - listControls.add(new Pair<>(Integer.valueOf(ofs), Integer.valueOf(len))); + listControls.add(Couple.with(Integer.valueOf(ofs), Integer.valueOf(len))); } // adding virtual entry for determining the true size of the last control entry ofs = Math.max(ofs + len, buffer.limit()); - listControls.add(new Pair<>(Integer.valueOf(ofs), Integer.valueOf(0))); + listControls.add(Couple.with(Integer.valueOf(ofs), Integer.valueOf(0))); } } diff --git a/src/org/infinity/resource/chu/Control.java b/src/org/infinity/resource/chu/Control.java index d301f6746..84411187f 100644 --- a/src/org/infinity/resource/chu/Control.java +++ b/src/org/infinity/resource/chu/Control.java @@ -16,6 +16,7 @@ import org.infinity.datatype.DecNumber; import org.infinity.datatype.Flag; import org.infinity.datatype.HexNumber; +import org.infinity.datatype.IsNumeric; import org.infinity.datatype.ResourceRef; import org.infinity.datatype.StringRef; import org.infinity.datatype.TextString; @@ -113,16 +114,13 @@ final class Control extends AbstractStruct // implements AddRemovable this.size = size; } - // @Override public void write(OutputStream os) throws IOException { getFields().get(0).write(os); getFields().get(1).write(os); } - // - // @Override public int read(ByteBuffer buffer, int offset) { @@ -130,38 +128,37 @@ public int read(ByteBuffer buffer, int offset) addField(new HexNumber(buffer, offset + 4, 4, CHU_CONTROL_LENGTH)); return offset + 8; } - // /** Returns the control id. */ public int getControlId() { - return ((DecNumber)getAttribute(CHU_CONTROL_ID)).getValue(); + return ((IsNumeric)getAttribute(CHU_CONTROL_ID)).getValue(); } /** Returns the x and y position of the control. */ public Point getControlPosition() { - return new Point(((DecNumber)getAttribute(CHU_CONTROL_POSITION_X)).getValue(), - ((DecNumber)getAttribute(CHU_CONTROL_POSITION_Y)).getValue()); + return new Point(((IsNumeric)getAttribute(CHU_CONTROL_POSITION_X)).getValue(), + ((IsNumeric)getAttribute(CHU_CONTROL_POSITION_Y)).getValue()); } /** Returns the width and height of the control. */ public Dimension getControlDimensions() { - return new Dimension(((DecNumber)getAttribute(CHU_CONTROL_WIDTH)).getValue(), - ((DecNumber)getAttribute(CHU_CONTROL_HEIGHT)).getValue()); + return new Dimension(((IsNumeric)getAttribute(CHU_CONTROL_WIDTH)).getValue(), + ((IsNumeric)getAttribute(CHU_CONTROL_HEIGHT)).getValue()); } /** Returns the control type. */ public int getControlType() { - return ((Bitmap)getAttribute(CHU_CONTROL_TYPE)).getValue(); + return ((IsNumeric)getAttribute(CHU_CONTROL_TYPE)).getValue(); } public int readControl(ByteBuffer buffer) { - int offset = ((HexNumber)getAttribute(CHU_CONTROL_OFFSET)).getValue(); + int offset = ((IsNumeric)getAttribute(CHU_CONTROL_OFFSET)).getValue(); final int endOffset = offset + size; addField(new DecNumber(buffer, offset, 2, CHU_CONTROL_ID)); addField(new DecNumber(buffer, offset + 2, 2, CHU_CONTROL_BUFFER_LENGTH)); @@ -264,7 +261,7 @@ public int readControl(ByteBuffer buffer) offset += 40; break; default: - HexNumber len = (HexNumber)getAttribute(CHU_CONTROL_LENGTH); + IsNumeric len = (IsNumeric)getAttribute(CHU_CONTROL_LENGTH); addField(new Unknown(buffer, offset + 14, len.getValue() - 14)); offset += len.getValue(); break; diff --git a/src/org/infinity/resource/chu/Viewer.java b/src/org/infinity/resource/chu/Viewer.java index 12339cdbe..7c3c44bb5 100644 --- a/src/org/infinity/resource/chu/Viewer.java +++ b/src/org/infinity/resource/chu/Viewer.java @@ -44,24 +44,21 @@ import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; -import org.infinity.datatype.Bitmap; -import org.infinity.datatype.ColorPicker; -import org.infinity.datatype.DecNumber; import org.infinity.datatype.Flag; -import org.infinity.datatype.ResourceRef; -import org.infinity.datatype.StringRef; -import org.infinity.datatype.TextString; +import org.infinity.datatype.IsNumeric; +import org.infinity.datatype.IsReference; +import org.infinity.datatype.IsTextual; import org.infinity.gui.RenderCanvas; import org.infinity.gui.StructViewer; import org.infinity.gui.ViewerUtil; import org.infinity.resource.Profile; import org.infinity.resource.ResourceFactory; import org.infinity.resource.graphics.BamDecoder; -import org.infinity.resource.graphics.MosDecoder; -import org.infinity.resource.graphics.MosV1Decoder; import org.infinity.resource.graphics.BamDecoder.BamControl; import org.infinity.resource.graphics.BamDecoder.FrameEntry; import org.infinity.resource.graphics.BamV1Decoder.BamV1Control; +import org.infinity.resource.graphics.MosDecoder; +import org.infinity.resource.graphics.MosV1Decoder; import org.infinity.util.StringTable; @@ -185,7 +182,7 @@ public void valueChanged(ListSelectionEvent e) setPreview(p.getImage()); } } else if (e.getSource() == controlsList) { - BaseControl c = (BaseControl)controlsList.getSelectedValue(); + BaseControl c = controlsList.getSelectedValue(); if (c != null) { // updating properties panel getProperties().updateProperties(c); @@ -227,7 +224,7 @@ public void mouseClicked(MouseEvent e) { if (e.getSource() == panelsList) { if (e.getClickCount() == 2 && !e.isConsumed()) { - Panel p = (Panel)panelsList.getSelectedValue(); + Panel p = panelsList.getSelectedValue(); if (p != null) { StructViewer v = getResource().getViewer(); if (v != null) { @@ -237,9 +234,9 @@ public void mouseClicked(MouseEvent e) } } else if (e.getSource() == controlsList) { if (e.getClickCount() == 2 && !e.isConsumed()) { - Panel p = (Panel)panelsList.getSelectedValue(); + Panel p = panelsList.getSelectedValue(); if (p != null) { - BaseControl c = (BaseControl)controlsList.getSelectedValue(); + BaseControl c = controlsList.getSelectedValue(); if (c != null && !c.isEmpty()) { StructViewer v = getResource().getViewer(); if (v != null) { @@ -429,7 +426,7 @@ private void initControls() // Data model for the panels list private class ListPanelsModel extends AbstractListModel { - private final List listPanels = new ArrayList(); + private final List listPanels = new ArrayList<>(); private final Viewer viewer; public ListPanelsModel(Viewer viewer) @@ -477,7 +474,7 @@ public ChuResource getResource() // Data model for the controls list private class ListControlsModel extends AbstractListModel { - private final List listControls = new ArrayList(); + private final List listControls = new ArrayList<>(); private final Viewer viewer; private Window panel; @@ -577,7 +574,7 @@ private static class PropertiesPanel extends JPanel implements ActionListener private static final String FMT_POSITION = "X: %d, Y: %d"; private static final String FMT_SIZE = "W: %d, H: %d"; - private final List listeners = new ArrayList(); + private final List listeners = new ArrayList<>(); private final JRadioButton[] rbButtonState = new JRadioButton[4]; private JLabel lPosition, lSize; @@ -914,7 +911,7 @@ public void reset() // recreating controls int numControls = getViewer().getControls().getSize(); for (int i = 0; i < numControls; i++) { - BaseControl control = (BaseControl)getViewer().getControls().getElementAt(i); + BaseControl control = getViewer().getControls().getElementAt(i); if (control != null) { control.updateImage(); } @@ -988,7 +985,7 @@ private void updateImage() // 2. drawing control elements onto the panel int numControls = getViewer().getControls().getSize(); for (int i = 0; i < numControls; i++) { - BaseControl control = (BaseControl)getViewer().getControls().getElementAt(i); + BaseControl control = getViewer().getControls().getElementAt(i); if (control != null) { Image ctrlImage = control.getImage(); if (ctrlImage != null) { @@ -1347,7 +1344,7 @@ private static class ButtonControl extends BaseControl private static final int SELECTED = 2; private static final int DISABLED = 3; - private static final HashSet ignoreResourceSet = new HashSet(); + private static final HashSet ignoreResourceSet = new HashSet<>(); static { // XXX: ignore a set of known background BAMs with cycle and frame indices @@ -1431,19 +1428,19 @@ public void updateImage() // 2. drawing control if (isVisible()) { // loading BAM - String resName = ((ResourceRef)getResource().getAttribute(Control.CHU_CONTROL_BTN_RESREF)).getResourceName(); + String resName = ((IsReference)getResource().getAttribute(Control.CHU_CONTROL_BTN_RESREF)).getResourceName(); // getting specified cycle index - int cycleIdx = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_BTN_ANIMATION_INDEX)).getValue(); + int cycleIdx = ((IsNumeric)getResource().getAttribute(Control.CHU_CONTROL_BTN_ANIMATION_INDEX)).getValue(); int frameIdx = 0; // getting specified cycle frame index if (isUnpressed()) { - frameIdx = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_BTN_FRAME_INDEX_UNPRESSED)).getValue(); + frameIdx = ((IsNumeric)getResource().getAttribute(Control.CHU_CONTROL_BTN_FRAME_INDEX_UNPRESSED)).getValue(); } else if (isPressed()) { - frameIdx = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_BTN_FRAME_INDEX_PRESSED)).getValue(); + frameIdx = ((IsNumeric)getResource().getAttribute(Control.CHU_CONTROL_BTN_FRAME_INDEX_PRESSED)).getValue(); } else if (isSelected()) { - frameIdx = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_BTN_FRAME_INDEX_SELECTED)).getValue(); + frameIdx = ((IsNumeric)getResource().getAttribute(Control.CHU_CONTROL_BTN_FRAME_INDEX_SELECTED)).getValue(); } else if (isDisabled()) { - frameIdx = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_BTN_FRAME_INDEX_DISABLED)).getValue(); + frameIdx = ((IsNumeric)getResource().getAttribute(Control.CHU_CONTROL_BTN_FRAME_INDEX_DISABLED)).getValue(); } if (!isResourceIgnored(resName, cycleIdx, frameIdx)) { BamDecoder bam = BamDecoder.loadBam(ResourceFactory.getResourceEntry(resName)); @@ -1539,7 +1536,7 @@ public void updateImage() // 2. drawing control // 2.1. drawing background image - String resName = ((ResourceRef)getResource().getAttribute(Control.CHU_CONTROL_SLD_BACKGROUND)).getResourceName(); + String resName = ((IsReference)getResource().getAttribute(Control.CHU_CONTROL_SLD_BACKGROUND)).getResourceName(); MosDecoder mos = MosDecoder.loadMos(ResourceFactory.getResourceEntry(resName)); if (mos != null) { if (mos instanceof MosV1Decoder) { @@ -1550,29 +1547,29 @@ public void updateImage() // 2.2. drawing control elements if (isVisible()) { - resName = ((ResourceRef)getResource().getAttribute(Control.CHU_CONTROL_SLD_KNOB)).getResourceName(); + resName = ((IsReference)getResource().getAttribute(Control.CHU_CONTROL_SLD_KNOB)).getResourceName(); BamDecoder bam = BamDecoder.loadBam(ResourceFactory.getResourceEntry(resName)); if (bam != null) { BamControl bamCtrl = bam.createControl(); bamCtrl.setMode(BamControl.Mode.INDIVIDUAL); // getting specified cycle index - int cycleIdx = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_SLD_ANIMATION_INDEX)).getValue(); + int cycleIdx = ((IsNumeric)getResource().getAttribute(Control.CHU_CONTROL_SLD_ANIMATION_INDEX)).getValue(); cycleIdx = Math.min(bamCtrl.cycleCount()-1, Math.max(0, cycleIdx)); bamCtrl.cycleSet(cycleIdx); int frameIdx; // getting specified cycle frame index if (isGrabbed()) { - frameIdx = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_SLD_FRAME_INDEX_GRABBED)).getValue(); + frameIdx = ((IsNumeric)getResource().getAttribute(Control.CHU_CONTROL_SLD_FRAME_INDEX_GRABBED)).getValue(); } else { - frameIdx = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_SLD_FRAME_INDEX_UNGRABBED)).getValue(); + frameIdx = ((IsNumeric)getResource().getAttribute(Control.CHU_CONTROL_SLD_FRAME_INDEX_UNGRABBED)).getValue(); } frameIdx = Math.min(bamCtrl.cycleFrameCount()-1, Math.max(0, frameIdx)); bamCtrl.cycleSetFrameIndex(frameIdx); // drawing frame Image knob = bamCtrl.cycleGetFrame(); - int knobX = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_SLD_KNOB_POSITION_X)).getValue(); - int knobY = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_SLD_KNOB_POSITION_Y)).getValue(); + int knobX = ((IsNumeric)getResource().getAttribute(Control.CHU_CONTROL_SLD_KNOB_POSITION_X)).getValue(); + int knobY = ((IsNumeric)getResource().getAttribute(Control.CHU_CONTROL_SLD_KNOB_POSITION_Y)).getValue(); g.drawImage(knob, knobX, knobY, knob.getWidth(null), knob.getHeight(null), null); } } @@ -1589,7 +1586,7 @@ public void updateImage() // Manages the visual appearance of text fields private static class TextFieldControl extends BaseControl { - private static final HashSet ignoreResourceSet = new HashSet(); + private static final HashSet ignoreResourceSet = new HashSet<>(); static { // XXX: ignore a set of known background MOS resources @@ -1648,7 +1645,7 @@ public void updateImage() // 2. drawing control // 2.1. drawing background image (is it actually used?) - String resName = ((ResourceRef)getResource().getAttribute(Control.CHU_CONTROL_TF_BACKGROUND_1)).getResourceName(); + String resName = ((IsReference)getResource().getAttribute(Control.CHU_CONTROL_TF_BACKGROUND_1)).getResourceName(); if (!isResourceIgnored(resName)) { MosDecoder mos = MosDecoder.loadMos(ResourceFactory.getResourceEntry(resName)); if (mos != null) { @@ -1661,18 +1658,18 @@ public void updateImage() // 2.2. drawing text if (isVisible()) { - int caretX = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_TF_CARET_POSITION_X)).getValue(); - int caretY = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_TF_CARET_POSITION_Y)).getValue(); + int caretX = ((IsNumeric)getResource().getAttribute(Control.CHU_CONTROL_TF_CARET_POSITION_X)).getValue(); + int caretY = ((IsNumeric)getResource().getAttribute(Control.CHU_CONTROL_TF_CARET_POSITION_Y)).getValue(); - String text = ((TextString)getResource().getAttribute(Control.CHU_CONTROL_TF_TEXT)).toString(); + String text = ((IsTextual)getResource().getAttribute(Control.CHU_CONTROL_TF_TEXT)).getText(); if (!text.isEmpty()) { - resName = ((ResourceRef)getResource().getAttribute(Control.CHU_CONTROL_TF_FONT)).getResourceName(); + resName = ((IsReference)getResource().getAttribute(Control.CHU_CONTROL_TF_FONT)).getResourceName(); resName = resName.toUpperCase(Locale.ENGLISH).replace(".FNT", ".BAM"); BamDecoder bam = BamDecoder.loadBam(ResourceFactory.getResourceEntry(resName)); if (bam != null) { - int maxLen = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_TF_FIELD_LENGTH)).getValue(); + int maxLen = ((IsNumeric)getResource().getAttribute(Control.CHU_CONTROL_TF_FIELD_LENGTH)).getValue(); if (text.length() > maxLen) text = text.substring(0, maxLen); - int flags = ((Bitmap)getResource().getAttribute(Control.CHU_CONTROL_TF_ALLOWED_CSE)).getValue(); + int flags = ((IsNumeric)getResource().getAttribute(Control.CHU_CONTROL_TF_ALLOWED_CSE)).getValue(); text = convertText(text, flags); Image textImage = drawText(text, bam, null, false); if (textImage != null) { @@ -1684,17 +1681,17 @@ public void updateImage() // 2.3. drawing caret if (isCaretEnabled()) { - resName = ((ResourceRef)getResource().getAttribute(Control.CHU_CONTROL_TF_CARET)).getResourceName(); + resName = ((IsReference)getResource().getAttribute(Control.CHU_CONTROL_TF_CARET)).getResourceName(); BamDecoder bam = BamDecoder.loadBam(ResourceFactory.getResourceEntry(resName)); if (bam != null) { BamControl bamCtrl = bam.createControl(); bamCtrl.setMode(BamControl.Mode.INDIVIDUAL); // getting specified cycle index - int cycleIdx = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_TF_ANIMATION_INDEX)).getValue(); + int cycleIdx = ((IsNumeric)getResource().getAttribute(Control.CHU_CONTROL_TF_ANIMATION_INDEX)).getValue(); cycleIdx = Math.min(bamCtrl.cycleCount()-1, Math.max(0, cycleIdx)); bamCtrl.cycleSet(cycleIdx); // getting specified cycle frame index - int frameIdx = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_TF_FRAME_INDEX)).getValue(); + int frameIdx = ((IsNumeric)getResource().getAttribute(Control.CHU_CONTROL_TF_FRAME_INDEX)).getValue(); frameIdx = Math.min(bamCtrl.cycleFrameCount()-1, Math.max(0, frameIdx)); bamCtrl.cycleSetFrameIndex(frameIdx); @@ -1816,16 +1813,16 @@ public void updateImage() // 2. drawing control if (isVisible()) { - String text = StringTable.getStringRef(((StringRef)getResource().getAttribute(Control.CHU_CONTROL_LBL_TEXT)).getValue()); + String text = StringTable.getStringRef(((IsNumeric)getResource().getAttribute(Control.CHU_CONTROL_LBL_TEXT)).getValue()); if (text != null) { - String resName = ((ResourceRef)getResource().getAttribute(Control.CHU_CONTROL_LBL_FONT)).getResourceName(); + String resName = ((IsReference)getResource().getAttribute(Control.CHU_CONTROL_LBL_FONT)).getResourceName(); resName = resName.toUpperCase(Locale.ENGLISH).replace(".FNT", ".BAM"); BamDecoder bam = BamDecoder.loadBam(ResourceFactory.getResourceEntry(resName)); if (bam != null) { Flag flags = (Flag)getResource().getAttribute(Control.CHU_CONTROL_LBL_FLAGS); Color col = null; if (flags.isFlagSet(0)) { - col = new Color(((ColorPicker)getResource().getAttribute(Control.CHU_CONTROL_LBL_COLOR_1)).getValue()); + col = new Color(((IsNumeric)getResource().getAttribute(Control.CHU_CONTROL_LBL_COLOR_1)).getValue()); } Image textImage = drawText(text, bam, col, flags.isFlagSet(1)); if (textImage != null) { @@ -1923,22 +1920,22 @@ public void updateImage() // 2. drawing control if (isVisible()) { - String resName = ((ResourceRef)getResource().getAttribute(Control.CHU_CONTROL_SB_GRAPHICS)).getResourceName(); + String resName = ((IsReference)getResource().getAttribute(Control.CHU_CONTROL_SB_GRAPHICS)).getResourceName(); BamDecoder bam = BamDecoder.loadBam(ResourceFactory.getResourceEntry(resName)); if (bam != null) { - int cycleIdx = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_SB_ANIMATION_INDEX)).getValue(); - int frameTroughIdx = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_SB_FRAME_INDEX_TROUGH)).getValue(); - int frameSliderIdx = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_SB_FRAME_INDEX_SLIDER)).getValue(); + int cycleIdx = ((IsNumeric)getResource().getAttribute(Control.CHU_CONTROL_SB_ANIMATION_INDEX)).getValue(); + int frameTroughIdx = ((IsNumeric)getResource().getAttribute(Control.CHU_CONTROL_SB_FRAME_INDEX_TROUGH)).getValue(); + int frameSliderIdx = ((IsNumeric)getResource().getAttribute(Control.CHU_CONTROL_SB_FRAME_INDEX_SLIDER)).getValue(); int frameUpIdx, frameDownIdx; if (isUpArrowPressed()) { - frameUpIdx = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_SB_FRAME_INDEX_UP_PRESSED)).getValue(); + frameUpIdx = ((IsNumeric)getResource().getAttribute(Control.CHU_CONTROL_SB_FRAME_INDEX_UP_PRESSED)).getValue(); } else { - frameUpIdx = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_SB_FRAME_INDEX_UP_UNPRESSED)).getValue(); + frameUpIdx = ((IsNumeric)getResource().getAttribute(Control.CHU_CONTROL_SB_FRAME_INDEX_UP_UNPRESSED)).getValue(); } if (isDownArrowPressed()) { - frameDownIdx = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_SB_FRAME_INDEX_DOWN_PRESSED)).getValue(); + frameDownIdx = ((IsNumeric)getResource().getAttribute(Control.CHU_CONTROL_SB_FRAME_INDEX_DOWN_PRESSED)).getValue(); } else { - frameDownIdx = ((DecNumber)getResource().getAttribute(Control.CHU_CONTROL_SB_FRAME_INDEX_DOWN_UNPRESSED)).getValue(); + frameDownIdx = ((IsNumeric)getResource().getAttribute(Control.CHU_CONTROL_SB_FRAME_INDEX_DOWN_UNPRESSED)).getValue(); } BamControl ctrl = bam.createControl(); ctrl.cycleSet(cycleIdx); diff --git a/src/org/infinity/resource/chu/Window.java b/src/org/infinity/resource/chu/Window.java index 0e2cfc048..7d566a53e 100644 --- a/src/org/infinity/resource/chu/Window.java +++ b/src/org/infinity/resource/chu/Window.java @@ -14,6 +14,7 @@ import org.infinity.datatype.Bitmap; import org.infinity.datatype.DecNumber; import org.infinity.datatype.Flag; +import org.infinity.datatype.IsNumeric; import org.infinity.datatype.ResourceRef; import org.infinity.datatype.TextString; import org.infinity.datatype.Unknown; @@ -75,7 +76,7 @@ public ChuResource getParent() /** Returns the number of controls associated with this panel. */ public int getControlCount() { - return (int)((UnsignDecNumber)getAttribute(CHU_WINDOW_NUM_CONTROLS)).getValue(); + return ((UnsignDecNumber)getAttribute(CHU_WINDOW_NUM_CONTROLS)).getValue(); } /** Returns the given control. Index is relative to the controls associated with this panel. */ @@ -91,27 +92,27 @@ public Control getControl(int index) /** Returns the panel id. */ public int getWindowId() { - return ((DecNumber)getAttribute(CHU_WINDOW_ID)).getValue(); + return ((IsNumeric)getAttribute(CHU_WINDOW_ID)).getValue(); } /** Returns the x and y positions of the panel. */ public Point getWindowPosition() { - return new Point(((DecNumber)getAttribute(CHU_WINDOW_POSITION_X)).getValue(), - ((DecNumber)getAttribute(CHU_WINDOW_POSITION_Y)).getValue()); + return new Point(((IsNumeric)getAttribute(CHU_WINDOW_POSITION_X)).getValue(), + ((IsNumeric)getAttribute(CHU_WINDOW_POSITION_Y)).getValue()); } /** Returns width and height of the panel. */ public Dimension getWindowDimension() { - return new Dimension(((DecNumber)getAttribute(CHU_WINDOW_WIDTH)).getValue(), - ((DecNumber)getAttribute(CHU_WINDOW_HEIGHT)).getValue()); + return new Dimension(((IsNumeric)getAttribute(CHU_WINDOW_WIDTH)).getValue(), + ((IsNumeric)getAttribute(CHU_WINDOW_HEIGHT)).getValue()); } /** Returns whether the panel references a background MOS. */ public boolean hasBackgroundImage() { - return ((Bitmap)getAttribute(CHU_WINDOW_HAS_BACKGROUND)).getValue() == 1; + return ((IsNumeric)getAttribute(CHU_WINDOW_HAS_BACKGROUND)).getValue() == 1; } /** Returns the background MOS for the panel. */ @@ -122,8 +123,8 @@ public String getBackgroundImage() public int readControls(ByteBuffer buffer) throws Exception { - int numctrl = (int)((UnsignDecNumber)getAttribute(CHU_WINDOW_NUM_CONTROLS)).getValue(); - int first = (int)((UnsignDecNumber)getAttribute(CHU_WINDOW_FIRST_CONTROL_INDEX)).getValue(); + int numctrl = ((UnsignDecNumber)getAttribute(CHU_WINDOW_NUM_CONTROLS)).getValue(); + int first = ((UnsignDecNumber)getAttribute(CHU_WINDOW_FIRST_CONTROL_INDEX)).getValue(); int controlsoffset = getParent().getControlsOffset() + (first*8); int endoffset = controlsoffset; for (int i = 0; i < numctrl; i++) { diff --git a/src/org/infinity/resource/cre/CreResource.java b/src/org/infinity/resource/cre/CreResource.java index fe0478276..18212ad7f 100644 --- a/src/org/infinity/resource/cre/CreResource.java +++ b/src/org/infinity/resource/cre/CreResource.java @@ -17,6 +17,7 @@ import java.util.Locale; import java.util.Map; import java.util.Set; +import java.util.TreeMap; import javax.swing.JButton; import javax.swing.JComponent; @@ -73,7 +74,6 @@ import org.infinity.util.IniMapCache; import org.infinity.util.IniMapEntry; import org.infinity.util.IniMapSection; -import org.infinity.util.LongIntegerHashMap; import org.infinity.util.Misc; import org.infinity.util.StringTable; import org.infinity.util.Table2da; @@ -423,8 +423,14 @@ public final class CreResource extends AbstractStruct public static final String CRE_SELECTED_WEAPON_SLOT = "Weapon slot selected"; public static final String CRE_SELECTED_WEAPON_ABILITY = "Weapon ability selected"; - private static final LongIntegerHashMap m_magetype = new LongIntegerHashMap(); - private static final LongIntegerHashMap m_colorPlacement = new LongIntegerHashMap(); + public static final String TAB_ANIMATION = "Animation"; + + public static final int TAB_INDEX_VIEW = 0; + public static final int TAB_INDEX_ANIMATION = 1; + public static final int TAB_INDEX_RAW = 2; + + private static final TreeMap m_magetype = new TreeMap<>(); + private static final TreeMap m_colorPlacement = new TreeMap<>(); public static final String[] s_flag = { "No flags set", "Identified", "No corpse", "Permanent corpse", "Original class: Fighter", "Original class: Mage", "Original class: Cleric", "Original class: Thief", @@ -433,6 +439,14 @@ public final class CreResource extends AbstractStruct "Holding item", "Reset bit 16", null, null, "EE: No exploding death", null, "EE: Ignore nightmare mode", "EE: No tooltip", "Allegiance tracking", "General tracking", "Race tracking", "Class tracking", "Specifics tracking", "Gender tracking", "Alignment tracking", "Uninterruptible"}; + public static final String[] s_flag_iwd2 = { + "No flags set", "Damage don't stop casting", "No corpse", "Permanent corpse", + null, null, null, null, null, null, "Fallen paladin", "Fallen ranger", + "Export allowed", null, "Quest critical", "Can activate non-NPC triggers", "Enabled", + "Seen party", "Invulnerable", "Non threatening enemy", "No talk", "Ignore return to start", "Ignore inhibit AI", + null, null, "Allegiance tracking", "General tracking", "Race tracking", "Class tracking", + "Specifics tracking", "Gender tracking", "Alignment tracking", "Corpse related?" + }; public static final String[] s_feats1 = { "No feats selected", "Aegis of rime", "Ambidexterity", "Aqua mortis", "Armor proficiency", "Armored arcana", "Arterial strike", "Blind fight", "Bullheaded", "Cleave", "Combat casting", "Courteous magocracy", "Crippling strike", @@ -562,7 +576,7 @@ public static void addScriptName(Map> scriptNames, entries.add(entry); } } else { - Set entries = new HashSet(); + Set entries = new HashSet<>(); entries.add(entry); synchronized (scriptNames) { scriptNames.put(scriptName, entries); @@ -729,7 +743,16 @@ public CreResource(AbstractStruct superStruct, String name, ByteBuffer data, int isChr = StreamUtils.readString(data, startoffset, 4).equalsIgnoreCase("CHR "); } - // + @Override + public void close() throws Exception + { + JComponent c = getViewerTab(TAB_INDEX_ANIMATION); + if (c instanceof ViewerAnimation) { + ((ViewerAnimation)c).close(); + } + super.close(); + } + @Override public AddRemovable[] getPrototypes() throws Exception { @@ -755,28 +778,27 @@ public AddRemovable confirmAddEntry(AddRemovable entry) throws Exception return entry; } - // @Override public boolean canRemove() { return true; } - // - // @Override public int getViewerTabCount() { - return showRawTab() ? 2 : 1; + return showRawTab() ? 3 : 2; } @Override public String getViewerTabName(int index) { switch (index) { - case 0: + case TAB_INDEX_VIEW: return StructViewer.TAB_VIEW; - case 1: + case TAB_INDEX_ANIMATION: + return TAB_ANIMATION; + case TAB_INDEX_RAW: return showRawTab() ? StructViewer.TAB_RAW : null; } return null; @@ -786,9 +808,11 @@ public String getViewerTabName(int index) public JComponent getViewerTab(int index) { switch (index) { - case 0: + case TAB_INDEX_VIEW: return new Viewer(this); - case 1: + case TAB_INDEX_ANIMATION: + return new ViewerAnimation(this); + case TAB_INDEX_RAW: if (showRawTab() && hexViewer == null) { hexViewer = new StructHexViewer(this, new BasicColorMap(this, true)); } @@ -802,7 +826,6 @@ public boolean viewerTabAddedBefore(int index) { return (index == 0); } - // // Needed for embedded CRE resources private boolean showRawTab() @@ -814,15 +837,12 @@ private boolean showRawTab() return hasRawTab.booleanValue(); } - // @Override public void write(OutputStream os) throws IOException { super.writeFlatFields(os); } - // - // @Override protected void viewerInitialized(StructViewer viewer) { @@ -890,9 +910,7 @@ protected void datatypeRemovedInChild(AbstractStruct child, AddRemovable datatyp hexViewer.dataModified(); } } - // - // @Override public int read(ByteBuffer buffer, int offset) throws Exception { @@ -902,16 +920,20 @@ public int read(ByteBuffer buffer, int offset) throws Exception TextString version = new TextString(buffer, offset + 4, 4, COMMON_VERSION); addField(version); if (signature.toString().equalsIgnoreCase("CHR ")) { + IdsBitmap bitmap; + final IdsMapEntry entryNone = new IdsMapEntry(-1L, "NONE"); addField(new TextString(buffer, offset + 8, 32, CHR_NAME)); HexNumber structOffset = new HexNumber(buffer, offset + 40, 4, CHR_OFFSET_CRE); addField(structOffset); addField(new HexNumber(buffer, offset + 44, 4, CHR_CRE_SIZE)); if (version.toString().equalsIgnoreCase("V2.2")) { for (int i = 0; i < 4; i++) { - addField(new IdsBitmap(buffer, offset + 48 + (i * 4), 2, - String.format(CHR_QUICK_WEAPON_SLOT_FMT, i+1), "SLOTS.IDS")); - addField(new IdsBitmap(buffer, offset + 50 + (i * 4), 2, - String.format(CHR_QUICK_SHIELD_SLOT_FMT, i+1), "SLOTS.IDS")); + bitmap = addField(new IdsBitmap(buffer, offset + 48 + (i * 4), 2, + String.format(CHR_QUICK_WEAPON_SLOT_FMT, i+1), "SLOTS.IDS", true, false, true)); + bitmap.addIdsMapEntry(entryNone); + bitmap = addField(new IdsBitmap(buffer, offset + 50 + (i * 4), 2, + String.format(CHR_QUICK_SHIELD_SLOT_FMT, i+1), "SLOTS.IDS", true, false, true)); + bitmap.addIdsMapEntry(entryNone); } for (int i = 0; i < 4; i++) { addField(new DecNumber(buffer, offset + 64 + (i * 4), 2, @@ -929,8 +951,9 @@ public int read(ByteBuffer buffer, int offset) throws Exception } addField(new Unknown(buffer, offset + 161, 1)); for (int i = 0; i < 3; i++) { - addField(new IdsBitmap(buffer, offset + 162 + (i * 2), 2, - String.format(CHR_QUICK_ITEM_SLOT_FMT, i+1), "SLOTS.IDS")); + bitmap = addField(new IdsBitmap(buffer, offset + 162 + (i * 2), 2, + String.format(CHR_QUICK_ITEM_SLOT_FMT, i+1), "SLOTS.IDS", true, false, true)); + bitmap.addIdsMapEntry(entryNone); } for (int i = 0; i < 3; i++) { addField(new DecNumber(buffer, offset + 168 + (i * 2), 2, @@ -957,8 +980,9 @@ else if (version.toString().equalsIgnoreCase("V1.0") || version.toString().equalsIgnoreCase("V2.0") || version.toString().equalsIgnoreCase("V2.1")) { for (int i = 0; i < 4; i++) { - addField(new IdsBitmap(buffer, offset + 48 + (i * 2), 2, - String.format(CHR_QUICK_WEAPON_SLOT_FMT, i+1), "SLOTS.IDS")); + bitmap = addField(new IdsBitmap(buffer, offset + 48 + (i * 2), 2, + String.format(CHR_QUICK_WEAPON_SLOT_FMT, i+1), "SLOTS.IDS", true, false, true)); + bitmap.addIdsMapEntry(entryNone); } for (int i = 0; i < 4; i++) { addField(new DecNumber(buffer, offset + 56 + (i * 2), 2, @@ -969,8 +993,9 @@ else if (version.toString().equalsIgnoreCase("V1.0") || String.format(CHR_QUICK_SPELL_FMT, i+1), "SPL")); } for (int i = 0; i < 3; i++) { - addField(new IdsBitmap(buffer, offset + 88 + (i * 2), 2, - String.format(CHR_QUICK_ITEM_SLOT_FMT, i+1), "SLOTS.IDS")); + bitmap = addField(new IdsBitmap(buffer, offset + 88 + (i * 2), 2, + String.format(CHR_QUICK_ITEM_SLOT_FMT, i+1), "SLOTS.IDS", true, false, true)); + bitmap.addIdsMapEntry(entryNone); } for (int i = 0; i < 3; i++) { addField(new DecNumber(buffer, offset + 94 + (i * 2), 2, @@ -992,7 +1017,6 @@ else if (version.toString().equalsIgnoreCase("V1.0") || } return readOther(version.toString(), buffer, offset); } - // //////////////////////// // Icewind Dale 2 @@ -1002,21 +1026,21 @@ private int readIWD2(ByteBuffer buffer, int offset) throws Exception { addField(new StringRef(buffer, offset, CRE_NAME)); addField(new StringRef(buffer, offset + 4, CRE_TOOLTIP)); - addField(new Flag(buffer, offset + 8, 4, CRE_FLAGS, s_flag)); // ToDo: figure these out whenever + addField(new Flag(buffer, offset + 8, 4, CRE_FLAGS, s_flag_iwd2)); addField(new DecNumber(buffer, offset + 12, 4, CRE_XP_VALUE)); addField(new DecNumber(buffer, offset + 16, 4, CRE_XP)); addField(new DecNumber(buffer, offset + 20, 4, CRE_GOLD)); addField(uniqueIdsFlag(new IdsFlag(buffer, offset + 24, 4, CRE_STATUS, "STATE.IDS"), "STATE.IDS", '_')); addField(new DecNumber(buffer, offset + 28, 2, CRE_HP_CURRENT)); addField(new DecNumber(buffer, offset + 30, 2, CRE_HP_MAX)); - addField(new IdsBitmap(buffer, offset + 32, 4, CRE_ANIMATION, "ANIMATE.IDS")); - addField(new ColorValue(buffer, offset + 36, 1, CRE_COLOR_METAL)); - addField(new ColorValue(buffer, offset + 37, 1, CRE_COLOR_MINOR)); - addField(new ColorValue(buffer, offset + 38, 1, CRE_COLOR_MAJOR)); - addField(new ColorValue(buffer, offset + 39, 1, CRE_COLOR_SKIN)); - addField(new ColorValue(buffer, offset + 40, 1, CRE_COLOR_LEATHER)); - addField(new ColorValue(buffer, offset + 41, 1, CRE_COLOR_ARMOR)); - addField(new ColorValue(buffer, offset + 42, 1, CRE_COLOR_HAIR)); + addField(new AnimateBitmap(buffer, offset + 32, 4, CRE_ANIMATION)); + addField(new ColorValue(buffer, offset + 36, 1, CRE_COLOR_METAL, false)); + addField(new ColorValue(buffer, offset + 37, 1, CRE_COLOR_MINOR, false)); + addField(new ColorValue(buffer, offset + 38, 1, CRE_COLOR_MAJOR, false)); + addField(new ColorValue(buffer, offset + 39, 1, CRE_COLOR_SKIN, false)); + addField(new ColorValue(buffer, offset + 40, 1, CRE_COLOR_LEATHER, false)); + addField(new ColorValue(buffer, offset + 41, 1, CRE_COLOR_ARMOR, false)); + addField(new ColorValue(buffer, offset + 42, 1, CRE_COLOR_HAIR, false)); Bitmap effect_version = addField(new Bitmap(buffer, offset + 43, 1, CRE_EFFECT_VERSION, s_effversion)); effect_version.addUpdateListener(this); addField(new ResourceRef(buffer, offset + 44, CRE_PORTRAIT_SMALL, "BMP")); @@ -1086,8 +1110,8 @@ private int readIWD2(ByteBuffer buffer, int offset) throws Exception addField(new ResourceRef(buffer, offset + 420, CRE_SCRIPT_TEAM, "BCS")); addField(new ResourceRef(buffer, offset + 428, CRE_SCRIPT_SPECIAL_1, "BCS")); - addField(new DecNumber(buffer, offset + 436, 2, CRE_ENCHANTMENT_LEVEL)); - addField(new Unknown(buffer, offset + 438, 2)); + addField(new DecNumber(buffer, offset + 436, 1, CRE_ENCHANTMENT_LEVEL)); + addField(new Unknown(buffer, offset + 437, 3)); addField(new Flag(buffer, offset + 440, 4, CRE_FEATS_1, s_feats1)); addField(new Flag(buffer, offset + 444, 4, CRE_FEATS_2, s_feats2)); addField(new Flag(buffer, offset + 448, 4, CRE_FEATS_3, s_feats3)); @@ -1194,7 +1218,8 @@ private int readIWD2(ByteBuffer buffer, int offset) throws Exception addField(new DecNumber(buffer, offset + 906, 2, CRE_IDENTIFIER_LOCAL)); addField(new TextString(buffer, offset + 908, 32, CRE_SCRIPT_NAME)); addField(new IdsBitmap(buffer, offset + 940, 2, CRE_CLASS_2, "CLASS.IDS")); - addField(new IdsBitmap(buffer, offset + 942, 4, CRE_CLASS_MASK, "CLASSMSK.IDS")); + addField(new IdsBitmap(buffer, offset + 942, 2, CRE_CLASS_MASK, "CLASSMSK.IDS")); + addField(new Unknown(buffer, offset + 944, 2)); // Bard spells for (int i = 0; i < 9; i++) { @@ -1433,13 +1458,13 @@ private int readOther(String version, ByteBuffer buffer, int offset) throws Exce if (Profile.getGame() == Profile.Game.PSTEE && version.equals("V1.0")) { setColorFieldsPSTEE(animate.getValue(), buffer, offset + 36, false); } else { - addField(new ColorValue(buffer, offset + 36, 1, CRE_COLOR_METAL)); - addField(new ColorValue(buffer, offset + 37, 1, CRE_COLOR_MINOR)); - addField(new ColorValue(buffer, offset + 38, 1, CRE_COLOR_MAJOR)); - addField(new ColorValue(buffer, offset + 39, 1, CRE_COLOR_SKIN)); - addField(new ColorValue(buffer, offset + 40, 1, CRE_COLOR_LEATHER)); - addField(new ColorValue(buffer, offset + 41, 1, CRE_COLOR_ARMOR)); - addField(new ColorValue(buffer, offset + 42, 1, CRE_COLOR_HAIR)); + addField(new ColorValue(buffer, offset + 36, 1, CRE_COLOR_METAL, true)); + addField(new ColorValue(buffer, offset + 37, 1, CRE_COLOR_MINOR, true)); + addField(new ColorValue(buffer, offset + 38, 1, CRE_COLOR_MAJOR, true)); + addField(new ColorValue(buffer, offset + 39, 1, CRE_COLOR_SKIN, true)); + addField(new ColorValue(buffer, offset + 40, 1, CRE_COLOR_LEATHER, true)); + addField(new ColorValue(buffer, offset + 41, 1, CRE_COLOR_ARMOR, true)); + addField(new ColorValue(buffer, offset + 42, 1, CRE_COLOR_HAIR, true)); } Bitmap effect_version = addField(new Bitmap(buffer, offset + 43, 1, CRE_EFFECT_VERSION, s_effversion)); effect_version.addUpdateListener(this); @@ -1619,9 +1644,9 @@ else if (version.equalsIgnoreCase("V9.0") || version.equalsIgnoreCase("V9.1")) { addField(new Unknown(buffer, offset + 572, 2)); } if (ResourceFactory.resourceExists("MAGESPEC.IDS")) { - addField(new IdsBitmap(buffer, offset + 574, 2, CRE_MAGE_TYPE, "MAGESPEC.IDS")); + addField(new IdsBitmap(buffer, offset + 574, 2, CRE_MAGE_TYPE, "MAGESPEC.IDS", true, true, false)); } else { - addField(new HashBitmap(buffer, offset + 574, 2, CRE_MAGE_TYPE, m_magetype)); + addField(new HashBitmap(buffer, offset + 574, 2, CRE_MAGE_TYPE, m_magetype, true, false, true)); } } addField(new ResourceRef(buffer, offset + 576, CRE_SCRIPT_OVERRIDE, "BCS")); @@ -1657,7 +1682,7 @@ else if (version.equalsIgnoreCase("V9.0") || version.equalsIgnoreCase("V9.1")) { addField(new DecNumber(buffer, offset + 727, 1, CRE_NUM_COLORS)); addField(new Flag(buffer, offset + 728, 4, CRE_ATTRIBUTES, s_attributes_pst)); for (int i = 0; i < 7; i++) { - addField(new ColorValue(buffer, offset + 732 + (i * 2), 2, String.format(CRE_COLOR_FMT, i+1), + addField(new ColorValue(buffer, offset + 732 + (i * 2), 2, String.format(CRE_COLOR_FMT, i+1), true, "PAL32.BMP")); // addField(new IdsBitmap(buffer, offset + 732 + (i * 2), 2, // String.format(CRE_COLOR_FMT, i+1), "CLOWNCLR.IDS")); @@ -1912,7 +1937,7 @@ else if (version.equalsIgnoreCase("V9.0") || version.equalsIgnoreCase("V9.1")) { private void updateMemorizedSpells() { // Assumes memorized spells offset is correct - int offset = ((HexNumber)getAttribute(CRE_OFFSET_MEMORIZED_SPELLS)).getValue() + getExtraOffset(); + int offset = ((IsNumeric)getAttribute(CRE_OFFSET_MEMORIZED_SPELLS)).getValue() + getExtraOffset(); int count = 0; for (final StructEntry o : getFields()) { if (o instanceof SpellMemorization) { @@ -1992,7 +2017,7 @@ private int setColorFieldsPSTEE(int animId, ByteBuffer buffer, int startOffset, } startOffset += field.getSize(); } else { - addField(new ColorValue(buffer, startOffset, 1, colorNames[i], "PAL32.BMP")); + addField(new ColorValue(buffer, startOffset, 1, colorNames[i], true, "PAL32.BMP")); startOffset++; } } @@ -2125,7 +2150,6 @@ private boolean convertEffects(int version, SectionOffset so, SectionCount sc) return retVal; } - // @Override public void itemStateChanged(ItemEvent event) { @@ -2138,9 +2162,7 @@ public void itemStateChanged(ItemEvent event) } } } - // - // @Override public boolean valueUpdated(UpdateEvent event) { @@ -2163,7 +2185,6 @@ public boolean valueUpdated(UpdateEvent event) } return false; } - // // Called by "Extended Search" // Checks whether the specified resource entry matches all available search options. @@ -2180,8 +2201,8 @@ public static boolean matchSearchOptions(ResourceEntry entry, SearchOptions sear Object o; // preparing substructures - DecNumber ofs = (DecNumber)cre.getAttribute(CRE_OFFSET_EFFECTS, false); - DecNumber cnt = (DecNumber)cre.getAttribute(CRE_NUM_EFFECTS, false); + IsNumeric ofs = (IsNumeric)cre.getAttribute(CRE_OFFSET_EFFECTS, false); + IsNumeric cnt = (IsNumeric)cre.getAttribute(CRE_NUM_EFFECTS, false); if (ofs != null && ofs.getValue() > 0 && cnt != null && cnt.getValue() > 0) { effects = new AbstractStruct[cnt.getValue()]; for (int idx = 0; idx < cnt.getValue(); idx++) { @@ -2192,8 +2213,8 @@ public static boolean matchSearchOptions(ResourceEntry entry, SearchOptions sear effects = new AbstractStruct[0]; } - ofs = (DecNumber)cre.getAttribute(CRE_OFFSET_ITEMS, false); - cnt = (DecNumber)cre.getAttribute(CRE_NUM_ITEMS, false); + ofs = (IsNumeric)cre.getAttribute(CRE_OFFSET_ITEMS, false); + cnt = (IsNumeric)cre.getAttribute(CRE_NUM_ITEMS, false); if (ofs != null && ofs.getValue() > 0 && cnt != null && cnt.getValue() > 0) { items = new AbstractStruct[cnt.getValue()]; for (int idx = 0; idx < cnt.getValue(); idx++) { @@ -2216,7 +2237,7 @@ public static boolean matchSearchOptions(ResourceEntry entry, SearchOptions sear SearchOptions.getResourceName(SearchOptions.CRE_IWD2SpellDomain)}; final String spellTypesStruct = SearchOptions.getResourceName(SearchOptions.CRE_IWD2SpellBard_Spell); final String spellTypesRef = SearchOptions.getResourceName(SearchOptions.CRE_IWD2SpellBard_Spell_ResRef); - List listSpells = new ArrayList(64); + List listSpells = new ArrayList<>(64); for (int i = 0; i < spellTypes.length; i++) { for (int j = 1; j < 10; j++) { String label = String.format(spellTypes[i], j); @@ -2237,8 +2258,8 @@ public static boolean matchSearchOptions(ResourceEntry entry, SearchOptions sear spells[i] = listSpells.get(i); } } else { - ofs = (DecNumber)cre.getAttribute(CRE_OFFSET_KNOWN_SPELLS, false); - cnt = (DecNumber)cre.getAttribute(CRE_NUM_KNOWN_SPELLS, false); + ofs = (IsNumeric)cre.getAttribute(CRE_OFFSET_KNOWN_SPELLS, false); + cnt = (IsNumeric)cre.getAttribute(CRE_NUM_KNOWN_SPELLS, false); if (ofs != null && ofs.getValue() > 0 && cnt != null && cnt.getValue() > 0) { spells = new Datatype[cnt.getValue()]; final String spellLabel = SearchOptions.getResourceName(SearchOptions.CRE_Spell_Spell1); diff --git a/src/org/infinity/resource/cre/Iwd2Struct.java b/src/org/infinity/resource/cre/Iwd2Struct.java index ba3b0f548..8db855592 100644 --- a/src/org/infinity/resource/cre/Iwd2Struct.java +++ b/src/org/infinity/resource/cre/Iwd2Struct.java @@ -52,7 +52,6 @@ public Iwd2Struct(AbstractStruct superStruct, ByteBuffer buffer, int offset, Dec setOffset(offset); } - // @Override public AddRemovable[] getPrototypes() throws Exception { @@ -75,9 +74,7 @@ public AddRemovable confirmAddEntry(AddRemovable entry) throws Exception { return entry; } - // - // @Override protected void datatypeAdded(AddRemovable datatype) { @@ -95,13 +92,10 @@ protected int getInsertPosition() { return count.getValue(); } - // - // @Override public int read(ByteBuffer buffer, int offset) { return -1; } - // } diff --git a/src/org/infinity/resource/cre/SpellMemorization.java b/src/org/infinity/resource/cre/SpellMemorization.java index c8cff0fd6..a94a2f5f8 100644 --- a/src/org/infinity/resource/cre/SpellMemorization.java +++ b/src/org/infinity/resource/cre/SpellMemorization.java @@ -8,7 +8,7 @@ import org.infinity.datatype.Bitmap; import org.infinity.datatype.DecNumber; -import org.infinity.datatype.HexNumber; +import org.infinity.datatype.IsNumeric; import org.infinity.resource.AbstractStruct; import org.infinity.resource.AddRemovable; import org.infinity.resource.HasChildStructs; @@ -36,15 +36,12 @@ public final class SpellMemorization extends AbstractStruct implements AddRemova super(cre, CRE_MEMORIZATION + " " + nr, buffer, offset); } - // @Override public boolean canRemove() { return true; } - // - // @Override public AddRemovable[] getPrototypes() throws Exception { @@ -56,25 +53,21 @@ public AddRemovable confirmAddEntry(AddRemovable entry) throws Exception { return entry; } - // - // @Override protected void setAddRemovableOffset(AddRemovable datatype) { if (datatype instanceof MemorizedSpells) { - int index = ((DecNumber)getAttribute(CRE_MEMORIZATION_SPELL_TABLE_INDEX)).getValue(); - index += ((DecNumber)getAttribute(CRE_MEMORIZATION_SPELL_COUNT)).getValue(); + int index = ((IsNumeric)getAttribute(CRE_MEMORIZATION_SPELL_TABLE_INDEX)).getValue(); + index += ((IsNumeric)getAttribute(CRE_MEMORIZATION_SPELL_COUNT)).getValue(); final CreResource cre = (CreResource)getParent(); - int offset = ((HexNumber)cre.getAttribute(CreResource.CRE_OFFSET_MEMORIZED_SPELLS)).getValue() + + int offset = ((IsNumeric)cre.getAttribute(CreResource.CRE_OFFSET_MEMORIZED_SPELLS)).getValue() + cre.getExtraOffset(); datatype.setOffset(offset + 12 * index); ((AbstractStruct)datatype).realignStructOffsets(); } } - // - // @Override public int read(ByteBuffer buffer, int offset) { @@ -86,12 +79,11 @@ public int read(ByteBuffer buffer, int offset) addField(new DecNumber(buffer, offset + 12, 4, CRE_MEMORIZATION_SPELL_COUNT)); return offset + 16; } - // public void readMemorizedSpells(ByteBuffer buffer, int offset) throws Exception { - DecNumber firstSpell = (DecNumber)getAttribute(CRE_MEMORIZATION_SPELL_TABLE_INDEX); - DecNumber numSpell = (DecNumber)getAttribute(CRE_MEMORIZATION_SPELL_COUNT); + IsNumeric firstSpell = (IsNumeric)getAttribute(CRE_MEMORIZATION_SPELL_TABLE_INDEX); + IsNumeric numSpell = (IsNumeric)getAttribute(CRE_MEMORIZATION_SPELL_COUNT); for (int i = 0; i < numSpell.getValue(); i++) { addField(new MemorizedSpells(this, buffer, offset + 12 * (firstSpell.getValue() + i))); } diff --git a/src/org/infinity/resource/cre/Viewer.java b/src/org/infinity/resource/cre/Viewer.java index f52ca07be..698e3b2fa 100644 --- a/src/org/infinity/resource/cre/Viewer.java +++ b/src/org/infinity/resource/cre/Viewer.java @@ -96,6 +96,7 @@ private static JPanel makeMiscPanelIWD2(CreResource cre) ViewerUtil.addLabelFieldPair(panel, cre.getAttribute(CreResource.CRE_GENDER), gbl, gbc, true); ViewerUtil.addLabelFieldPair(panel, cre.getAttribute(CreResource.CRE_ALIGNMENT), gbl, gbc, true); ViewerUtil.addLabelFieldPair(panel, cre.getAttribute(CreResource.CRE_KIT), gbl, gbc, true); + ViewerUtil.addLabelFieldPair(panel, cre.getAttribute(CreResource.CRE_ANIMATION), gbl, gbc, true); ViewerUtil.addLabelFieldPair(panel, cre.getAttribute(CreResource.CRE_CHALLENGE_RATING), gbl, gbc, true); ViewerUtil.addLabelFieldPair(panel, cre.getAttribute(CreResource.CRE_SAVE_FORTITUDE), gbl, gbc, true); ViewerUtil.addLabelFieldPair(panel, cre.getAttribute(CreResource.CRE_SAVE_REFLEX), gbl, gbc, true); diff --git a/src/org/infinity/resource/cre/ViewerAnimation.java b/src/org/infinity/resource/cre/ViewerAnimation.java new file mode 100644 index 000000000..6823993fd --- /dev/null +++ b/src/org/infinity/resource/cre/ViewerAnimation.java @@ -0,0 +1,625 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre; + +import java.awt.AlphaComposite; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.image.BufferedImage; +import java.util.Objects; + +import javax.swing.BorderFactory; +import javax.swing.DefaultComboBoxModel; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JToggleButton; +import javax.swing.ScrollPaneConstants; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; +import javax.swing.Timer; + +import org.infinity.NearInfinity; +import org.infinity.gui.ChildFrame; +import org.infinity.gui.RenderCanvas; +import org.infinity.gui.ViewerUtil; +import org.infinity.gui.WindowBlocker; +import org.infinity.icon.Icons; +import org.infinity.resource.cre.browser.CreatureBrowser; +import org.infinity.resource.cre.decoder.SpriteDecoder; +import org.infinity.resource.cre.decoder.SpriteDecoder.SpriteBamControl; +import org.infinity.resource.cre.decoder.util.Sequence; +import org.infinity.resource.cre.decoder.util.SpriteUtils; + +/** + * A basic creature animation viewer. + */ +public class ViewerAnimation extends JComponent implements ActionListener +{ + private static final Color COLOR_TRANSPARENT = new Color(0, true); + private static final int ANIM_DELAY = 1000 / 15; // 15 fps in milliseconds + + private static boolean zoom = false; + private static boolean showSelectionCircle = false; + private static boolean showPersonalSpace = false; + private static boolean showOverlayBorders = false; + + private final CreResource cre; + + private boolean initialized; + private SpriteDecoder decoder; + private SpriteBamControl bamControl; + private BufferedImage tmpImage; + private RenderCanvas rcDisplay; + private int curCycle, curFrame; + private Timer timer; + private Sequence sequence; + private JButton bNextCycle, bPrevCycle, bNextFrame, bPrevFrame, bOpenBrowser; + private JToggleButton bPlay; + private JLabel lCurCycle, lCurFrame; + private JComboBox cbSequences; + private JCheckBox cbShowCircle, cbShowSpace, cbShowBorders, cbZoom; + + public ViewerAnimation(CreResource cre) + { + this.cre = Objects.requireNonNull(cre); + init(); + } + + public CreResource getCre() + { + return cre; + } + + /** Returns the associated {@code SpriteDecoder} instance. */ + public SpriteDecoder getDecoder() + { + return decoder; + } + + /** Returns the {@code BamControl} instance linked to the {@code SpriteDecoder}. */ + public SpriteBamControl getController() + { + return bamControl; + } + + private void setController(SpriteBamControl ctrl) + { + this.bamControl = Objects.requireNonNull(ctrl, "BamControl cannot be null"); + } + + /** Returns the selected animation sequence. */ + public Sequence getAnimationSequence() + { + return sequence; + } + + /** Loads a new animation sequence. */ + private void setAnimationSequence(Sequence seq) throws Exception + { + if (seq != null && seq != getAnimationSequence()) { + sequence = seq; + curFrame = 0; + getDecoder().loadSequence(seq); + resetAnimationSequence(); + showFrame(); + } + } + + private void resetAnimationSequence() throws Exception + { + setController(getDecoder().createControl()); + getController().setMode(SpriteBamControl.Mode.SHARED); + getController().setSharedPerCycle(false); + if (curCycle < getController().cycleCount()) { + getController().cycleSet(curCycle); + if (curFrame < getController().cycleFrameCount()) { + getController().cycleSetFrameIndex(curFrame); + } + } + curCycle = getController().cycleGet(); + curFrame = getController().cycleGetFrameIndex(); + updateCanvasSize(); + } + + /** Ensures that the canvas is big enough to contain the current creature animation sequence. */ + public void updateCanvasSize() + { + int zoom = isZoomed() ? 2 : 1; + Dimension dim = getController().getSharedDimension(); + Dimension dimDisplay = new Dimension(dim.width * zoom, dim.height * zoom); + boolean imageChanged = !dim.equals(new Dimension(rcDisplay.getImage().getWidth(null), rcDisplay.getImage().getHeight(null))); + boolean sizeChanged = !dimDisplay.equals(rcDisplay.getPreferredSize()); + if (imageChanged || sizeChanged) { + tmpImage = new BufferedImage(dim.width, dim.height, BufferedImage.TYPE_INT_ARGB); + rcDisplay.setImage(new BufferedImage(dim.width, dim.height, BufferedImage.TYPE_INT_ARGB)); + if (sizeChanged) { + rcDisplay.setPreferredSize(dimDisplay); + Container c = SwingUtilities.getAncestorOfClass(JScrollPane.class, rcDisplay); + if (c != null) { + c.setMinimumSize(rcDisplay.getPreferredSize()); + c.invalidate(); + c.getParent().validate(); + } + } + } + updateCanvas(); + } + + /** Updates display with content of the current animation frame. */ + public void updateCanvas() + { + // pre-rendering new frame + Graphics2D g = tmpImage.createGraphics(); + try { + g.setComposite(AlphaComposite.Src); + g.setColor(COLOR_TRANSPARENT); + g.fillRect(0, 0, tmpImage.getWidth(), tmpImage.getHeight()); + } finally { + g.dispose(); + g = null; + } + getController().cycleGetFrame(tmpImage); + + g = (Graphics2D)rcDisplay.getImage().getGraphics(); + try { + // clearing old content + g.setComposite(AlphaComposite.Src); + g.setColor(COLOR_TRANSPARENT); + g.fillRect(0, 0, rcDisplay.getImage().getWidth(null), rcDisplay.getImage().getHeight(null)); + + // drawing markers + getController().getVisualMarkers(g, null); + + // drawing animation frame + g.setComposite(AlphaComposite.SrcOver); + g.drawImage(tmpImage, 0, 0, null); + } finally { + g.dispose(); + g = null; + } + + rcDisplay.repaint(); + } + + /** Returns whether animation is zoomed. */ + public boolean isZoomed() + { + return cbZoom.isSelected(); + } + + /** Returns whether the animation is played back. */ + public boolean isPlaying() + { + if (timer == null) { + timer = new Timer(ANIM_DELAY, this); + } + return timer.isRunning(); + } + + /** Toggles playback between "play" and "pause". */ + public void togglePlay() + { + if (isPlaying()) { + pause(); + } else { + play(); + } + } + + /** Starts playback. Does nothing if animation is already played back. */ + public void play() + { + if (!isPlaying()) { + timer.restart(); + } + } + + /** Stops playback. Does nothing if animation is already stopped. */ + public void pause() + { + if (isPlaying()) { + timer.stop(); + } + } + + /** Rewinds animation of current cycle to first frame. */ + public void rewind() + { + curFrame = 0; + showFrame(); + } + + /** Loads the creature animation associated with the current CRE resource. */ + public void open() + { + // loading animation on demand + if (!isInitialized()) { + try { + WindowBlocker.blockWindow(true); + initAnimation(); + } catch (Exception ex) { + ex.printStackTrace(); + WindowBlocker.blockWindow(false); + JOptionPane.showMessageDialog(NearInfinity.getInstance(), + "Creature animation could not be loaded.\nError message: " + ex.getMessage(), + "Error", JOptionPane.ERROR_MESSAGE); + } finally { + WindowBlocker.blockWindow(false); + } + } + } + + /** Cleans up resources. */ + public void close() + { + pause(); + setInitialized(false); + if (getDecoder() != null) { + getDecoder().close(); + } + SpriteUtils.clearCache(); + this.bamControl = null; + this.decoder = null; + } + +//--------------------- Begin Interface ActionListener --------------------- + + @Override + public void actionPerformed(ActionEvent event) + { + if (timer == event.getSource()) { + if (getController().cycleFrameCount() > 0) { + curFrame = (curFrame + 1) % getController().cycleFrameCount(); + } + showFrame(); + } + else if (cbSequences == event.getSource()) { + Sequence seq = cbSequences.getModel().getElementAt(cbSequences.getSelectedIndex()); + try { + WindowBlocker.blockWindow(true); + setAnimationSequence(seq); + updateControls(); + } catch (Exception e) { + e.printStackTrace(); + JOptionPane.showMessageDialog(this, e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); + cbSequences.setSelectedItem(getAnimationSequence()); + } finally { + WindowBlocker.blockWindow(false); + } + } + else if (cbZoom == event.getSource()) { + try { + WindowBlocker.blockWindow(true); + zoom = cbZoom.isSelected(); + updateCanvasSize(); + } finally { + WindowBlocker.blockWindow(false); + } + } + else if (cbShowCircle == event.getSource()) { + showSelectionCircle = cbShowCircle.isSelected(); + getDecoder().setSelectionCircleEnabled(showSelectionCircle); + updateCanvas(); + } + else if (cbShowSpace == event.getSource()) { + showPersonalSpace = cbShowSpace.isSelected(); + getDecoder().setPersonalSpaceVisible(showPersonalSpace); + updateCanvas(); + } + else if (cbShowBorders == event.getSource()) { + try { + WindowBlocker.blockWindow(true); + showOverlayBorders = cbShowBorders.isSelected(); + getDecoder().setBoundingBoxVisible(showOverlayBorders); + resetAnimationSequence(); + } catch (Exception e) { + e.printStackTrace(); + } finally { + WindowBlocker.blockWindow(false); + } + } + else if (bPrevCycle == event.getSource()) { + if (curCycle > 0) { + curCycle--; + getController().cycleSet(curCycle); + if (isPlaying() && getController().cycleFrameCount() == 0) { + pause(); + bPlay.setSelected(false); + } + rewind(); + showFrame(); + } + } + else if (bNextCycle == event.getSource()) { + if (curCycle < getController().cycleCount() - 1) { + curCycle++; + getController().cycleSet(curCycle); + if (isPlaying() && getController().cycleFrameCount() == 0) { + pause(); + bPlay.setSelected(false); + } + rewind(); + showFrame(); + } + } + else if (bPrevFrame == event.getSource()) { + if (curFrame > 0) { + curFrame--; + showFrame(); + } + } + else if (bNextFrame == event.getSource()) { + if (curFrame < getController().cycleFrameCount() - 1) { + curFrame++; + showFrame(); + } + } + else if (bPlay == event.getSource()) { + if (bPlay.isSelected()) { + play(); + } else { + pause(); + } + updateControls(); + } + else if (bOpenBrowser == event.getSource()) { + CreatureBrowser cv = ChildFrame.show(CreatureBrowser.class, () -> new CreatureBrowser(getCre())); + if (cv != null) { + pause(); + if (getCre() != cv.getCreResource()) { + cv.setCreResource(getCre()); + } + cv.toFront(); + } + } + } + +//--------------------- End Interface ActionListener --------------------- + + private void init() + { + tmpImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); + rcDisplay = new RenderCanvas(new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB)); + rcDisplay.setHorizontalAlignment(SwingConstants.CENTER); + rcDisplay.setVerticalAlignment(SwingConstants.CENTER); + rcDisplay.setInterpolationType(RenderCanvas.TYPE_NEAREST_NEIGHBOR); + rcDisplay.setScalingEnabled(true); + JScrollPane scrollDisplay = new JScrollPane(rcDisplay, ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + scrollDisplay.setBorder(BorderFactory.createEmptyBorder()); + + bPlay = new JToggleButton("Play", Icons.getIcon(Icons.ICON_PLAY_16)); + bPlay.addActionListener(this); + + lCurCycle = new JLabel("", JLabel.CENTER); + bPrevCycle = new JButton(Icons.getIcon(Icons.ICON_BACK_16)); + bPrevCycle.setMargin(new Insets(bPrevCycle.getMargin().top, 2, bPrevCycle.getMargin().bottom, 2)); + bPrevCycle.addActionListener(this); + bNextCycle = new JButton(Icons.getIcon(Icons.ICON_FORWARD_16)); + bNextCycle.setMargin(bPrevCycle.getMargin()); + bNextCycle.addActionListener(this); + + lCurFrame = new JLabel("", JLabel.CENTER); + bPrevFrame = new JButton(Icons.getIcon(Icons.ICON_BACK_16)); + bPrevFrame.setMargin(new Insets(bPrevFrame.getMargin().top, 2, bPrevFrame.getMargin().bottom, 2)); + bPrevFrame.addActionListener(this); + bNextFrame = new JButton(Icons.getIcon(Icons.ICON_FORWARD_16)); + bNextFrame.setMargin(bPrevFrame.getMargin()); + bNextFrame.addActionListener(this); + + JLabel lSequence = new JLabel("Sequence:"); + DefaultComboBoxModel modelSequences = new DefaultComboBoxModel<>(); + cbSequences = new JComboBox<>(modelSequences); + + cbZoom = new JCheckBox("Zoom", zoom); + cbZoom.addActionListener(this); + cbShowCircle = new JCheckBox("Show selection circle", showSelectionCircle); + cbShowCircle.addActionListener(this); + cbShowSpace = new JCheckBox("Show personal space", showPersonalSpace); + cbShowSpace.addActionListener(this); + cbShowBorders = new JCheckBox("Show overlay borders", showOverlayBorders); + cbShowBorders.addActionListener(this); + + bOpenBrowser = new JButton("Open in browser", Icons.getIcon(Icons.ICON_CRE_VIEWER_24)); + bOpenBrowser.setToolTipText("Open in Creature Animation Browser"); + bOpenBrowser.addActionListener(this); + + GridBagConstraints c = new GridBagConstraints(); + // first row of controls: animation controls, sequence selection and browser button + JPanel pRow1 = new JPanel(new GridBagLayout()); + c = ViewerUtil.setGBC(c, 0, 0, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); + pRow1.add(new JPanel(), c); + c = ViewerUtil.setGBC(c, 1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0); + pRow1.add(lCurCycle, c); + c = ViewerUtil.setGBC(c, 2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.NONE, new Insets(0, 8, 0, 0), 0, 0); + pRow1.add(bPrevCycle, c); + c = ViewerUtil.setGBC(c, 3, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.NONE, new Insets(0, 4, 0, 0), 0, 0); + pRow1.add(bNextCycle, c); + + c = ViewerUtil.setGBC(c, 4, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.NONE, new Insets(0, 16, 0, 0), 0, 0); + pRow1.add(lCurFrame, c); + c = ViewerUtil.setGBC(c, 5, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.NONE, new Insets(0, 8, 0, 0), 0, 0); + pRow1.add(bPrevFrame, c); + c = ViewerUtil.setGBC(c, 6, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.NONE, new Insets(0, 4, 0, 0), 0, 0); + pRow1.add(bNextFrame, c); + + c = ViewerUtil.setGBC(c, 7, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.NONE, new Insets(0, 16, 0, 0), 0, 0); + pRow1.add(bPlay, c); + + c = ViewerUtil.setGBC(c, 8, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.NONE, new Insets(0, 16, 0, 0), 0, 0); + pRow1.add(lSequence, c); + c = ViewerUtil.setGBC(c, 9, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.NONE, new Insets(0, 4, 0, 0), 0, 0); + pRow1.add(cbSequences, c); + + c = ViewerUtil.setGBC(c, 10, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.NONE, new Insets(0, 16, 0, 0), 0, 0); + pRow1.add(bOpenBrowser, c); + + c = ViewerUtil.setGBC(c, 11, 0, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); + pRow1.add(new JPanel(), c); + + // second row of controls: various checkboxes + JPanel pRow2 = new JPanel(new GridBagLayout()); + c = ViewerUtil.setGBC(c, 0, 0, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); + pRow2.add(new JPanel(), c); + c = ViewerUtil.setGBC(c, 1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0); + pRow2.add(cbZoom, c); + c = ViewerUtil.setGBC(c, 2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.NONE, new Insets(0, 8, 0, 0), 0, 0); + pRow2.add(cbShowCircle, c); + c = ViewerUtil.setGBC(c, 3, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.NONE, new Insets(0, 8, 0, 0), 0, 0); + pRow2.add(cbShowSpace, c); + c = ViewerUtil.setGBC(c, 4, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.NONE, new Insets(0, 8, 0, 0), 0, 0); + pRow2.add(cbShowBorders, c); + c = ViewerUtil.setGBC(c, 5, 0, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); + pRow2.add(new JPanel(), c); + + + JPanel pMain = new JPanel(new GridBagLayout()); + c = ViewerUtil.setGBC(c, 0, 0, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, + GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0); + pMain.add(scrollDisplay, c); + c = ViewerUtil.setGBC(c, 0, 1, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, + GridBagConstraints.HORIZONTAL, new Insets(8, 0, 0, 0), 0, 0); + pMain.add(pRow1, c); + c = ViewerUtil.setGBC(c, 0, 2, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, + GridBagConstraints.HORIZONTAL, new Insets(8, 0, 8, 0), 0, 0); + pMain.add(pRow2, c); + + setLayout(new BorderLayout()); + add(pMain, BorderLayout.CENTER); + + setInitialized(false); + + addComponentListener(new ComponentAdapter() { + @Override + public void componentShown(ComponentEvent e) + { + // loading animation on demand + open(); + } + }); + } + + private void initAnimation() throws Exception + { + this.decoder = SpriteDecoder.importSprite(getCre()); + getDecoder().setBoundingBoxVisible(showOverlayBorders); + getDecoder().setSelectionCircleEnabled(showSelectionCircle); + getDecoder().setPersonalSpaceVisible(showPersonalSpace); + + // preparing sequence list + DefaultComboBoxModel modelSequences = (DefaultComboBoxModel)cbSequences.getModel(); + for (final Sequence seq : Sequence.values()) { + if (getDecoder().isSequenceAvailable(seq)) { + modelSequences.addElement(seq); + } + } + cbSequences.setEnabled(cbSequences.getItemCount() > 0); + cbSequences.addActionListener(this); + + // loading animation sequence + if (cbSequences.isEnabled()) { + int seqIdx = 0; + for (final Sequence sequence : Sequence.getDefaultSequences()) { + int idx = ((DefaultComboBoxModel)cbSequences.getModel()).getIndexOf(sequence); + if (idx >= 0) { + seqIdx = idx; + break; + } + } + Sequence seq = cbSequences.getModel().getElementAt(seqIdx); + cbSequences.setSelectedItem(seq); + setAnimationSequence(seq); + } + + setInitialized(true); + } + + private boolean isInitialized() + { + return initialized; + } + + private void setInitialized(boolean b) + { + initialized = b; + JComponent[] controls = new JComponent[] { + bNextCycle, bPrevCycle, bNextFrame, bPrevFrame, bOpenBrowser, bPlay, + lCurCycle, lCurFrame, cbSequences, cbShowCircle, cbShowSpace, cbShowBorders, cbZoom + }; + for (final JComponent c : controls) { + if (c != null) { + c.setEnabled(initialized); + } + } + } + + private void showFrame() + { + if (getController() == null) { + return; + } + + if (!getController().cycleSetFrameIndex(curFrame)) { + getController().cycleReset(); + curFrame = 0; + } + + updateCanvas(); + + lCurCycle.setText("Cycle: " + curCycle + "/" + (getController().cycleCount() - 1)); + lCurFrame.setText("Frame: " + curFrame + "/" + (getController().cycleFrameCount() - 1)); + updateControls(); + } + + private void updateControls() + { + if (getController() != null) { + bPrevFrame.setEnabled(curFrame > 0); + bPrevCycle.setEnabled(curCycle > 0); + bNextFrame.setEnabled(curFrame < getController().cycleFrameCount() - 1); + bNextCycle.setEnabled(curCycle < getController().cycleCount() - 1); + bPlay.setEnabled(getController().cycleFrameCount() > 0); + } else { + bPrevFrame.setEnabled(false); + bPrevCycle.setEnabled(false); + bNextFrame.setEnabled(false); + bNextCycle.setEnabled(false); + bPlay.setEnabled(false); + } + } +} diff --git a/src/org/infinity/resource/cre/ViewerItems.java b/src/org/infinity/resource/cre/ViewerItems.java index ebe4efa72..b21147268 100644 --- a/src/org/infinity/resource/cre/ViewerItems.java +++ b/src/org/infinity/resource/cre/ViewerItems.java @@ -26,7 +26,6 @@ import javax.swing.table.DefaultTableCellRenderer; import org.infinity.datatype.DecNumber; -import org.infinity.datatype.HexNumber; import org.infinity.datatype.IsNumeric; import org.infinity.datatype.ResourceRef; import org.infinity.gui.ToolTipTableCellRenderer; @@ -48,7 +47,7 @@ final class ViewerItems extends JPanel implements ActionListener, ListSelectionL { super(new BorderLayout(0, 3)); final List items = new ArrayList<>(); - HexNumber slots_offset = (HexNumber)cre.getAttribute(CreResource.CRE_OFFSET_ITEM_SLOTS); + IsNumeric slots_offset = (IsNumeric)cre.getAttribute(CreResource.CRE_OFFSET_ITEM_SLOTS); for (final StructEntry entry : cre.getFields()) { if (entry instanceof Item) items.add((Item)entry); diff --git a/src/org/infinity/resource/cre/browser/AttributesPanel.java b/src/org/infinity/resource/cre/browser/AttributesPanel.java new file mode 100644 index 000000000..dc9162786 --- /dev/null +++ b/src/org/infinity/resource/cre/browser/AttributesPanel.java @@ -0,0 +1,135 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.browser; + +import java.awt.Dimension; +import java.awt.Font; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Objects; + +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.border.EtchedBorder; + +import org.infinity.gui.ViewFrame; +import org.infinity.gui.ViewerUtil; +import org.infinity.icon.Icons; +import org.infinity.resource.ResourceFactory; +import org.infinity.resource.cre.decoder.SpriteDecoder; +import org.infinity.resource.key.ResourceEntry; + +/** + * This panel provides a table view of creature animation attributes. + */ +public class AttributesPanel extends JPanel implements ActionListener +{ + private final CreatureBrowser browser; + + private JTable tableAnimation; + private DecoderAttributesTableModel tableModel; + private JButton bIni; + + public AttributesPanel(CreatureBrowser viewer) + { + super(); + this.browser = Objects.requireNonNull(viewer); + init(); + } + + /** Returns the associated {@code CreatureViewer} instance. */ + public CreatureBrowser getBrowser() { return browser; } + + /** Discards and reloads the current settings and attributes list. */ + public void reset() + { + SpriteDecoder decoder = getBrowser().getDecoder(); + tableModel.setDecoder(decoder); + bIni.setEnabled(getIniResource() != null); + } + + /** + * Returns the INI file associated with the currently selected creature animation if available, + * returns {@code null} otherwise. + */ + private ResourceEntry getIniResource() + { + ResourceEntry retVal = null; + if (getBrowser().getDecoder() != null) { + String iniResref = String.format("%04X", getBrowser().getDecoder().getAnimationId()); + if (iniResref.length() == 4) { + retVal = ResourceFactory.getResourceEntry(iniResref + ".INI"); + } + } + return retVal; + } + + private void init() + { + JLabel label = new JLabel("Animation attributes:"); + + bIni = new JButton("View/Edit INI", Icons.getIcon(Icons.ICON_ZOOM_16)); + bIni.addActionListener(this); + + GridBagConstraints c = new GridBagConstraints(); + JPanel panel = new JPanel(new GridBagLayout()); + c = ViewerUtil.setGBC(c, 0, 0, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); + panel.add(label, c); + c = ViewerUtil.setGBC(c, 1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_END, + GridBagConstraints.NONE, new Insets(0, 8, 0, 0), 0, 0); + panel.add(bIni); + + tableModel = new DecoderAttributesTableModel(); + tableAnimation = new JTable(tableModel, + new DecoderAttributesTableModel.AttributesColumnModel(), + new DecoderAttributesTableModel.AttributesListSelectionModel()); + Dimension dim = new Dimension(0, 50); + for (int i = 0; i < tableAnimation.getColumnModel().getColumnCount(); i++) { + dim.width += tableAnimation.getColumnModel().getColumn(i).getPreferredWidth(); + } + tableAnimation.setPreferredScrollableViewportSize(dim); + tableAnimation.getTableHeader().setFont(tableAnimation.getTableHeader().getFont().deriveFont(Font.BOLD)); + tableAnimation.getTableHeader().setDefaultRenderer(new DecoderAttributesTableModel.AttributesHeaderRenderer(tableAnimation)); + tableAnimation.getTableHeader().setReorderingAllowed(false); + tableAnimation.setRowHeight(tableAnimation.getRowHeight() * 3 / 2); + tableAnimation.setIntercellSpacing(new Dimension(4, 0)); + JScrollPane scrollTable = new JScrollPane(tableAnimation); + scrollTable.setBorder(new EtchedBorder()); + + setLayout(new GridBagLayout()); + c = ViewerUtil.setGBC(c, 0, 0, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); + add(panel, c); + c = ViewerUtil.setGBC(c, 0, 1, 1, 1, 1.0, 1.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.BOTH, new Insets(4, 0, 0, 0), 0, 0); + add(scrollTable, c); + } + + //--------------------- Begin Interface ActionListener --------------------- + + @Override + public void actionPerformed(ActionEvent e) + { + if (e.getSource() == bIni) { + ResourceEntry entry = getIniResource(); + if (entry != null) { + new ViewFrame(getBrowser(), ResourceFactory.getResource(entry)); + } else { + JOptionPane.showMessageDialog(getBrowser(), "Unable to open INI resource.", "View/Edit INI", + JOptionPane.ERROR_MESSAGE); + } + } + } + + //--------------------- End Interface ActionListener --------------------- +} diff --git a/src/org/infinity/resource/cre/browser/ColorSelectionModel.java b/src/org/infinity/resource/cre/browser/ColorSelectionModel.java new file mode 100644 index 000000000..f70b0ded5 --- /dev/null +++ b/src/org/infinity/resource/cre/browser/ColorSelectionModel.java @@ -0,0 +1,421 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.browser; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Component; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.RenderingHints; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.stream.IntStream; + +import javax.swing.AbstractListModel; +import javax.swing.ComboBoxModel; +import javax.swing.DefaultListCellRenderer; +import javax.swing.ImageIcon; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.UIManager; +import javax.swing.border.Border; +import javax.swing.border.EmptyBorder; + +import org.infinity.gui.BrowserMenuBar; +import org.infinity.resource.Profile; +import org.infinity.resource.ResourceFactory; +import org.infinity.resource.graphics.GraphicsResource; +import org.infinity.resource.key.ResourceEntry; +import org.infinity.util.IdsMap; +import org.infinity.util.IdsMapCache; +import org.infinity.util.IdsMapEntry; +import org.infinity.util.Misc; +import org.infinity.util.Table2da; +import org.infinity.util.Table2daCache; + +/** + * {@code ComboBoxModel} for the color selection combo box used in the Creature Animation Browser. + */ +public class ColorSelectionModel extends AbstractListModel +implements ComboBoxModel +{ + private static final HashMap randomColors = new HashMap<>(); + + private final List colorList = new ArrayList<>(256); + private final ColorCellRenderer renderer = new ColorCellRenderer(); + + private Object selectedItem; + private ResourceEntry colorEntry; + private IdsMap colorMap; + + public ColorSelectionModel() + { + this(null); + } + + public ColorSelectionModel(ResourceEntry bmpResource) + { + super(); + this.colorMap = ResourceFactory.resourceExists("CLOWNCLR.IDS") ? IdsMapCache.get("CLOWNCLR.IDS") : null; + setColorRangesEntry(bmpResource); + } + + /** Returns the {@code ListCellRenderer} instance associated with the list model. */ + public ColorCellRenderer getRenderer() { return renderer; } + + /** + * Returns the {@code ResourceEntry} instance of the BMP resource containing color ranges. + * Returns {@code null} if no BMP resource is available. + */ + public ResourceEntry getColorRangesEntry() { return colorEntry; } + + public void setColorRangesEntry(ResourceEntry bmpResource) + { + if (bmpResource == null) { + if (Profile.getGame() == Profile.Game.PST || Profile.getGame() == Profile.Game.PSTEE) { + bmpResource = ResourceFactory.getResourceEntry("PAL32.BMP"); + } else { + bmpResource = ResourceFactory.getResourceEntry("RANGES12.BMP"); + if (bmpResource == null) { + bmpResource = ResourceFactory.getResourceEntry("MPALETTE.BMP"); + } + } + } + + if ((colorEntry != null && !colorEntry.equals(bmpResource)) || + (colorEntry == null && bmpResource != null)) { + colorEntry = bmpResource; + init(); + } + } + + public void reload() + { + initRandomColors(true); + init(); + } + + /** + * Returns the index-position of the specified object in the list. + * @param anItem a {@code ColorEntry} object or {@code Number} object. + * @return an int representing the index position, where 0 is the first position. Returns -1 + * if the item could not be found in the list. + */ + public int getIndexOf(Object anItem) + { + if (anItem instanceof ColorEntry) { + return colorList.indexOf(anItem); + } else if (anItem instanceof Number) { + final int colIdx = ((Number)anItem).intValue(); + return IntStream + .range(0, colorList.size()) + .filter(i -> colorList.get(i).getIndex() == colIdx) + .findAny() + .orElse(-1); + } + return -1; + } + + /** Empties the list. */ + public void removeAllElements() + { + if (!colorList.isEmpty()) { + int oldSize = colorList.size(); + colorList.clear(); + selectedItem = null; + if (oldSize > 0) { + fireIntervalRemoved(this, 0, oldSize - 1); + } + } else { + selectedItem = null; + } + } + +//--------------------- Begin Interface ListModel --------------------- + + @Override + public int getSize() + { + return colorList.size(); + } + + @Override + public ColorEntry getElementAt(int index) + { + if (index >= 0 && index < colorList.size()) { + return colorList.get(index); + } else { + return null; + } + } + +//--------------------- End Interface ListModel --------------------- + +//--------------------- Begin Interface ComboBoxModel --------------------- + + @Override + public void setSelectedItem(Object anItem) + { + if ((selectedItem != null && !selectedItem.equals(anItem)) || + selectedItem == null && anItem != null) { + selectedItem = anItem; + fireContentsChanged(this, -1, -1); + } + } + + @Override + public Object getSelectedItem() + { + return selectedItem; + } + +//--------------------- End Interface ComboBoxModel --------------------- + + private void init() + { + removeAllElements(); + + initRandomColors(false); + + BufferedImage image = null; + try {image = new GraphicsResource(getColorRangesEntry()).getImage(); } catch (Exception e) {} + + int max = (image != null) ? image.getHeight() - 1 : 0; + if (!randomColors.isEmpty()) { + max = Math.max(max, Collections.max(randomColors.keySet())); + } + + for (int i = 0; i <= max; i++) { + String name = randomColors.get(Integer.valueOf(i)); + if (name != null) { + colorList.add(new ColorEntry(i, name, true)); + } else { + name = ""; + if (colorMap != null) { + IdsMapEntry idsEntry = colorMap.get(i); + name = (idsEntry != null) ? Misc.prettifySymbol(idsEntry.getSymbol()) : ""; + } + colorList.add(new ColorEntry(image, i, name)); + } + } + + if (!colorList.isEmpty()) { + fireIntervalAdded(this, 0, colorList.size() - 1); + } + + setSelectedItem(getElementAt(0)); + } + + private static synchronized void initRandomColors(boolean forced) + { + if (forced) { + randomColors.clear(); + } + + if (randomColors.isEmpty()) { + ResourceEntry randomEntry = ResourceFactory.getResourceEntry("RANDCOLR.2DA"); + + // collecting valid random color indices + if (randomEntry != null) { + Table2da table = Table2daCache.get(randomEntry); + for (int col = 1, count = table.getColCount(); col < count; col++) { + int index = Misc.toNumber(table.get(0, col), -1); + String name = Misc.prettifySymbol(table.getHeader(col)); + if (index >= 0 && index < 256) { + randomColors.put(index, name); + } + } + } + } + } + +//-------------------------- INNER CLASSES -------------------------- + + public static class ColorCellRenderer extends DefaultListCellRenderer + { + private static final Border DEFAULT_NO_FOCUS_BORDER = new EmptyBorder(1, 1, 1, 1); + + public ColorCellRenderer() + { + super(); + } + + @Override + public Component getListCellRendererComponent(JList list, Object value, int index, + boolean isSelected, boolean cellHasFocus) + { + if (value == null || !(value instanceof ColorEntry)) { + return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + } + + ColorEntry entry = (ColorEntry)value; + if (isSelected) { + setBackground(list.getSelectionBackground()); + setForeground(list.getSelectionForeground()); + } else { + setBackground(list.getBackground()); + setForeground(list.getForeground()); + } + + setText(entry.toString()); + + if (entry.getImage() != null) { + setIcon(new ImageIcon(entry.getImage())); + setIconTextGap(8); + } else { + setIcon(null); + } + + setEnabled(list.isEnabled()); + setFont(list.getFont()); + + Border border = null; + if (cellHasFocus) { + if (isSelected) { + border = UIManager.getBorder("List.focusSelectedCellHighlightBorder"); + } + if (border == null) { + border = UIManager.getBorder("List.focusCellHighlightBorder"); + } + } else { + border = UIManager.getBorder("List.cellNoFocusBorder"); + if (border == null) { + border = DEFAULT_NO_FOCUS_BORDER; + } + } + setBorder(border); + + return this; + } + } + + public static class ColorEntry implements Comparable + { + public enum State { + /** Indicates a fixed color entry. */ + FIXED, + /** Indicates a randomized color entry. */ + RANDOM, + /** Indicates a non-existing color entry. */ + INVALID, + } + + private static final int DEFAULT_IMAGE_WIDTH = 128; // total width of color range + // take global font scaling into account + private static final int DEFAULT_IMAGE_HEIGHT = 20 * BrowserMenuBar.getInstance().getGlobalFontSize() / 100; + + private static final Color COLOR_INVALID = new Color(0xe0e0e0); + private static final Color COLOR_RANDOM = Color.LIGHT_GRAY; + + private static final String LABEL_INVALID = "(Invalid)"; + private static final String LABEL_RANDOM = "(Random)"; + + private final int index; + private final Image image; + private String name; + private State state; + + /** Creates a fixed color entry. */ + public ColorEntry(BufferedImage ranges, int index, String name) + { + this.index = Math.max(0, Math.min(255, index)); + this.name = (name != null) ? name.trim() : ""; + this.image = createFixedColor(ranges, index); + } + + /** + * Creates a random color or invalid color entry. + * @param name + * @param isRandom + */ + public ColorEntry(int index, String name, boolean isRandom) + { + this.index = Math.max(0, Math.min(255, index)); + this.name = (name != null) ? name.trim() : ""; + this.image = isRandom ? createRandomColor() : createInvalidColor(); + } + + public int getIndex() { return index; } + public Image getImage() { return image; } + public String getName() { return name; } + public State getState() { return state; } + + @Override + public int compareTo(ColorEntry o) + { + return getIndex() - o.getIndex(); + } + + @Override + public String toString() + { + if (getName().isEmpty()) { + return Integer.toString(getIndex()); + } else { + return getIndex() + " (" + getName() + ")"; + } + } + + private Image createFixedColor(BufferedImage image, int index) + { + if (image == null || index < 0 || index >= image.getHeight()) { + return createInvalidColor(); + } + + state = State.FIXED; + BufferedImage range = new BufferedImage(DEFAULT_IMAGE_WIDTH, DEFAULT_IMAGE_HEIGHT, image.getType()); + Graphics2D g = range.createGraphics(); + try { + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); + g.drawImage(image, 0, 0, range.getWidth(), range.getHeight(), 0, index, image.getWidth(), index + 1, null); + g.setColor(Color.BLACK); + g.setStroke(new BasicStroke(1.0f)); + g.drawRect(0, 0, range.getWidth() - 1, range.getHeight() - 1); + } finally { + g.dispose(); + } + return range; + } + + private Image createRandomColor() { return createCustomColor(COLOR_RANDOM, LABEL_RANDOM); } + + private Image createInvalidColor() { return createCustomColor(COLOR_INVALID, LABEL_INVALID); } + + private Image createCustomColor(Color color, String label) + { + state = (color != null) ? State.RANDOM : State.INVALID; + Color col = (color != null) ? color : COLOR_INVALID; + String text = (label != null) ? label : LABEL_INVALID; + BufferedImage range = new BufferedImage(DEFAULT_IMAGE_WIDTH, DEFAULT_IMAGE_HEIGHT, BufferedImage.TYPE_INT_RGB); + Graphics2D g = range.createGraphics(); + try { + g.setColor(col); + g.fillRect(0, 0, range.getWidth(), range.getHeight()); + g.setFont(new JLabel().getFont().deriveFont(12.0f)); + g.setColor(Color.BLACK); + g.setStroke(new BasicStroke(1.0f)); + g.drawRect(0, 0, range.getWidth() - 1, range.getHeight() - 1); + if (!text.isEmpty()) { + g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_GASP); + FontMetrics fm = g.getFontMetrics(); + Rectangle2D rect = fm.getStringBounds(label, g); + g.drawString(text, + (float)(range.getWidth() - rect.getWidth()) / 2.0f, + (float)(range.getHeight() - rect.getY()) / 2.0f); + } + } finally { + g.dispose(); + } + return range; + } + } + +} diff --git a/src/org/infinity/resource/cre/browser/CreUtils.java b/src/org/infinity/resource/cre/browser/CreUtils.java new file mode 100644 index 000000000..227ec5feb --- /dev/null +++ b/src/org/infinity/resource/cre/browser/CreUtils.java @@ -0,0 +1,574 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.browser; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +import org.infinity.datatype.Flag; +import org.infinity.datatype.IsNumeric; +import org.infinity.datatype.IsReference; +import org.infinity.datatype.IsTextual; +import org.infinity.datatype.Readable; +import org.infinity.resource.AddRemovable; +import org.infinity.resource.Profile; +import org.infinity.resource.ResourceFactory; +import org.infinity.resource.StructEntry; +import org.infinity.resource.cre.CreResource; +import org.infinity.resource.cre.Item; +import org.infinity.resource.cre.decoder.util.ItemInfo; +import org.infinity.resource.key.ResourceEntry; +import org.infinity.util.Misc; + +/** + * Collection of methods for creating and manipulating CRE resources. + */ +public class CreUtils +{ + private static ByteBuffer BUFFER; + + /** Returns the animation id for the specified CRE resource. */ + public static int getAnimation(CreResource cre) + { + return ((IsNumeric)Objects.requireNonNull(cre).getAttribute(CreResource.CRE_ANIMATION)).getValue(); + } + + /** Sets a new animation id for the specified CRE resource. */ + public static void setAnimation(CreResource cre, int animId) throws IllegalArgumentException + { + if (animId >= 0 && animId <= 0xffff) { + int oldValue = getAnimation(cre); + if (oldValue != animId) { + setFieldValue(Objects.requireNonNull(cre).getAttribute(CreResource.CRE_ANIMATION), null, animId); + } + } else { + throw new IllegalArgumentException(String.format("Invalid animation id specified: 0x%04X", animId)); + } + } + + /** Returns the allegiance value for the specified CRE resource. */ + public static int getAllegiance(CreResource cre) + { + return ((IsNumeric)Objects.requireNonNull(cre).getAttribute(CreResource.CRE_ALLEGIANCE)).getValue(); + } + + /** Sets a new allegiance value for the specified CRE resource. */ + public static void setAllegiance(CreResource cre, int ea) throws IllegalArgumentException + { + if (ea >= -128 && ea <= 255) { + ea &= 0xff; + int oldValue = getAllegiance(cre); + if (oldValue != ea) { + setFieldValue(Objects.requireNonNull(cre).getAttribute(CreResource.CRE_ALLEGIANCE), null, ea); + } + } else { + throw new IllegalArgumentException("Invalid allegiance value specified: " + ea); + } + } + + /** Returns whether the panic status is enabled for the specified CRE resource. */ + public static boolean getStatusPanic(CreResource cre) + { + int status = ((IsNumeric)Objects.requireNonNull(cre).getAttribute(CreResource.CRE_STATUS)).getValue(); + return (status & (1 << 2)) != 0; + } + + /** Sets the panic status for the specified CRE resource. */ + public static void setStatusPanic(CreResource cre, boolean b) + { + int oldStatus = ((IsNumeric)Objects.requireNonNull(cre).getAttribute(CreResource.CRE_STATUS)).getValue(); + int newStatus = oldStatus; + if (b) { + newStatus |= 1 << 2; + } else { + newStatus &= ~(1 << 2); + } + if (newStatus != oldStatus) { + setFieldValue(cre.getAttribute(CreResource.CRE_STATUS), null, newStatus); + } + } + + /** Returns the color value at the specified location index for the specified CRE resource. */ + public static int getColor(CreResource cre, int locationIdx) throws IllegalArgumentException + { + if (locationIdx >= 0 && locationIdx < 7) { + StructEntry se = getColorField(cre, locationIdx); + if (se instanceof IsNumeric) { + return ((IsNumeric)se).getValue(); + } else { + throw new IllegalArgumentException("Color location at index " + locationIdx + " does not exist"); + } + } else { + throw new IllegalArgumentException("Color location index out of bounds: " + locationIdx); + } + } + + /** Sets a new color value to the field specified by the location index. */ + public static void setColor(CreResource cre, int locationIdx, int value) throws IllegalArgumentException + { + if (locationIdx >= 0 && locationIdx < 7) { + StructEntry se = getColorField(cre, locationIdx); + if (se instanceof IsNumeric) { + setFieldValue(se, null, value); + } + } else { + throw new IllegalArgumentException("Invalid color location index specified: " + locationIdx); + } + } + + /** Returns the item equipped in the helmet slot of the specified CRE resource. Returns {@code null} if no item is equipped. */ + public static ItemInfo getEquipmentHelmet(CreResource cre) + { + Objects.requireNonNull(cre); + ItemInfo retVal = null; + boolean isPST = (Profile.getGame() == Profile.Game.PSTEE) || + ((IsTextual)cre.getAttribute(CreResource.COMMON_VERSION)).getText().equalsIgnoreCase("V1.2"); + String fieldName = isPST ? CreResource.CRE_ITEM_SLOT_RIGHT_EARRING : CreResource.CRE_ITEM_SLOT_HELMET; + Item item = getEquippedItem(cre, fieldName); + if (item != null) { + ResourceEntry itemEntry = ResourceFactory.getResourceEntry(((IsReference)item.getAttribute(Item.CRE_ITEM_RESREF)).getResourceName()); + if (itemEntry != null) { + try { + retVal = ItemInfo.get(itemEntry); + retVal.overrideDroppableFlag(((Flag)item.getAttribute(Item.CRE_ITEM_FLAGS)).isFlagSet(3)); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + return retVal; + } + + /** + * Equips the given item in the helmet slot of the specified CRE resource. + * @param cre the CRE resource. + * @param item the {@code ItemInfo} object. Specify {@code null} to unequip the current helmet. + */ + public static void setEquipmentHelmet(CreResource cre, ItemInfo item) + { + Objects.requireNonNull(cre); + boolean isPST = (Profile.getGame() == Profile.Game.PSTEE) || + ((IsTextual)cre.getAttribute(CreResource.COMMON_VERSION)).getText().equalsIgnoreCase("V1.2"); + String fieldName = isPST ? CreResource.CRE_ITEM_SLOT_RIGHT_EARRING : CreResource.CRE_ITEM_SLOT_HELMET; + setEquippedItem(cre, item, fieldName); + } + + /** Returns the item equipped in the armor slot of the specified CRE resource. Returns {@code null} if no item is equipped. */ + public static ItemInfo getEquipmentArmor(CreResource cre) + { + Objects.requireNonNull(cre); + ItemInfo retVal = null; + boolean isPST = (Profile.getGame() == Profile.Game.PSTEE) || + ((IsTextual)cre.getAttribute(CreResource.COMMON_VERSION)).getText().equalsIgnoreCase("V1.2"); + String fieldName = isPST ? CreResource.CRE_ITEM_SLOT_CHEST : CreResource.CRE_ITEM_SLOT_ARMOR; + Item item = getEquippedItem(cre, fieldName); + if (item != null) { + ResourceEntry itemEntry = ResourceFactory.getResourceEntry(((IsReference)item.getAttribute(Item.CRE_ITEM_RESREF)).getResourceName()); + if (itemEntry != null) { + try { + retVal = ItemInfo.get(itemEntry); + retVal.overrideDroppableFlag(((Flag)item.getAttribute(Item.CRE_ITEM_FLAGS)).isFlagSet(3)); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + return retVal; + } + + /** + * Equips the given item in the armor slot of the specified CRE resource. + * @param cre the CRE resource. + * @param item the {@code ItemInfo} object. Specify {@code null} to unequip the current helmet. + */ + public static void setEquipmentArmor(CreResource cre, ItemInfo item) + { + Objects.requireNonNull(cre); + boolean isPST = (Profile.getGame() == Profile.Game.PSTEE) || + ((IsTextual)cre.getAttribute(CreResource.COMMON_VERSION)).getText().equalsIgnoreCase("V1.2"); + String fieldName = isPST ? CreResource.CRE_ITEM_SLOT_CHEST : CreResource.CRE_ITEM_SLOT_ARMOR; + setEquippedItem(cre, item, fieldName); + } + + /** Returns the item equipped in the shield slot of the specified CRE resource. Returns {@code null} if no item is equipped. */ + public static ItemInfo getEquipmentShield(CreResource cre) + { + Objects.requireNonNull(cre); + ItemInfo retVal = null; + String creVer = ((IsTextual)cre.getAttribute(CreResource.COMMON_VERSION)).getText(); + boolean isPST = (Profile.getGame() == Profile.Game.PSTEE) || creVer.equalsIgnoreCase("V1.2"); + boolean isIWD2 = creVer.equalsIgnoreCase("V2.2"); + String fieldName; + if (isPST) + fieldName = CreResource.CRE_ITEM_SLOT_RIGHT_TATTOO_LOWER; + else if (isIWD2) { + fieldName = String.format(CreResource.CRE_ITEM_SLOT_SHIELD_FMT, 1); + } else { + fieldName = CreResource.CRE_ITEM_SLOT_SHIELD; + } + Item item = getEquippedItem(cre, fieldName); + if (item != null) { + ResourceEntry itemEntry = ResourceFactory.getResourceEntry(((IsReference)item.getAttribute(Item.CRE_ITEM_RESREF)).getResourceName()); + if (itemEntry != null) { + try { + retVal = ItemInfo.get(itemEntry); + retVal.overrideDroppableFlag(((Flag)item.getAttribute(Item.CRE_ITEM_FLAGS)).isFlagSet(3)); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + return retVal; + } + + /** + * Equips the given item in the shield slot of the specified CRE resource. + * @param cre the CRE resource. + * @param item the {@code ItemInfo} object. Specify {@code null} to unequip the current helmet. + */ + public static void setEquipmentShield(CreResource cre, ItemInfo item) + { + Objects.requireNonNull(cre); + String creVer = ((IsTextual)cre.getAttribute(CreResource.COMMON_VERSION)).getText(); + boolean isPST = (Profile.getGame() == Profile.Game.PSTEE) || creVer.equalsIgnoreCase("V1.2"); + boolean isIWD2 = creVer.equalsIgnoreCase("V2.2"); + String fieldName; + if (isPST) + fieldName = CreResource.CRE_ITEM_SLOT_RIGHT_TATTOO_LOWER; + else if (isIWD2) { + fieldName = String.format(CreResource.CRE_ITEM_SLOT_SHIELD_FMT, 1); + } else { + fieldName = CreResource.CRE_ITEM_SLOT_SHIELD; + } + setEquippedItem(cre, item, fieldName); + } + + /** Returns the item equipped in the weapon slot of the specified CRE resource. Returns {@code null} if no item is equipped. */ + public static ItemInfo getEquipmentWeapon(CreResource cre) + { + Objects.requireNonNull(cre); + ItemInfo retVal = null; + String fieldName = String.format(CreResource.CRE_ITEM_SLOT_WEAPON_FMT, 1); + Item item = getEquippedItem(cre, fieldName); + if (item != null) { + ResourceEntry itemEntry = ResourceFactory.getResourceEntry(((IsReference)item.getAttribute(Item.CRE_ITEM_RESREF)).getResourceName()); + if (itemEntry != null) { + try { + retVal = ItemInfo.get(itemEntry); + retVal.overrideDroppableFlag(((Flag)item.getAttribute(Item.CRE_ITEM_FLAGS)).isFlagSet(3)); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + return retVal; + } + + /** + * Equips the given item in the weapon slot of the specified CRE resource. + * @param cre the CRE resource. + * @param item the {@code ItemInfo} object. Specify {@code null} to unequip the current helmet. + */ + public static void setEquipmentWeapon(CreResource cre, ItemInfo item) + { + Objects.requireNonNull(cre); + String fieldName = String.format(CreResource.CRE_ITEM_SLOT_WEAPON_FMT, 1); + setEquippedItem(cre, item, fieldName); + // selecting first weapon slot + setFieldValue(cre.getAttribute(CreResource.CRE_SELECTED_WEAPON_SLOT), null, 0); + } + + /** + * Helper method: Equips the specified item to the item slot of given name. + * A previously equipped item will be overwritten. Otherwise a new item will be added and item slot indices are updated. + * @param cre the CRE resource + * @param item the item to equip. Specify {@code null} to unequip current item without equipping a new one. + * @param slotName the item slot name + */ + private static void setEquippedItem(CreResource cre, ItemInfo item, String slotName) + { + StructEntry se = Objects.requireNonNull(cre).getAttribute(slotName); + if (se == null) { + return; + } + + if (item != null && item.getResourceEntry() != null) { + // equip new item + addEquippedItem(cre, item, slotName); + } else { + // unequip current item + removeEquippedItem(cre, slotName); + } + } + + /** + * Helper method: Updates an existing equipped item in the specified slot. + * @param cre the CRE resource + * @param item the item to equip + * @param slotName the item slot name + * @return {@code true} if item slot could be updated. {@code false} otherwise. + */ + private static boolean updateEquippedItem(CreResource cre, ItemInfo item, String slotName) + { + boolean retVal = false; + if (item == null) { + return retVal; + } + + StructEntry se = Objects.requireNonNull(cre).getAttribute(slotName); + if (se == null) { + return retVal; + } + + int idx = ((IsNumeric)se).getValue(); + int numItems = ((IsNumeric)cre.getAttribute(CreResource.CRE_NUM_ITEMS)).getValue(); + if (idx < 0 || idx >= numItems) { + return retVal; + } + + List itemList = cre.getFields(Item.class); + if (idx < itemList.size() && itemList.get(idx) instanceof Item) { + Item curItem = (Item)itemList.get(idx); + String resref = item.getResourceEntry().getResourceRef(); + setFieldValue(curItem.getAttribute(Item.CRE_ITEM_RESREF), null, resref, 8); + retVal = true; + } + + return retVal; + } + + /** + * Helper method: Adds a new equipped item and assigns it to the specified slot. + * @param cre the CRE resource + * @param item the item to equip + * @param slotName the item slot name + */ + private static void addEquippedItem(CreResource cre, ItemInfo item, String slotName) + { + if (item == null) { + return; + } + + StructEntry se = Objects.requireNonNull(cre).getAttribute(slotName); + if (se == null) { + return; + } + + int idx = ((IsNumeric)se).getValue(); + int numItems = ((IsNumeric)cre.getAttribute(CreResource.CRE_NUM_ITEMS)).getValue(); + if (idx >= 0 && idx < numItems) { + // Old item exists? Update! + if (updateEquippedItem(cre, item, slotName)) { + return; + } + } + + ByteBuffer buf = getByteBuffer(); + buf.putLong(0L); + buf.putLong(0L); + buf.putInt(1); // flags: identified + try { + Item newItem = new Item(cre, getByteBuffer(), 0, numItems); + String resref = item.getResourceEntry().getResourceRef(); + setFieldValue(newItem.getAttribute(Item.CRE_ITEM_RESREF), buf, resref, 8); + cre.addDatatype(newItem); + setFieldValue(cre.getAttribute(slotName), null, numItems); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * Helper method: Removes the currently equipped item from the specified slot. + * @param cre the CRE resource + * @param slotName the item slot name + */ + private static void removeEquippedItem(CreResource cre, String slotName) + { + StructEntry se = cre.getAttribute(slotName); + if (se == null) { + return; + } + + int idx = ((IsNumeric)se).getValue(); + if (idx < 0) { + return; + } + + // clearing item reference + setFieldValue(se, null, -1); + int numItems = ((IsNumeric)cre.getAttribute(CreResource.CRE_NUM_ITEMS)).getValue(); + if (idx < numItems) { + List itemList = cre.getFields(Item.class); + if (idx < itemList.size() && itemList.get(idx) instanceof AddRemovable) { + // removing item structure + cre.removeDatatype((AddRemovable)itemList.get(idx), false); + // updating slot indices + updateItemSlots(cre, idx + 1, -1); + } + } + } + + /** + * Helper method: Updates item slot references greater than or equal to {@code startIndex} by {@code diff}. + * @param cre the CRE resource + * @param startIndex lower bound of item index to update. + * @param diff this value is added to the item index. + */ + private static void updateItemSlots(CreResource cre, int startIndex, int diff) + { + int ofsItemSlots = Objects.requireNonNull(cre).getExtraOffset() + + ((IsNumeric)cre.getAttribute(CreResource.CRE_OFFSET_ITEM_SLOTS)).getValue(); + int numItemSlots = getItemSlotCount(cre, false); + + for (int i = 0; i < numItemSlots; i++) { + StructEntry se = cre.getAttribute(ofsItemSlots + i * 2); + int slotIdx = ((IsNumeric)se).getValue(); + if (slotIdx >= startIndex) { + slotIdx += diff; + setFieldValue(se, null, slotIdx); + } + } + } + + /** + * Helper method: Determines the number of available item slots. + * @param cre the CRE resource. + * @param includeSelectionSlots whether selected weapon slot and weapon ability should be included in the count. + * @return number of available item slots. + */ + private static int getItemSlotCount(CreResource cre, boolean includeSelectionSlots) + { + int retVal = 0; + if (cre != null) { + String ver = ((IsTextual)cre.getAttribute(CreResource.COMMON_VERSION)).getText().toUpperCase(); + switch (ver) { + case "V1.2": + retVal = 48; + break; + case "V2.2": + retVal = 52; + break; + default: + if (Profile.getGame() == Profile.Game.PSTEE) { + int numSlots = ((IsNumeric)cre.getAttribute(CreResource.CRE_NUM_ITEM_SLOTS)).getValue(); + if (numSlots > 0) { + retVal = numSlots; + } + } + if (retVal == 0) { + retVal = 40; + } + } + } + + if (!includeSelectionSlots) { + retVal -= 2; + } + + return retVal; + } + + /** Helper method: Returns the item structure referenced by the specified item slot. */ + private static Item getEquippedItem(CreResource cre, String slotName) + { + Item retVal = null; + int idx = ((IsNumeric)Objects.requireNonNull(cre).getAttribute(Objects.requireNonNull(slotName))).getValue(); + if (idx >= 0) { + int numItems = ((IsNumeric)cre.getAttribute(CreResource.CRE_NUM_ITEMS)).getValue(); + if (idx < numItems) { + List itemList = cre.getFields(Item.class); + if (idx < itemList.size() && itemList.get(idx) instanceof Item) { + retVal = (Item)itemList.get(idx); +// retVal = ResourceFactory.getResourceEntry(((IsReference)item.getAttribute(Item.CRE_ITEM_RESREF)).getResourceName()); + } + } + } + return retVal; + } + + + /** Helper method: Returns the specified color field. */ + private static StructEntry getColorField(CreResource cre, int locationIdx) + { + StructEntry retVal = null; + if (locationIdx >= 0 && locationIdx < 7) { + boolean isV12 = ((IsTextual)Objects.requireNonNull(cre).getAttribute(CreResource.COMMON_VERSION)).getText().equalsIgnoreCase("V1.2"); + int ofsBase = isV12 ? 0x2e4 : 0x2c; + int size = isV12 ? 2 : 1; + retVal = cre.getAttribute(ofsBase + locationIdx * size); + } + return retVal; + } + + /** + * Attempts to write the specified numeric value into the given field. + * @param field the {@code Readable} instance to update. + * @param buf the {@code ByteBuffer} object to use as temporary storage. + * @param value the value to set. + * @return number of bytes written. + */ + private static int setFieldValue(Readable field, ByteBuffer buf, int value) + { + int retVal = 0; + if (field != null) { + if (buf == null) { + buf = getByteBuffer(); + } + int pos = buf.position(); + buf.putInt(value); + try { + retVal = field.read(buf, pos); + } catch (Exception e) { + e.printStackTrace(); + } + } + return retVal; + } + + /** + * Attempts to write the specified textual value into the given field. + * @param field the {@code Readable} instance to update. + * @param buf the {@code ByteBuffer} object to use as temporary storage. + * @param value the value to set. + * @param maxLen the maximum number of characters to write. String is truncated or padded to fit the required length. + * @return number of bytes written. + */ + private static int setFieldValue(Readable field, ByteBuffer buf, String value, int maxLen) + { + int retVal = 0; + if (field != null) { + maxLen = Math.max(0, Math.min(getByteBuffer().limit(), maxLen)); + byte[] arr = Arrays.copyOfRange(value.getBytes(Misc.CHARSET_DEFAULT), 0, maxLen); + if (buf == null) { + buf = getByteBuffer(); + } + int pos = buf.position(); + buf.put(arr); + try { + retVal = field.read(buf, pos); + } catch (Exception e) { + e.printStackTrace(); + } + } + return retVal; + } + + /** Returns a {@code ByteBuffer} instance for temporary storage with little endian byte order and position set to 0. */ + private static ByteBuffer getByteBuffer() + { + if (BUFFER == null) { + BUFFER = ByteBuffer.allocate(1024); + BUFFER.order(ByteOrder.LITTLE_ENDIAN); + } + BUFFER.position(0); + return BUFFER; + } + + private CreUtils() { } +} diff --git a/src/org/infinity/resource/cre/browser/CreatureAnimationModel.java b/src/org/infinity/resource/cre/browser/CreatureAnimationModel.java new file mode 100644 index 000000000..310b781c2 --- /dev/null +++ b/src/org/infinity/resource/cre/browser/CreatureAnimationModel.java @@ -0,0 +1,324 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.browser; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Objects; +import java.util.stream.IntStream; + +import javax.swing.AbstractListModel; +import javax.swing.MutableComboBoxModel; + +import org.infinity.util.IdsMap; +import org.infinity.util.IdsMapCache; +import org.infinity.util.IdsMapEntry; + +/** + * {@code ComboBoxModel} for the creature animation combo box used in the Creature Animation Browser. + */ +public class CreatureAnimationModel extends AbstractListModel + implements MutableComboBoxModel +{ + private final List animationList = new ArrayList<>(65536); + + private Object selectedItem; + + public CreatureAnimationModel() + { + super(); + init(); + } + + public void reload() + { + init(); + } + + /** + * Returns the numeric value associated with the selected item. + * It returns the animation id for a regular entry from the model. + * It attempts to detect and return a manually entered number. Use prefix "0x" or suffix "h" to indicate that + * number is entered in hexadecimal notation. + * @return the numeric value of the selected item. Returns -1 if numeric value could not be determined. + */ + public int getSelectedValue() + { + return parseValue(getSelectedItem()); + } + + /** + * Returns the index-position of the specified object in the list. + * @param anItem a {@code AnimateEntry} object, {@code Number} object or {@code String} object. + * @return an int representing the index position, where 0 is the first position. Returns -1 + * if the item could not be found in the list. + */ + public int getIndexOf(Object anItem) + { + if (anItem instanceof AnimateEntry) { + return animationList.indexOf(anItem); + } else { + int idx = -1; + if (anItem instanceof Number) { + idx = ((Number)anItem).intValue(); + } else { + idx = parseValue(anItem); + } + if (idx != -1) { + final int aidx = idx; + return IntStream + .range(0, animationList.size()) + .filter(i -> aidx == animationList.get(i).getValue()) + .findAny() + .orElse(-1); + } + } + + if (anItem != null) { + final String aname = anItem.toString().toUpperCase(Locale.ENGLISH); + return IntStream + .range(0, animationList.size()) + .filter(i -> animationList.get(i).getSymbol().toUpperCase(Locale.ENGLISH).startsWith(aname)) + .findAny() + .orElse(-1); + } + return -1; + } + + /** Empties the list. */ + public void removeAllElements() + { + if (!animationList.isEmpty()) { + int oldSize = animationList.size(); + animationList.clear(); + selectedItem = null; + if (oldSize > 0) { + fireIntervalRemoved(this, 0, oldSize - 1); + } + } else { + selectedItem = null; + } + } + +//--------------------- Begin Interface ListModel --------------------- + + @Override + public int getSize() + { + return animationList.size(); + } + + + @Override + public AnimateEntry getElementAt(int index) + { + if (index >= 0 && index < animationList.size()) { + return animationList.get(index); + } else { + return null; + } + } + +//--------------------- End Interface ListModel --------------------- + +//--------------------- Begin Interface ComboBoxModel --------------------- + + @Override + public void setSelectedItem(Object anItem) + { + if ((selectedItem != null && !selectedItem.equals(anItem)) || + selectedItem == null && anItem != null) { + selectedItem = anItem; + fireContentsChanged(this, -1, -1); + } + } + + + @Override + public Object getSelectedItem() + { + return selectedItem; + } + +//--------------------- End Interface ComboBoxModel --------------------- + +//--------------------- Begin Interface MutableComboBoxModel --------------------- + + @Override + public void addElement(AnimateEntry item) + { + animationList.add(Objects.requireNonNull(item)); + fireIntervalAdded(this, animationList.size() - 1, animationList.size() - 1); + if (animationList.size() == 1 && selectedItem == null) { + setSelectedItem(item); + } + } + + + @Override + public void removeElement(Object obj) + { + int index = animationList.indexOf(obj); + if (index != -1) { + removeElementAt(index); + } + } + + + @Override + public void insertElementAt(AnimateEntry item, int index) + { + animationList.add(index, Objects.requireNonNull(item)); + fireIntervalAdded(this, index, index); + } + + + @Override + public void removeElementAt(int index) + { + if (getElementAt(index) == selectedItem) { + if (index == 0) { + setSelectedItem(getSize() == 1 ? null : getElementAt(index + 1)); + } else { + setSelectedItem(getElementAt(index - 1)); + } + } + animationList.remove(index); + fireIntervalRemoved(this, index, index); + } + +//--------------------- End Interface MutableComboBoxModel --------------------- + + /** + * Helper method: Attempts to convert the specified argument into a meaningful numeric value. + * An {@code AnimateEntry} object returns the associted animation id. + * A string is parsed and returns as decimal or hexadecimal number. + * Returns -1 if argument could not be converted. + */ + public int parseValue(Object o) + { + int retVal = -1; + if (o instanceof AnimateEntry) { + retVal = ((AnimateEntry)o).getValue(); + } else if (o instanceof Number) { + retVal = ((Number)o).intValue(); + } else if (o != null) { + String s = o.toString().trim().toLowerCase().replaceAll("\\s+", ""); + try { + // preserve sign + int factor = 1; + if (s.startsWith("-")) { + factor = -1; + s = s.substring(1); + } + + // determine base + int base = 10; + if (s.startsWith("0x")) { + base = 16; + s = s.substring(2); + } + if (s.endsWith("h")) { + base = 16; + s = s.substring(0, s.length() - 1); + } + + retVal = Integer.parseInt(s, base) * factor; + } catch (Exception e) { + } + } + return retVal; + } + + private void init() + { + removeAllElements(); + + IdsMap map = IdsMapCache.get("ANIMATE.IDS"); + if (map != null) { + for (Iterator iter = map.getAllValues().iterator(); iter.hasNext(); ) { + IdsMapEntry entry = iter.next(); + animationList.add(new AnimateEntry((int)entry.getID(), entry.getSymbol())); + } + } + Collections.sort(animationList); + if (!animationList.isEmpty()) { + fireIntervalAdded(this, 0, animationList.size() - 1); + } + + setSelectedItem(getElementAt(0)); + } + +//-------------------------- INNER CLASSES -------------------------- + + public static class AnimateEntry implements Comparable + { + private final int id; + private final String name; + + public AnimateEntry(int animId, String name) + { + this.id = Math.max(0, Math.min(0xffff, animId)); + this.name = (name != null) ? name : ""; + } + + /** Returns the numeric animation id. */ + public int getValue() { return id; } + + /** Returns the symbolic animation id if available. Returns empty string otherwise. */ + public String getSymbol() + { + if (name.isEmpty()) { + String idString = Integer.toString(getValue(), 16).toUpperCase(Locale.ENGLISH); + switch (idString.length()) { + case 1: + idString = "000" + idString; + break; + case 2: + idString = "00" + idString; + break; + case 3: + idString = "0" + idString; + break; + default: + } + return "UNKNOWN_" + idString; + } else { + return name; + } + } + + @Override + public int compareTo(AnimateEntry o) + { + // sort by symbolic name + return toString().compareTo(o.toString()); + } + + @Override + public String toString() + { + String idString = Integer.toString(getValue(), 16).toUpperCase(Locale.ENGLISH); + switch (idString.length()) { + case 1: + idString = "0x000" + idString; + break; + case 2: + idString = "0x00" + idString; + break; + case 3: + idString = "0x0" + idString; + break; + default: + idString = "0x" + idString; + } + + return getSymbol() + " - " + idString; + } + } +} diff --git a/src/org/infinity/resource/cre/browser/CreatureBrowser.java b/src/org/infinity/resource/cre/browser/CreatureBrowser.java new file mode 100644 index 000000000..c8952adae --- /dev/null +++ b/src/org/infinity/resource/cre/browser/CreatureBrowser.java @@ -0,0 +1,370 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.browser; + +import java.awt.BorderLayout; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.Objects; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ExecutionException; + +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.ProgressMonitor; +import javax.swing.SwingWorker; +import javax.swing.border.EtchedBorder; + +import org.infinity.NearInfinity; +import org.infinity.gui.Center; +import org.infinity.gui.ChildFrame; +import org.infinity.gui.WindowBlocker; +import org.infinity.icon.Icons; +import org.infinity.resource.cre.CreResource; +import org.infinity.resource.cre.decoder.SpriteDecoder; +import org.infinity.resource.cre.decoder.util.SpriteUtils; + +/** + * The Creature Browser implements a highly customizable browser and viewer for creature animations. + */ +public class CreatureBrowser extends ChildFrame +{ + private final ConcurrentLinkedQueue actionQueue = new ConcurrentLinkedQueue<>(); + private final Listeners listeners = new Listeners(); + + private CreatureControlPanel panelCreature; + private SettingsPanel panelSettings; + private MediaPanel panelMedia; + private RenderPanel panelCanvas; + private CreResource cre; + private SwingWorker worker; + + /** + * Creates an instance of the creature browser with a (virtual) default creature. + */ + public CreatureBrowser() + { + this(null); + } + + /** + * Creates an instance of the creature browser and loads the specified CRE resource. + * @param cre the CRE resource to load + */ + public CreatureBrowser(CreResource cre) + { + super(""); + init(); + setCreResource(cre); + } + + /** Returns the currently active CRE resource. */ + public CreResource getCreResource() + { + return cre; + } + + /** + * Discards the current creature data and loads the specified creature. + * @param cre the CRE resource to load. Specify {@code null} to load the (virtual) default creature. + */ + public void setCreResource(CreResource cre) + { + if ((this.cre == null && cre != null) || + this.cre != null && !this.cre.equals(cre) || + cre == null) { + this.cre = cre; + performBackgroundTask(this::taskSetCreResource, this::postTaskDefault, true); + } + } + + /** Returns the active {@code SpriteDecoder} instance. Returns {@code null} otherwise. */ + public SpriteDecoder getDecoder() { return getControlPanel().getControlModel().getDecoder(); } + + /** Returns the attached {@code CreatureControlPanel} instance. */ + public CreatureControlPanel getControlPanel() { return panelCreature; } + + /** Returns the attached {@code SettingsPanel} instance. */ + public SettingsPanel getSettingsPanel() { return panelSettings; } + + public AttributesPanel getAttributesPanel() { return getSettingsPanel().getAttributesPanel(); } + + /** Returns the attached {@code MediaPanel} instance. */ + public MediaPanel getMediaPanel() { return panelMedia; } + + /** Returns the attached {@code RenderPanel} instance. */ + public RenderPanel getRenderPanel() { return panelCanvas; } + + /** Shows an error dialog with the specified message. */ + public void showErrorMessage(String msg, String title) + { + if (msg == null || msg.isEmpty()) { + msg = "An error has occurred."; + } + if (title == null || title.isEmpty()) { + title = "Error"; + } + JOptionPane.showMessageDialog(this, msg, title, JOptionPane.ERROR_MESSAGE); + } + +//--------------------- Begin Class ChildFrame --------------------- + + @Override + protected boolean windowClosing(boolean forced) throws Exception + { + getMediaPanel().pause(); + cleanup(); + return true; + } + +//--------------------- End Class ChildFrame --------------------- + + private void init() + { + setIconImages(NearInfinity.getInstance().getIconImages()); + + // *** CRE customization panel *** + panelCreature = new CreatureControlPanel(this); + panelCreature.setBorder(new EtchedBorder()); + + // *** Settings panel *** + panelSettings = new SettingsPanel(this); + panelSettings.setBorder(new EtchedBorder()); + + // *** Animation viewer panel *** + panelCanvas = new RenderPanel(this); + + JPanel viewerPanel = new JPanel(new BorderLayout()); + viewerPanel.setBorder(new EtchedBorder()); + viewerPanel.add(panelCanvas, BorderLayout.CENTER); + + // *** Controls panel *** + // sub panel for playback controls + panelMedia = new MediaPanel(this); + panelMedia.setBorder(new EtchedBorder()); + + // *** Top-level viewer panel *** + JPanel viewerMainPanel = new JPanel(new BorderLayout()); + viewerMainPanel.add(viewerPanel, BorderLayout.CENTER); + viewerMainPanel.add(panelMedia, BorderLayout.PAGE_END); + + // main panel to hold settings panel and main viewer panel + JPanel mainCentralPanel = new JPanel(new BorderLayout()); + mainCentralPanel.add(panelSettings, BorderLayout.LINE_END); + mainCentralPanel.add(viewerMainPanel, BorderLayout.CENTER); + + getContentPane().setLayout(new BorderLayout()); + add(panelCreature, BorderLayout.PAGE_START); + add(mainCentralPanel, BorderLayout.CENTER); + + addComponentListener(listeners); + setIconImage(Icons.getImage(Icons.ICON_CRE_VIEWER_24)); + setTitle("Creature Animation Browser"); + setSize(NearInfinity.getInstance().getPreferredSize()); + Center.center(this, NearInfinity.getInstance().getBounds()); + setExtendedState(NearInfinity.getInstance().getExtendedState() & ~ICONIFIED); + setVisible(true); + } + + private void cleanup() + { + SpriteUtils.clearCache(); + } + + /** Background task: Loads the selected creature and initializes the browser. */ + private Object taskSetCreResource() throws Exception + { + ProgressMonitor pm = new ProgressMonitor(this, "Initializing controls...", " ", 0, 2); + pm.setMillisToDecideToPopup(0); + pm.setMillisToPopup(0); + try { + pm.setProgress(1); + getControlPanel().getControlModel().setSelectedCreature(getCreResource()); + getControlPanel().getControlModel().resetModified(); + getSettingsPanel().reset(); + getMediaPanel().reset(false); + pm.setProgress(2); + } finally { + pm.close(); + } + return null; + } + + /** A generic catch-all operation that can be used to evaluate exceptions thrown in a background task. */ + private void postTaskDefault(Object o, Exception e) + { + if (e != null) { + e.printStackTrace(); + JOptionPane.showMessageDialog(this, "Error: " + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); + } + } + + /** + * A method that performs a lengthy GUI-interaction task in a background thread. + * @param action a {@link Task} to execute in the background. + * @param postAction an optional {@link PostTask} that is executed when the + * background task is completed. Return value of the {@code Task} as well as + * unhandled exceptions thrown in "action" are passed to the {@code PostTask}. + * @param block whether user interaction in the window is blocked during execution of the background thread. + */ + public synchronized void performBackgroundTask(Task action, PostTask postAction, boolean block) + { + actionQueue.add(new TaskInfo(Objects.requireNonNull(action), postAction, block)); + performBackgroundTask(); + } + + private synchronized void performBackgroundTask() + { + final TaskInfo taskInfo = actionQueue.poll(); + if (worker == null && taskInfo != null) { + worker = new SwingWorker() { + @Override + protected TaskInfo doInBackground() throws Exception + { + TaskInfo retVal = taskInfo; + WindowBlocker blocker = null; + try { + if (retVal.blockWindow) { + blocker = new WindowBlocker(CreatureBrowser.this); + blocker.setBlocked(true); + } + retVal.result = retVal.action.get(); + } catch (Exception e) { + if (retVal.postAction != null) { + retVal.exception = e; + } else { + e.printStackTrace(); + } + } finally { + if (blocker != null) { + blocker.setBlocked(false); + blocker = null; + } + } + return retVal; + } + }; + worker.addPropertyChangeListener(listeners); + worker.execute(); + } + } + +//-------------------------- INNER CLASSES -------------------------- + + private class Listeners implements PropertyChangeListener, ComponentListener + { + public Listeners() + { + } + + //--------------------- Begin Interface PropertyChangeListener --------------------- + + @Override + public void propertyChange(PropertyChangeEvent event) + { + if (event.getSource() == worker) { + if ("state".equals(event.getPropertyName()) && + SwingWorker.StateValue.DONE == event.getNewValue()) { + TaskInfo retVal = null; + try { + retVal = worker.get(); + } catch (ExecutionException | InterruptedException e) { + } + if (retVal != null) { + if (retVal.postAction != null) { + retVal.postAction.accept(retVal.result, retVal.exception); + } + } + worker = null; + performBackgroundTask(); + } + } + } + + //--------------------- End Interface PropertyChangeListener --------------------- + + //--------------------- Begin Interface ComponentListener --------------------- + + @Override + public void componentResized(ComponentEvent e) + { + } + + @Override + public void componentMoved(ComponentEvent e) + { + } + + @Override + public void componentShown(ComponentEvent e) + { + } + + @Override + public void componentHidden(ComponentEvent e) + { + getMediaPanel().pause(); + } + + //--------------------- End Interface ComponentListener --------------------- + } + + /** Represents a supplier of results. */ + public static interface Task + { + /** Gets a result. */ + Object get() throws Exception; + } + + /** + * Represents an operation that accepts two arguments: + * {@code Object} returned by and potential {@code Exception} thrown in the previous {@code Task} operation. + */ + public static interface PostTask + { + void accept(Object o, Exception e); + + default PostTask andThen(PostTask after) { + Objects.requireNonNull(after); + + return (o, e) -> { + accept(o, e); + after.accept(o, e); + }; + } + + } + + /** + * Helper structure for data related to executing tasks in the background. + */ + private static class TaskInfo + { + /** Task executed in background thread. */ + public final Task action; + /** + * Optional task executed when {@code action} thread completed. + * Parameters: {@code Object} is the result of the {@code action} task, + * {@code Exception} is an exception thrown in the {@code action} task. + */ + public final PostTask postAction; + /** Indicates whether window should be blocked during background thread execution. */ + public final boolean blockWindow; + + /** The return value of {@code action}. */ + public Object result; + /** Unhandled exceptions thrown in the background task are forwarded to the post action task. */ + public Exception exception; + + public TaskInfo(Task action, PostTask postAction, boolean block) + { + this.action = Objects.requireNonNull(action); + this.postAction = postAction; + this.blockWindow = block; + } + } +} diff --git a/src/org/infinity/resource/cre/browser/CreatureControlModel.java b/src/org/infinity/resource/cre/browser/CreatureControlModel.java new file mode 100644 index 000000000..6fa1a2025 --- /dev/null +++ b/src/org/infinity/resource/cre/browser/CreatureControlModel.java @@ -0,0 +1,599 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.browser; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; + +import org.infinity.resource.Profile; +import org.infinity.resource.cre.CreResource; +import org.infinity.resource.cre.browser.ColorSelectionModel.ColorEntry; +import org.infinity.resource.cre.browser.CreatureAnimationModel.AnimateEntry; +import org.infinity.resource.cre.browser.CreatureSelectionModel.CreatureItem; +import org.infinity.resource.cre.browser.CreatureStatusModel.StatusEntry; +import org.infinity.resource.cre.decoder.SpriteDecoder; +import org.infinity.resource.cre.decoder.util.CreatureInfo; +import org.infinity.resource.cre.decoder.util.ItemInfo; +import org.infinity.resource.cre.decoder.util.ItemInfo.ItemPredicate; +import org.infinity.resource.key.ResourceEntry; + +/** + * This model controls the relationships between creature controls and provides access to the various + * creature-specific options. + */ +public class CreatureControlModel +{ + private final List colorModels = new ArrayList<>(); + private final CreatureControlPanel panel; + + private CreatureSelectionModel modelCreSelection; + private CreatureAnimationModel modelCreAnimation; + private CreatureStatusModel modelCreAllegiance; + private ItemSelectionModel modelItemHelmet; + private ItemSelectionModel modelItemArmor; + private ItemSelectionModel modelItemShield; + private ItemSelectionModel modelItemWeapon; + private int hashCreature; + private boolean canApply, canReset; + + private SpriteDecoder decoder; + + /** + * Creates a new {@code CreatureControlModel} object. + * @param panel the {@code CreatureControlPanel} instance associated with this model. + */ + public CreatureControlModel(CreatureControlPanel panel) + { + super(); + this.panel = Objects.requireNonNull(panel); + init(); + } + + /** + * Returns the {@link SpriteDecoder} instance associated with the currently selected creature animation. + * Returns {@code null} if no creature resource has been selected. + */ + public SpriteDecoder getDecoder() { return decoder; } + + /** Recreates the {@code SpriteDecoder} instance with the specified {@code CreResource}. */ + public void resetDecoder(CreResource cre) throws Exception + { + decoder = SpriteDecoder.importSprite(cre); + } + + /** + * Selects the specified CRE resource and initializes related fields. + * @param entry {@code ResourceEntry} instance of the creature + * @throws Exception if creature resource could not be initialized + */ + public void setSelectedCreature(ResourceEntry entry) throws Exception + { + if (getModelCreature().getSize() == 0) { + getModelCreature().reload(); + } + + int idx = Math.max(0, getModelCreature().getIndexOf(entry)); + getModelCreature().setSelectedItem(getModelCreature().getElementAt(idx)); + creSelectionChanged(); + } + + /** + * Selects the specified CRE resource and initializes related fields. + * @param entry {@code CreResource} instance of the creature + * @throws Exception if creature resource could not be initialized + */ + public void setSelectedCreature(CreResource cre) throws Exception + { + Object selection = cre; + if (selection == null) { + selection = CreatureSelectionModel.CreatureItem.getDefault(); + } + + if (getModelCreature().getSize() == 0) { + getModelCreature().reload(); + } + + int idx = getModelCreature().getIndexOf(selection); + if (idx >= 0) { + getModelCreature().setSelectedItem(getModelCreature().getElementAt(idx)); + } else { + getModelCreature().setSelectedItem(selection); + } + creSelectionChanged(); + } + + /** Selects the specified creature animation and updates related fields. */ + public void setSelectedAnimation(int value) + { + if (getModelAnimation().getSize() == 0) { + getModelAnimation().reload(); + } + + int idx = getModelAnimation().getIndexOf(Integer.valueOf(value)); + if (idx >= 0) { + getModelAnimation().setSelectedItem(getModelAnimation().getElementAt(idx)); + } else { + getModelAnimation().setSelectedItem(new AnimateEntry(value, "(Unknown)")); + } + creAnimationChanged(); + } + + /** Selects the specified creature allegiance and updates related fields. */ + public void setSelectedAllegiance(int value) + { + if (getModelAllegiance().getSize() == 0) { + getModelAllegiance().reload(); + } + + int idx = Math.max(0, getModelAllegiance().getIndexOf(Integer.valueOf(value))); + getModelAllegiance().setSelectedItem(getModelAllegiance().getElementAt(idx)); + creAllegianceChanged(); + } + + /** Selects the specified helmet item and updates related fields. */ + public void setSelectedHelmet(ItemInfo item) + { + if (getModelHelmet().getSize() == 0) { + getModelHelmet().reload(); + } + + int idx = Math.max(0, getModelHelmet().getIndexOf(item)); + getModelHelmet().setSelectedItem(getModelHelmet().getElementAt(idx)); + itemHelmetChanged(); + } + + /** Selects the specified armor item and updates related fields. */ + public void setSelectedArmor(ItemInfo item) + { + if (getModelArmor().getSize() == 0) { + getModelArmor().reload(); + } + + int idx = Math.max(0, getModelArmor().getIndexOf(item)); + getModelArmor().setSelectedItem(getModelArmor().getElementAt(idx)); + itemArmorChanged(); + } + + /** + * Selects the specified shield or left-handed weapon item and updates related fields. + */ + public void setSelectedShield(ItemInfo item) + { + if (getModelShield().getSize() == 0) { + getModelShield().reload(); + } + + int idx = Math.max(0, getModelShield().getIndexOf(item)); + getModelShield().setSelectedItem(getModelShield().getElementAt(idx)); + itemShieldChanged(); + } + + /** + * Selects the specified weapon item and updates related fields. + * This method should be called BEFORE {@link #setSelectedShield(ItemInfo)} to ensure + * shield item list is compatible with the selected weapon. + */ + public void setSelectedWeapon(ItemInfo item) + { + if (getModelWeapon().getSize() == 0) { + getModelWeapon().reload(); + } + + int idx = Math.max(0, getModelWeapon().getIndexOf(item)); + getModelWeapon().setSelectedItem(getModelWeapon().getElementAt(idx)); + itemWeaponChanged(); + } + + /** + * Selects the specified color entry for the given location. + * @param index the color location index + * @param color the color value + */ + public void setSelectedColor(int index, int color) + { + final ColorSelectionModel model = getModelColor(index); + if (model == null) { + return; + } + + if (model.getSize() == 0) { + model.reload(); + } + + int idx = model.getIndexOf(Integer.valueOf(color)); + model.setSelectedItem(model.getElementAt(idx)); + colorChanged(index); + } + + /** Returns the control panel associated with the model. */ + public CreatureControlPanel getPanel() + { + return panel; + } + + /** Returns the model of the creature selection combobox. */ + public CreatureSelectionModel getModelCreature() + { + return modelCreSelection; + } + + /** Returns the model of the creature animation combobox. */ + public CreatureAnimationModel getModelAnimation() + { + return modelCreAnimation; + } + + /** Returns the model of the creature allegiance combobox. */ + public CreatureStatusModel getModelAllegiance() + { + return modelCreAllegiance; + } + + /** Returns the model of the helmet combobox. */ + public ItemSelectionModel getModelHelmet() + { + return modelItemHelmet; + } + + /** Returns the model of the armor combobox. */ + public ItemSelectionModel getModelArmor() + { + return modelItemArmor; + } + + /** Returns the model of the shield combobox. */ + public ItemSelectionModel getModelShield() + { + return modelItemShield; + } + + /** Returns the model of the weapon combobox. */ + public ItemSelectionModel getModelWeapon() + { + return modelItemWeapon; + } + + /** Returns the model of the specified color combobox. */ + public ColorSelectionModel getModelColor(int index) + { + if (index >= 0 && index < colorModels.size()) { + return colorModels.get(index); + } + return null; + } + + /** Returns an iterator over the list of color models. */ + public Iterator getColorModelIterator() + { + return colorModels.iterator(); + } + + /** + * Returns the {@code CreatureItem} instance of the currently selected CRE resource. + * Returns {@code null} if no creature is selected. + */ + public CreatureItem getSelectedCreature() + { + if (modelCreSelection != null && modelCreSelection.getSelectedItem() instanceof CreatureItem) { + return (CreatureItem)modelCreSelection.getSelectedItem(); + } else { + return null; + } + } + + /** + * Returns the {@code AnimateEntry} instance of the currently selected creature animation. + * Returns {@code null} if valid entry is not available. + */ + public AnimateEntry getSelectedAnimation() + { + AnimateEntry retVal = null; + if (modelCreAnimation != null && modelCreAnimation.getSelectedItem() != null) { + Object o = modelCreAnimation.getSelectedItem(); + if (o instanceof AnimateEntry) { + retVal = (AnimateEntry)o; + } else { + int value = modelCreAnimation.parseValue(o); + if (value >= 0) { + retVal = new AnimateEntry(value, "(Unknown)"); + } + } + } + return retVal; + } + + /** + * Returns the {@code AnimateEntry} instance of the currently selected creature allegiance. + * Returns {@code null} if entry is not available. + */ + public StatusEntry getSelectedAllegiance() + { + if (modelCreAllegiance != null && modelCreAllegiance.getSelectedItem() instanceof StatusEntry) { + return (StatusEntry)modelCreAllegiance.getSelectedItem(); + } else { + return null; + } + } + + /** + * Returns the {@code ItemInfo} instance of the currently equipped helmet. + * Returns {@code null} if entry is not available. + */ + public ItemInfo getSelectedHelmet(ItemSelectionModel model) + { + return getItem(modelItemHelmet); + } + + /** + * Returns the {@code ItemInfo} instance of the currently equipped armor. + * Returns {@code null} if entry is not available. + */ + public ItemInfo getSelectedArmor(ItemSelectionModel model) + { + return getItem(modelItemArmor); + } + + /** + * Returns the {@code ItemInfo} instance of the currently equipped shield. + * Returns {@code null} if entry is not available. + */ + public ItemInfo getSelectedShield(ItemSelectionModel model) + { + return getItem(modelItemShield); + } + + /** + * Returns the {@code ItemInfo} instance of the currently equipped weapon. + * Returns {@code null} if entry is not available. + */ + public ItemInfo getSelectedWeapon(ItemSelectionModel model) + { + return getItem(modelItemWeapon); + } + + /** + * Returns the {@code ColorEntry} instance of the specified color location. + * Returns {@code null} if entry is not available. + * @param index color location index (range: [0, 7]) + */ + public ColorEntry getSelectedColor(int index) + { + try { + ColorSelectionModel model = colorModels.get(index); + if (model != null && model.getSelectedItem() instanceof ColorEntry) { + return (ColorEntry)model.getSelectedItem(); + } + } catch (IndexOutOfBoundsException e) { + } + return null; + } + + /** This method updates relevant settings when creature resource selection has changed. */ + public void creSelectionChanged() throws Exception + { + Object item = getModelCreature().getSelectedItem(); + if (item != null) { + CreResource cre; + if (item instanceof CreatureItem) { + cre = new CreResource(((CreatureItem)item).getResourceEntry()); + } else if (item instanceof CreResource) { + cre = (CreResource)item; + } else if (item instanceof ResourceEntry) { + cre = new CreResource((ResourceEntry)item); + } else { + throw new IllegalArgumentException("No valid creature resource selected"); + } + + resetDecoder(cre); + hashCreature = decoder.getCreResource().hashCode(); + final CreatureInfo creInfo = getDecoder().getCreatureInfo(); + + // setting animation + int animId = creInfo.getAnimationId(); + setSelectedAnimation(animId); + + // setting allegiance + int ea = creInfo.getAllegiance(); + setSelectedAllegiance(ea); + + // setting equipped helmet + ItemInfo helmet = creInfo.getEquippedHelmet(); + setSelectedHelmet(helmet); + + // setting equipped armor + ItemInfo armor = creInfo.getEquippedArmor(); + setSelectedArmor(armor); + + // setting equipped main-hand weapon + ItemInfo weapon = creInfo.getEquippedWeapon(); + setSelectedWeapon(weapon); + + // setting equipped shield or off-hand weapon + ItemInfo shield = creInfo.getEquippedShield(); + setSelectedShield(shield); + + // setting colors + String[] labels = CreatureControlPanel.createColorLabels(getDecoder()); + getPanel().setColorLabels(labels); + + for (int i = 0, numColors = creInfo.getColorCount(); i < colorModels.size(); i++) { + int value = (i < numColors) ? creInfo.getColorValue(i) : -1; + setSelectedColor(i, value); + } + } + + setModified(); + } + + /** This method updates relevant settings when creature animation selection has changed. */ + public void creAnimationChanged() + { + // nothing to do + setModified(); + } + + /** This method updates relevant settings when creature allegiance selection has changed. */ + public void creAllegianceChanged() + { + // nothing to do + setModified(); + } + + public void crePanicChanged() + { + // nothing to do + setModified(); + } + + /** This method updates relevant settings when helmet equipment has changed. */ + public void itemHelmetChanged() + { + // nothing to do + setModified(); + } + + /** This method updates relevant settings when armor equipment has changed. */ + public void itemArmorChanged() + { + // nothing to do + setModified(); + } + + /** This method updates relevant settings when shield/left-handed weapon equipment has changed. */ + public void itemShieldChanged() + { + // nothing to do + setModified(); + } + + /** This method updates relevant settings when weapon equipment has changed. */ + public void itemWeaponChanged() + { + // update shield equipment list depending on selected weapon type + if (getModelWeapon().getSelectedItem() instanceof ItemInfo) { + // determining valid filters for shield slot + ItemInfo info = (ItemInfo)getModelWeapon().getSelectedItem(); + boolean isMelee = (info.getAbilityCount() > 0) && (info.getAbility(0).getAbilityType() == 1); + boolean isTwoHanded = (info.getFlags() & (1 << 1)) != 0; + isTwoHanded |= Profile.isEnhancedEdition() && ((info.getFlags() & (1 << 12)) != 0); + ItemPredicate shieldPred = null; + if (!isTwoHanded) { + shieldPred = (shieldPred == null) ? ItemInfo.FILTER_SHIELD : shieldPred.or(ItemInfo.FILTER_SHIELD); + if (isMelee) { + shieldPred = (shieldPred == null) ? ItemInfo.FILTER_WEAPON_MELEE_LEFT_HANDED : shieldPred.or(ItemInfo.FILTER_WEAPON_MELEE_LEFT_HANDED); + } + } + if (shieldPred == null) { + shieldPred = ItemInfo.FILTER_NONE; + } + + // updating item list of shield slot + Object oldItem = getModelShield().getSelectedItem(); + getModelShield().setFilter(shieldPred); + getModelShield().reload(); + int idx = Math.max(0, getModelShield().getIndexOf(oldItem)); + getModelShield().setSelectedItem(getModelShield().getElementAt(idx)); + } + setModified(); + } + + /** This method updates relevant settings when specified color entry has changed. */ + public void colorChanged(int index) + { + // nothing to do + setModified(); + } + + /** Resets settings to the defaults as defined by the currently selected creature resource. */ + public void reset() + { + try { + creSelectionChanged(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** Returns whether the specified color entry is a random color. */ + public boolean isColorRandom(int index) + { + boolean retVal = false; + if (index >= 0 && index < colorModels.size()) { + Object item = colorModels.get(index).getSelectedItem(); + if (item instanceof ColorSelectionModel.ColorEntry) { + retVal = (((ColorSelectionModel.ColorEntry)item).getState() == ColorSelectionModel.ColorEntry.State.RANDOM); + } + } + return retVal; + } + + /** Returns whether settings have been modified since the last "Reset" or "Apply" operation. */ + public boolean canApply() + { + return canApply; + } + + /** Returns whether creature selection changed since the last "Reset" operation. */ + public boolean canReset() + { + return canReset; + } + + protected void setModified() + { + if (!canApply || !canReset) { + canApply = true; + canReset = isCreatureModified(); + getPanel().fireSettingsChanged(); + } + } + + protected void resetModified() + { + if (canApply || canReset) { + boolean randomColor = false; + for (int i = 0; i < colorModels.size() && !randomColor; i++) { + randomColor |= isColorRandom(i); + } + canApply = randomColor; + canReset = isCreatureModified(); + getPanel().fireSettingsChanged(); + } + } + + /** Returns whether modifications have been applied to the currently selected creature entry. */ + public boolean isCreatureModified() + { + int hash = getDecoder().getCreResource().hashCode(); + return (hashCreature != hash); + } + + /** Returns the currently selected item in the specified selection model. */ + private ItemInfo getItem(ItemSelectionModel model) + { + if (model != null && model.getSelectedItem() instanceof ItemInfo) { + return (ItemInfo)model.getSelectedItem(); + } else { + return null; + } + } + + private void init() + { + // perform lazy initialization: time-consuming initializations are performed on demand + modelCreSelection = new CreatureSelectionModel(false); + modelCreAnimation = new CreatureAnimationModel(); + modelCreAllegiance = new CreatureStatusModel(); + modelItemHelmet = new ItemSelectionModel(ItemInfo.FILTER_HELMET, false); + modelItemArmor = new ItemSelectionModel(ItemInfo.FILTER_ARMOR, false); + modelItemShield = new ItemSelectionModel(ItemInfo.FILTER_SHIELD.or(ItemInfo.FILTER_WEAPON_MELEE_LEFT_HANDED), false); + modelItemWeapon = new ItemSelectionModel(ItemInfo.FILTER_WEAPON, false); + for (int i = 0; i < 7; i++) { + colorModels.add(new ColorSelectionModel()); + } + } +} diff --git a/src/org/infinity/resource/cre/browser/CreatureControlPanel.java b/src/org/infinity/resource/cre/browser/CreatureControlPanel.java new file mode 100644 index 000000000..a55757fc2 --- /dev/null +++ b/src/org/infinity/resource/cre/browser/CreatureControlPanel.java @@ -0,0 +1,608 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.browser; + +import java.awt.BorderLayout; +import java.awt.CardLayout; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; + +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.event.ListDataEvent; +import javax.swing.event.ListDataListener; +import javax.swing.text.JTextComponent; + +import org.infinity.gui.ViewerUtil; +import org.infinity.icon.Icons; +import org.infinity.resource.cre.CreResource; +import org.infinity.resource.cre.browser.ColorSelectionModel.ColorEntry; +import org.infinity.resource.cre.browser.CreatureSelectionModel.CreatureItem; +import org.infinity.resource.cre.decoder.MonsterPlanescapeDecoder; +import org.infinity.resource.cre.decoder.SpriteDecoder; +import org.infinity.resource.cre.decoder.util.ItemInfo; +import org.infinity.util.IdsMap; +import org.infinity.util.IdsMapCache; +import org.infinity.util.IdsMapEntry; +import org.infinity.util.Misc; + +/** + * This panel provides controls for customizing various aspects of a CRE resource. + */ +public class CreatureControlPanel extends JPanel +{ + private static final String[] defaultColorLabels = { "Metal color", "Minor color", "Major color", "Skin color", + "Leather color", "Armor color", "Hair color" }; + + // Labels for color selection lists + private final List colorLabels = new ArrayList<>(); + // Color selection list controls + private final List> colorControls = new ArrayList<>(); + private final Listeners listeners = new Listeners(); + + private final CreatureBrowser browser; + + private CreatureControlModel model; + private JComboBox cbCreSelection; + private JComboBox cbCreAnimation; + private JComboBox cbCreAllegiance; + private JComboBox cbItemHelmet; + private JComboBox cbItemArmor; + private JComboBox cbItemShield; + private JComboBox cbItemWeapon; + private JButton bReset, bApply, bHidePanel, bShowPanel; + private JPanel panelMain, panelHidden; + private JScrollPane scrollShown; + private CardLayout layoutMain; + + public CreatureControlPanel(CreatureBrowser browser) + { + super(); + this.browser = browser; + init(); + } + + /** Returns the associated {@code CreatureBrowser} instance. */ + public CreatureBrowser getBrowser() { return browser; } + + public CreatureControlModel getControlModel() { return model; } + + /** + * Sets color labels to the specified values. Empty strings are considered as "unused". + * Specify empty array to set default labels. + */ + public void setColorLabels(String[] labels) + { + if (labels == null) { + labels = defaultColorLabels; + } + for (int i = 0; i < colorLabels.size(); i++) { + JLabel l = colorLabels.get(i); + if (i < labels.length && labels[i] != null && !labels[i].isEmpty()) { + l.setText(labels[i] + ":"); + } else { + l.setText("Unused color:"); + } + l.getParent().invalidate(); + } + validate(); + } + + /** Resets all CRE-related settings to the values provided by the selected CRE resource. */ + public void resetSettings() + { + getControlModel().reset(); + } + + /** Applies the current CRE-related settings to the creature animation. */ + public void applySettings() + { + CreResource cre = getControlModel().getDecoder().getCreResource(); + CreUtils.setAnimation(cre, getControlModel().getSelectedAnimation().getValue()); + CreUtils.setAllegiance(cre, getControlModel().getSelectedAllegiance().getValue()); + CreUtils.setStatusPanic(cre, getControlModel().getSelectedAllegiance().getStatus() == CreatureStatusModel.Status.PANICKED); + CreUtils.setEquipmentHelmet(cre, getControlModel().getSelectedHelmet(getControlModel().getModelHelmet())); + CreUtils.setEquipmentArmor(cre, getControlModel().getSelectedArmor(getControlModel().getModelArmor())); + CreUtils.setEquipmentWeapon(cre, getControlModel().getSelectedWeapon(getControlModel().getModelWeapon())); + CreUtils.setEquipmentShield(cre, getControlModel().getSelectedShield(getControlModel().getModelShield())); + int idx = 0; + for (final Iterator iter = getControlModel().getColorModelIterator(); iter.hasNext(); ) { + ColorSelectionModel cm = iter.next(); + if (cm.getSelectedItem() instanceof ColorSelectionModel.ColorEntry) { + ColorSelectionModel.ColorEntry ce = (ColorSelectionModel.ColorEntry)cm.getSelectedItem(); + CreUtils.setColor(cre, idx, ce.getIndex()); + } + idx++; + } + try { + getControlModel().resetDecoder(cre); + } catch (Exception e) { + e.printStackTrace(); + getBrowser().showErrorMessage(e.getMessage(), "Loading creature"); + } + + getBrowser().getSettingsPanel().reset(); + getBrowser().getMediaPanel().reset(true); + getControlModel().resetModified(); + } + + /** Called whenever a setting has been changed by the user. */ + public void fireSettingsChanged() + { + bReset.setEnabled(getControlModel().canReset()); + bApply.setEnabled(getControlModel().canApply()); + } + + private void init() + { + model = new CreatureControlModel(this); + + GridBagConstraints c = new GridBagConstraints(); + + // first column: creature and item selection + JLabel l1 = new JLabel("Select CRE resource:"); + cbCreSelection = new JComboBox<>(model.getModelCreature()); + // this is a good default width for all selection controls in this panel + cbCreSelection.setPrototypeDisplayValue(CreatureItem.getDefault()); + int defWidth = cbCreSelection.getPreferredSize().width * 5 / 4; +// setPreferredWidth(cbCreSelection, defWidth); + cbCreSelection.addActionListener(listeners); + updateToolTip(cbCreSelection); + + JLabel l2 = new JLabel("Creature animation:"); + l2.setToolTipText("Supports manually entered numbers. Add \"0x\" prefix or \"h\" suffix to specify a hexadecimal number."); + cbCreAnimation = new JComboBox<>(model.getModelAnimation()); + cbCreAnimation.getEditor().getEditorComponent().addFocusListener(new FocusAdapter() { + @Override + public void focusGained(FocusEvent e) + { + if (e.getSource() instanceof JTextComponent) { + ((JTextComponent)e.getSource()).selectAll(); + } + } + }); +// setPreferredWidth(cbCreAnimation, defWidth); + cbCreAnimation.setEditable(true); + cbCreAnimation.addActionListener(listeners); + updateToolTip(cbCreAnimation); + + JLabel l3 = new JLabel("Status:"); + cbCreAllegiance = new JComboBox<>(model.getModelAllegiance()); +// setPreferredWidth(cbCreAllegiance, defWidth); + cbCreAllegiance.addActionListener(listeners); + updateToolTip(cbCreAllegiance); + + JLabel l4 = new JLabel("Helmet slot:"); + cbItemHelmet = new JComboBox<>(model.getModelHelmet()); + setPreferredWidth(cbItemHelmet, defWidth); + cbItemHelmet.addActionListener(listeners); + updateToolTip(cbItemHelmet); + + JLabel l5 = new JLabel("Armor slot:"); + cbItemArmor = new JComboBox<>(model.getModelArmor()); + setPreferredWidth(cbItemArmor, defWidth); + cbItemArmor.addActionListener(listeners); + updateToolTip(cbItemArmor); + + JLabel l6 = new JLabel("Shield slot:"); + cbItemShield = new JComboBox<>(model.getModelShield()); + setPreferredWidth(cbItemShield, defWidth); + cbItemShield.addActionListener(listeners); + updateToolTip(cbItemShield); + + JLabel l7 = new JLabel("Weapon slot:"); + cbItemWeapon = new JComboBox<>(model.getModelWeapon()); + setPreferredWidth(cbItemWeapon, defWidth); + cbItemWeapon.addActionListener(listeners); + updateToolTip(cbItemWeapon); + + JPanel pColumn1 = new JPanel(new GridBagLayout()); + c = ViewerUtil.setGBC(c, 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_END, + GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0); + pColumn1.add(l1, c); + c = ViewerUtil.setGBC(c, 1, 0, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(0, 8, 0, 0), 0, 0); + pColumn1.add(cbCreSelection, c); + + c = ViewerUtil.setGBC(c, 0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_END, + GridBagConstraints.NONE, new Insets(8, 0, 0, 0), 0, 0); + pColumn1.add(l2, c); + c = ViewerUtil.setGBC(c, 1, 1, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(8, 8, 0, 0), 0, 0); + pColumn1.add(cbCreAnimation, c); + + c = ViewerUtil.setGBC(c, 0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_END, + GridBagConstraints.NONE, new Insets(8, 0, 0, 0), 0, 0); + pColumn1.add(l3, c); + c = ViewerUtil.setGBC(c, 1, 2, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(8, 8, 0, 0), 0, 0); + pColumn1.add(cbCreAllegiance, c); + + c = ViewerUtil.setGBC(c, 0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_END, + GridBagConstraints.NONE, new Insets(24, 0, 0, 0), 0, 0); + pColumn1.add(l4, c); + c = ViewerUtil.setGBC(c, 1, 3, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(24, 8, 0, 0), 0, 0); + pColumn1.add(cbItemHelmet, c); + + c = ViewerUtil.setGBC(c, 0, 4, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_END, + GridBagConstraints.NONE, new Insets(8, 0, 0, 0), 0, 0); + pColumn1.add(l5, c); + c = ViewerUtil.setGBC(c, 1, 4, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(8, 8, 0, 0), 0, 0); + pColumn1.add(cbItemArmor, c); + + c = ViewerUtil.setGBC(c, 0, 5, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_END, + GridBagConstraints.NONE, new Insets(8, 0, 0, 0), 0, 0); + pColumn1.add(l6, c); + c = ViewerUtil.setGBC(c, 1, 5, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(8, 8, 0, 0), 0, 0); + pColumn1.add(cbItemShield, c); + + c = ViewerUtil.setGBC(c, 0, 6, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_END, + GridBagConstraints.NONE, new Insets(8, 0, 0, 0), 0, 0); + pColumn1.add(l7, c); + c = ViewerUtil.setGBC(c, 1, 6, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(8, 8, 0, 0), 0, 0); + pColumn1.add(cbItemWeapon, c); + + + // second column: color selection + JComboBox cb; + ColorSelectionModel cm = model.getModelColor(0); + + // determine suitable color entry for preferred combobox width + ColorSelectionModel.ColorEntry ce = cm.getElementAt(0); + for (int i = 0; i < cm.getSize(); i++) { + ColorSelectionModel.ColorEntry item = cm.getElementAt(i); + if (item != null && (ce == null || item.toString().length() > ce.toString().length())) { + ce = item; + } + } + + final String[] labels = {"Metal color:", "Minor color:", "Major color:", "Skin color:", + "Leather color:", "Armor color:", "Hair color:"}; + for (int i = 0; i < labels.length; i++) { + l1 = new JLabel(labels[i]); + colorLabels.add(l1); + cm = model.getModelColor(i); + cb = new JComboBox<>(cm); + cb.setRenderer(cm.getRenderer()); + if (i == 0 && ce != null) { + cb.setPrototypeDisplayValue(ce); + defWidth = cb.getPreferredSize().width; + } else { + cb.setPrototypeDisplayValue((ColorSelectionModel.ColorEntry)cm.getSelectedItem()); + } + setPreferredWidth(cb, defWidth); + cb.addActionListener(listeners); + cm.addListDataListener(listeners); + updateToolTip(cb); + colorControls.add(cb); + } + + JPanel pColumn2 = new JPanel(new GridBagLayout()); + for (int i = 0; i < colorControls.size(); i++) { + int top = (i == 0) ? 0 : 8; + c = ViewerUtil.setGBC(c, 0, i, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_END, + GridBagConstraints.NONE, new Insets(top, 0, 0, 0), 0, 0); + pColumn2.add(colorLabels.get(i), c); + c = ViewerUtil.setGBC(c, 1, i, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(top, 8, 0, 0), 0, 0); + pColumn2.add(colorControls.get(i), c); + } + + + // third column: buttons + Insets margin; + bApply = new JButton("Apply", Icons.getIcon(Icons.ICON_REFRESH_16)); + Font fnt = bApply.getFont().deriveFont(bApply.getFont().getSize2D() * 1.25f); + bApply.setFont(fnt); + margin = bApply.getMargin(); + margin.top += 4; margin.bottom += 4; + bApply.setMargin(margin); + bApply.addActionListener(listeners); + bReset = new JButton("Reset", Icons.getIcon(Icons.ICON_UNDO_16)); + margin = bReset.getMargin(); + margin.top += 4; margin.bottom += 4; + bReset.setMargin(margin); + bReset.setToolTipText("Revert to creature defaults"); + bReset.addActionListener(listeners); + + JPanel pButtons = new JPanel(new GridBagLayout()); + c = ViewerUtil.setGBC(c, 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); + pButtons.add(bApply, c); + c = ViewerUtil.setGBC(c, 0, 1, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(8, 0, 0, 0), 0, 0); + pButtons.add(bReset, c); + c = ViewerUtil.setGBC(c, 0, 2, 1, 1, 0.0, 1.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.VERTICAL, new Insets(8, 0, 0, 0), 0, 0); + pButtons.add(new JPanel(), c); + + + // fourth column: show/hide panel button + bHidePanel = new JButton("Hide panel"); + margin = bHidePanel.getMargin(); + margin.top += 4; margin.bottom += 4; + bHidePanel.setMargin(margin); + defWidth = bHidePanel.getPreferredSize().width; + bHidePanel.addActionListener(listeners); + + JPanel pColumn4 = new JPanel(new GridBagLayout()); + c = ViewerUtil.setGBC(c, 0, 0, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_END, + GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0); + pColumn4.add(bHidePanel, c); + c = ViewerUtil.setGBC(c, 0, 1, 1, 1, 1.0, 1.0, GridBagConstraints.FIRST_LINE_END, + GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0); + pColumn4.add(new JPanel(), c); + + // combining columns + JPanel panelShown = new JPanel(new GridBagLayout()); + c = ViewerUtil.setGBC(c, 0, 0, 1, 1, 1.0, 1.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0); + panelShown.add(new JPanel(), c); + c = ViewerUtil.setGBC(c, 1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.NONE, new Insets(8, 8, 8, 0), 0, 0); + panelShown.add(pColumn1, c); + c = ViewerUtil.setGBC(c, 2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.NONE, new Insets(8, 8, 8, 0), 0, 0); + panelShown.add(pButtons, c); + c = ViewerUtil.setGBC(c, 3, 0, 1, 1, 0.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.NONE, new Insets(8, 32, 8, 0), 0, 0); + panelShown.add(pColumn2, c); + c = ViewerUtil.setGBC(c, 4, 0, 1, 1, 1.0, 1.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.BOTH, new Insets(8, 16, 8, 8), 0, 0); + panelShown.add(pColumn4, c); + + scrollShown = new JScrollPane(panelShown, JScrollPane.VERTICAL_SCROLLBAR_NEVER, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + scrollShown.getHorizontalScrollBar().setUnitIncrement(32); + scrollShown.setBorder(panelShown.getBorder()); + + // hidden panel + bShowPanel = new JButton("Show panel"); + margin = bShowPanel.getMargin(); + margin.top += 4; margin.bottom += 4; + bShowPanel.setMargin(margin); + defWidth = Math.max(defWidth, bShowPanel.getPreferredSize().width); + bShowPanel.setPreferredSize(new Dimension(defWidth, bShowPanel.getPreferredSize().height)); + bShowPanel.addActionListener(listeners); + + bHidePanel.setPreferredSize(new Dimension(defWidth, bHidePanel.getPreferredSize().height)); + + panelHidden = new JPanel(new GridBagLayout()); + c = ViewerUtil.setGBC(c, 0, 0, 1, 1, 1.0, 1.0, GridBagConstraints.FIRST_LINE_END, + GridBagConstraints.NONE, new Insets(8, 8, 8, 8), 0, 0); + panelHidden.add(bShowPanel, c); + + layoutMain = new CardLayout(); + panelMain = new JPanel(layoutMain); + panelMain.add(scrollShown); + panelMain.add(panelHidden); + layoutMain.first(panelMain); + + setLayout(new BorderLayout()); + add(panelMain, BorderLayout.CENTER); + + fireSettingsChanged(); + } + + /** + * Returns {@code true} if the pending reset can be performed. Shows a confirmation dialog if changes were made. + */ + private boolean confirmReset() + { + boolean retVal = true; + if (getControlModel().canReset()) { + retVal = (JOptionPane.showConfirmDialog(getBrowser(), + "Creature settings have been modified. Do you want to revert these changes?", + "Revert changes", + JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION); + } + return retVal; + } + + /** + * Generates an array of color location labels based on the creature animation provided + * by the specified {@code SpriteDecoder}. + */ + public static String[] createColorLabels(SpriteDecoder decoder) + { + String[] retVal = null; + if (decoder != null && decoder instanceof MonsterPlanescapeDecoder) { + MonsterPlanescapeDecoder mpd = (MonsterPlanescapeDecoder)decoder; + IdsMap map = IdsMapCache.get("CLOWNRGE.IDS"); + if (map != null) { + retVal = new String[mpd.getColorLocationCount()]; + for (int i = 0; i < retVal.length; i++) { + int location = mpd.getColorLocation(i) >>> 4; + if (location > 0) { + IdsMapEntry entry = map.get(location); + if (entry != null) { + retVal[i] = Misc.prettifySymbol(entry.getSymbol()) + " Color"; + } + } + } + } + } + + if (retVal == null) { + retVal = Arrays.copyOf(defaultColorLabels, defaultColorLabels.length); + } + + return retVal; + } + + /** Helper method: Updates preferred width of the specified {@code JComponent} instance. */ + private static void setPreferredWidth(JComponent c, int width) + { + if (c != null) { + Dimension dim = c.getPreferredSize(); + dim.width = width; + c.setPreferredSize(dim); + } + } + + /** Helper method: Updates the tooltip of the specified {@code JComboBox} object with the label of the selected item. */ + private static void updateToolTip(Object o) + { + if (o instanceof JComboBox) { + JComboBox cb = (JComboBox)o; + Object s = cb.getSelectedItem(); + if (s != null) { + cb.setToolTipText(s.toString()); + } else { + cb.setToolTipText(""); + } + } + } + + //-------------------------- INNER CLASSES -------------------------- + + /** + * Listeners are outsourced to this class for cleaner code. + */ + private class Listeners implements ActionListener, ListDataListener + { + private Listeners() + { + } + + //--------------------- Begin Interface ActionListener --------------------- + + @Override + public void actionPerformed(ActionEvent e) + { + if (e.getSource() == bApply) { + applySettings(); + } + else if (e.getSource() == bReset) { + if (confirmReset()) { + resetSettings(); + } + } + else if (e.getSource() == cbCreSelection) { + try { + getControlModel().creSelectionChanged(); + updateToolTip(cbCreSelection); + } catch (Exception ex) { + ex.printStackTrace(); + getBrowser().showErrorMessage(ex.getMessage(), "Creature selection"); + } + } + else if (e.getSource() == cbCreAnimation) { + if (cbCreAnimation.getSelectedItem() != null) { + // find matching list entry + int idx = getControlModel().getModelAnimation().getIndexOf(cbCreAnimation.getSelectedItem()); + if (idx != -1) { + getControlModel().getModelAnimation().setSelectedItem(getControlModel().getModelAnimation().getElementAt(idx)); + } + } + getControlModel().creAnimationChanged(); + updateToolTip(cbCreAnimation); + } + else if (e.getSource() == cbCreAllegiance) { + getControlModel().creAllegianceChanged(); + updateToolTip(cbCreAllegiance); + } + else if (e.getSource() == cbItemHelmet) { + getControlModel().itemHelmetChanged(); + updateToolTip(cbItemHelmet); + } + else if (e.getSource() == cbItemArmor) { + getControlModel().itemArmorChanged(); + updateToolTip(cbItemArmor); + } + else if (e.getSource() == cbItemShield) { + getControlModel().itemShieldChanged(); + updateToolTip(cbItemShield); + } + else if (e.getSource() == cbItemWeapon) { + getControlModel().itemWeaponChanged(); + updateToolTip(cbItemWeapon); + } + else if (e.getSource() == bHidePanel) { + layoutMain.last(panelMain); + panelMain.setPreferredSize(panelHidden.getPreferredSize()); + } + else if (e.getSource() == bShowPanel) { + layoutMain.first(panelMain); + panelMain.setPreferredSize(scrollShown.getPreferredSize()); + } + else { + // color selection + int idx = colorControls.indexOf(e.getSource()); + if (idx >= 0) { + getControlModel().colorChanged(idx); + updateToolTip(colorControls.get(idx)); + } + } + } + + //--------------------- End Interface ActionListener --------------------- + + //--------------------- Begin Interface ListDataListener --------------------- + + @Override + public void intervalAdded(ListDataEvent e) + { + } + + @Override + public void intervalRemoved(ListDataEvent e) + { + } + + @Override + public void contentsChanged(ListDataEvent e) + { +// if (e.getSource() == getControlModel().getModelCreature()) { +// } +// else if (e.getSource() == getControlModel().getModelAnimation()) { +// } +// else if (e.getSource() == getControlModel().getModelAllegiance()) { +// } +// else if (e.getSource() == getControlModel().getModelHelmet()) { +// } +// else if (e.getSource() == getControlModel().getModelArmor()) { +// } +// else if (e.getSource() == getControlModel().getModelShield()) { +// } +// else if (e.getSource() == getControlModel().getModelWeapon()) { +// } +// else { + // color selections may be unused in PST/PSTEE + for (int idx = 0, cnt = colorControls.size(); idx < cnt; idx++) { + final JComboBox cb = colorControls.get(idx); + if (e.getSource() == colorControls.get(idx).getModel()) { + boolean enabled = (colorControls.get(idx).getSelectedItem() != null); + cb.setEnabled(enabled); + colorLabels.get(idx).setEnabled(enabled); + break; + } + } +// } + } + + //--------------------- End Interface ListDataListener --------------------- + } +} diff --git a/src/org/infinity/resource/cre/browser/CreatureSelectionModel.java b/src/org/infinity/resource/cre/browser/CreatureSelectionModel.java new file mode 100644 index 000000000..309bf940d --- /dev/null +++ b/src/org/infinity/resource/cre/browser/CreatureSelectionModel.java @@ -0,0 +1,289 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.browser; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.stream.IntStream; + +import javax.swing.AbstractListModel; +import javax.swing.ComboBoxModel; + +import org.infinity.resource.Profile; +import org.infinity.resource.ResourceFactory; +import org.infinity.resource.StructureFactory; +import org.infinity.resource.StructureFactory.StructureException; +import org.infinity.resource.cre.CreResource; +import org.infinity.resource.key.BufferedResourceEntry; +import org.infinity.resource.key.ResourceEntry; +import org.infinity.util.Misc; +import org.infinity.util.ResourceStructure; +import org.infinity.util.io.StreamUtils; +import org.infinity.util.tuples.Couple; + +/** + * {@code ComboBoxModel} for the creature selection combo box used in the Creature Animation Browser. + */ +public class CreatureSelectionModel extends AbstractListModel + implements ComboBoxModel +{ + private final List creList = new ArrayList<>(); + + private Object selectedItem; + + /** + * Creates a new combobox model. + * @param autoInit whether the model is automatically populated with creature data. + */ + public CreatureSelectionModel(boolean autoInit) + { + super(); + if (autoInit) { + init(); + } + } + + /** Discards the current content and reloads creature data. */ + public void reload() + { + init(); + } + + /** + * Returns the index-position of the specified object in the list. + * @param anItem a {@code CreatureItem} object, {@code ResourceEntry} object or {@code String} specifying a resref. + * @return an int representing the index position, where 0 is the first position. + * Returns -1 if the item could not be found in the list. + */ + public int getIndexOf(Object anItem) + { + if (anItem instanceof CreatureItem) { + return creList.indexOf(anItem); + } else if (anItem instanceof CreResource) { + CreResource cre = (CreResource)anItem; + if (cre.getResourceEntry() != null) { + return getIndexOf(cre.getResourceEntry()); + } + } else if (anItem instanceof ResourceEntry) { + return IntStream + .range(0, creList.size()) + .filter(i -> creList.get(i).getResourceEntry().equals(anItem)) + .findAny() + .orElse(-1); + } else if (anItem != null) { + final String name = anItem.toString(); + return IntStream + .range(0, creList.size()) + .filter(i -> name.equalsIgnoreCase(creList.get(i).getResourceEntry().getResourceRef())) + .findAny() + .orElse(-1); + } + return -1; + } + + /** Empties the list. */ + public void removeAllElements() + { + if (!creList.isEmpty()) { + int oldSize = creList.size(); + creList.clear(); + selectedItem = null; + if (oldSize > 0) { + fireIntervalRemoved(this, 0, oldSize - 1); + } + } else { + selectedItem = null; + } + } + +//--------------------- Begin Interface ListModel --------------------- + + @Override + public int getSize() + { + return creList.size(); + } + + @Override + public CreatureItem getElementAt(int index) + { + if (index >= 0 && index < creList.size()) { + return creList.get(index); + } else { + return null; + } + } + +//--------------------- End Interface ListModel --------------------- + +//--------------------- Begin Interface ComboBoxModel --------------------- + + @Override + public void setSelectedItem(Object anItem) + { + if ((selectedItem != null && !selectedItem.equals(anItem)) || + selectedItem == null && anItem != null) { + selectedItem = anItem; + fireContentsChanged(this, -1, -1); + } + } + + @Override + public Object getSelectedItem() + { + return selectedItem; + } + +//--------------------- End Interface ComboBoxModel --------------------- + + private void init() + { + removeAllElements(); + selectedItem = null; + + ResourceFactory.getResources("CRE").stream().forEach(re -> creList.add(new CreatureItem(re))); + Collections.sort(creList); + creList.add(0, CreatureItem.getDefault()); + if (!creList.isEmpty()) { + fireIntervalAdded(this, 0, creList.size() - 1); + } + } + +//-------------------------- INNER CLASSES -------------------------- + + public static class CreatureItem implements Comparable + { + /** The default creature item referencing a pseudo creature. */ + private static final Couple DEFAULT_CREATURE = Couple.with(null, null); + + private final ResourceEntry entry; + + public CreatureItem(ResourceEntry entry) + { + this.entry = Objects.requireNonNull(entry); + } + + /** Returns the {@code ResourceEntry} associated with the item. */ + public ResourceEntry getResourceEntry() { return entry; } + + @Override + public String toString() + { + if (entry == DEFAULT_CREATURE.getValue1().getResourceEntry()) { + return "None (customize from scratch)"; + } else { + String resref = entry.getResourceName(); + String name = entry.getSearchString(); + if (name == null || name.isEmpty()) { + return resref; + } else { + return resref + " (" + name + ")"; + } + } + } + + //--------------------- Begin Interface Comparable --------------------- + + @Override + public int compareTo(CreatureItem o) + { + return entry.compareTo(o.entry); + } + + //--------------------- End Interface Comparable --------------------- + + /** Returns a default creature suitable for the current game. */ + public static CreatureItem getDefault() + { + if (DEFAULT_CREATURE.getValue0() != Profile.getGame()) { + DEFAULT_CREATURE.setValue0(Profile.getGame()); + DEFAULT_CREATURE.setValue1(new CreatureItem(createCreature())); + } + return DEFAULT_CREATURE.getValue1(); + } + + /** Helper method: Creates a default CRE resource from scratch based on the current game. */ + private static ResourceEntry createCreature() + { + ResourceEntry retVal = null; + try { + ResourceStructure res = StructureFactory.getInstance().createStructure(StructureFactory.ResType.RES_CRE, null, null); + ByteBuffer buf = res.getBuffer(); + + // setting default animation + switch (Profile.getGame()) { + case PST: + buf.putInt(0x28, 0xe03e); // Townie, LC Male + break; + case PSTEE: + buf.putInt(0x28, 0xf03e); // cl_3_Townie_LC_Male_2 + break; + default: + buf.putInt(0x28, 0x6100); // fighter_male_human + } + + // setting default colors + final byte[] colorsPst = {(byte)7, (byte)86, (byte)40, (byte)75}; + final byte[] locationsPst = {(byte)224, (byte)144, (byte)176, (byte)160}; + final byte[] colorsOther = {(byte)30, (byte)37, (byte)57, (byte)12, (byte)23, (byte)28, (byte)0}; + switch (Profile.getGame()) { + case PST: + case PSTEE: + { + int ofsColors = (Profile.getGame() == Profile.Game.PST) ? 0x2e4 : 0x2c; + int ofsLocations = (Profile.getGame() == Profile.Game.PST) ? 0x2f5 : -1; + int sizeColors = (Profile.getGame() == Profile.Game.PST) ? 2 : 1; + int cnt = Math.min(colorsPst.length, locationsPst.length); + if (ofsLocations > 0) { + buf.put(0x2df, (byte)cnt); // # colors + } + for (int i = 0; i < cnt; i++) { + buf.put(ofsColors + i * sizeColors, colorsPst[i]); + if (ofsLocations > 0) { + buf.put(ofsLocations + i, locationsPst[i]); + } + } + break; + } + default: + for (int i = 0; i < colorsOther.length; i++) { + buf.put(0x2c + i, colorsOther[i]); + } + } + + // setting default properties + // SEX=MALE, EA=NEUTRAL, GENERAL=HUMANOID, RACE=HUMAN, CLASS=FIGHTER, GENDER=MALE, ALIGNMENT=NEUTRAL + byte[] properties = {(byte)1, (byte)128, (byte)1, (byte)1, (byte)2, (byte)1, (byte)0x22}; + int[] offsets; + String ver = StreamUtils.readString(buf, 4, 4, Misc.CHARSET_DEFAULT).toUpperCase(); + switch (ver) { + case "V1.2": + offsets = new int[] {0x237, 0x314, 0x315, 0x316, 0x317, 0x319, 0x31f}; + break; + case "V2.2": + offsets = new int[] {0x389, 0x384, 0x385, 0x386, 0x387, 0x389, 0x38f}; + break; + case "V9.0": + offsets = new int[] {0x237, 0x2d8, 0x2d9, 0x2da, 0x2db, 0x2dd, 0x2e3}; + break; + default: + offsets = new int[] {0x237, 0x270, 0x271, 0x272, 0x273, 0x275, 0x27b}; + } + for (int i = 0; i < properties.length; i++) { + buf.put(offsets[i], properties[i]); + } + + retVal = new BufferedResourceEntry(buf, "*.CRE"); + } catch (StructureException e) { + e.printStackTrace(); + } + + return retVal; + } + } +} diff --git a/src/org/infinity/resource/cre/browser/CreatureStatusModel.java b/src/org/infinity/resource/cre/browser/CreatureStatusModel.java new file mode 100644 index 000000000..b5c72360c --- /dev/null +++ b/src/org/infinity/resource/cre/browser/CreatureStatusModel.java @@ -0,0 +1,182 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.browser; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.stream.IntStream; + +import javax.swing.AbstractListModel; +import javax.swing.ComboBoxModel; + +import org.infinity.util.Misc; + +/** + * {@code ComboBoxModel} for the creature allegiance combo box used in the Creature Animation Browser. + */ +public class CreatureStatusModel extends AbstractListModel + implements ComboBoxModel +{ + /** Available creature status entries. */ + public enum Status { + /** Indicates green selection circle. */ + FRIENDLY(2), + /** Indicates cyan selection circle. */ + NEUTRAL(128), + /** Indicates red selection circle. */ + HOSTILE(255), + /** Indicates yellow selection circle. */ + PANICKED(-1); + + private final int id; + private Status(int id) { this.id = id; } + + /** Returns the numeric id associated with the enum. */ + public int getValue() { return id; } + } + + private final List statusList = new ArrayList<>(); + + private Object selectedItem; + + public CreatureStatusModel() + { + super(); + init(); + } + + public void reload() + { + init(); + } + + /** + * Returns the index-position of the specified object in the list. + * @param anItem a {@code StatusEntry} object, {@code Number} object or {@code String} specifying an allegiance name. + * @return an int representing the index position, where 0 is the first position. Returns -1 + * if the item could not be found in the list. + */ + public int getIndexOf(Object anItem) + { + if (anItem instanceof StatusEntry) { + return statusList.indexOf(anItem); + } else if (anItem instanceof Number) { + final int eaValue = ((Number)anItem).intValue(); + return IntStream + .range(0, statusList.size()) + .filter(i -> statusList.get(i).getStatus().getValue() == eaValue) + .findAny() + .orElse(-1); + } else if (anItem != null) { + final String eaName = anItem.toString().trim(); + return IntStream + .range(0, statusList.size()) + .filter(i -> eaName.equalsIgnoreCase(statusList.get(i).getName())) + .findAny() + .orElse(-1); + } + return -1; + } + + /** Empties the list. */ + public void removeAllElements() + { + if (!statusList.isEmpty()) { + int oldSize = statusList.size(); + statusList.clear(); + selectedItem = null; + if (oldSize > 0) { + fireIntervalRemoved(this, 0, oldSize - 1); + } + } else { + selectedItem = null; + } + } + +//--------------------- Begin Interface ListModel --------------------- + + @Override + public int getSize() + { + return statusList.size(); + } + + @Override + public StatusEntry getElementAt(int index) + { + if (index >= 0 && index < statusList.size()) { + return statusList.get(index); + } else { + return null; + } + } + +//--------------------- End Interface ListModel --------------------- + +//--------------------- Begin Interface ComboBoxModel --------------------- + + @Override + public void setSelectedItem(Object anItem) + { + if ((selectedItem != null && !selectedItem.equals(anItem)) || + selectedItem == null && anItem != null) { + selectedItem = anItem; + fireContentsChanged(this, -1, -1); + } + } + + @Override + public Object getSelectedItem() + { + return selectedItem; + } + +//--------------------- End Interface ComboBoxModel --------------------- + + private void init() + { + removeAllElements(); + + for (final Status status : Status.values()) { + statusList.add(new StatusEntry(status)); + } + fireIntervalAdded(this, 0, statusList.size() - 1); + + setSelectedItem(getElementAt(0)); + } + +//-------------------------- INNER CLASSES -------------------------- + + public static class StatusEntry + { + private final Status status; + private final String name; + + public StatusEntry(Status status) + { + this.status = Objects.requireNonNull(status); + this.name = Misc.prettifySymbol(this.status.toString()); + } + + /** Returns the status enum. */ + public Status getStatus() { return status; } + + /** + * Returns a typical EA.IDS value representing the status. + * Returns negative values for non-allegiance status types. + */ + public int getValue() { return status.getValue(); } + + /** Returns the descriptive name of the status. */ + public String getName() { return name; } + + @Override + public String toString() + { + return getName(); + } + } +} diff --git a/src/org/infinity/resource/cre/browser/DecoderAttributesTableModel.java b/src/org/infinity/resource/cre/browser/DecoderAttributesTableModel.java new file mode 100644 index 000000000..7829435fe --- /dev/null +++ b/src/org/infinity/resource/cre/browser/DecoderAttributesTableModel.java @@ -0,0 +1,218 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.browser; + +import java.awt.Component; +import java.util.Iterator; +import java.util.TreeMap; + +import javax.swing.DefaultListSelectionModel; +import javax.swing.JTable; +import javax.swing.ListSelectionModel; +import javax.swing.SwingConstants; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.DefaultTableColumnModel; +import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableColumn; + +import org.infinity.resource.cre.decoder.SpriteDecoder; +import org.infinity.resource.cre.decoder.util.DecoderAttribute; +import org.infinity.util.Misc; + +/** + * A table model that handles key/value pairs of animation attributes stored in {@code SpriteDecoder} instances. + */ +public class DecoderAttributesTableModel extends AbstractTableModel +{ + private final TreeMap attributes = new TreeMap<>(); + + private SpriteDecoder decoder; + // working array for map keys to speed up lookup + private String[] keys; + + public DecoderAttributesTableModel() + { + this(null); + } + + public DecoderAttributesTableModel(SpriteDecoder decoder) + { + super(); + setDecoder(decoder); + } + + /** Returns the currently assigned {@code SpriteDecoder}. */ + public SpriteDecoder getDecoder() { return decoder; } + + /** + * Assigns a {@code SpriteDecoder} instance to the table model and updates the list of available + * animation attributes. + * @param decoder the {@code SpriteDecoder} instance + */ + public void setDecoder(SpriteDecoder decoder) + { + if (this.decoder == null && decoder != null || + this.decoder != null && !this.decoder.equals(decoder)) { + this.decoder = decoder; + reload(); + } + } + + /** + * Discards the current list of animation attributes and loads a new list from the + * defined {@code SpriteDecoder} instance. + */ + public void reload() + { + int oldSize = attributes.size(); + attributes.clear(); + if (oldSize > 0) { + fireTableRowsDeleted(0, oldSize - 1); + } + + if (decoder != null) { + // Special: add animation id to list of attributes + attributes.put(Misc.prettifySymbol("animation_id"), String.format("0x%04x (%d)", decoder.getAnimationId(), decoder.getAnimationId())); + + for (final Iterator iter = decoder.getAttributeIterator(); iter.hasNext(); ) { + DecoderAttribute att = iter.next(); + + // skip selected attributes + if (SpriteDecoder.KEY_ANIMATION_TYPE.equals(att)) { + continue; + } + + String key = ""; + String value = ""; + switch (att.getType()) { + case BOOLEAN: + { + Boolean b = decoder.getAttribute(att); + if (b != null) { + key = att.getName(); + value = b.toString(); + } + break; + } + case DECIMAL: + { + Double d = decoder.getAttribute(att); + if (d != null) { + key = att.getName(); + value = d.toString(); + } + break; + } + case INT: + { + Integer n = decoder.getAttribute(att); + if (n != null) { + key = att.getName(); + value = n.toString(); + } + break; + } + case STRING: + { + String s = decoder.getAttribute(att); + if (s != null) { + key = att.getName(); + value = s; + } + break; + } + default: + { + Object o = decoder.getAttribute(att); + if (o != null) { + key = att.getName(); + value = o.toString(); + } + } + } + + if (!key.isEmpty()) { + attributes.put(Misc.prettifySymbol(key), value); + } + } + keys = attributes.keySet().toArray(new String[attributes.keySet().size()]); + fireTableRowsInserted(0, keys.length - 1); + } + } + + @Override + public int getRowCount() + { + return attributes.size(); + } + + @Override + public int getColumnCount() + { + return 2; + } + + @Override + public Object getValueAt(int rowIndex, int columnIndex) + { + if (rowIndex >= 0 && rowIndex < keys.length && columnIndex >= 0 && columnIndex < 2) { + String key = keys[rowIndex]; + switch (columnIndex) { + case 0: + return key; + case 1: + return attributes.get(key); + } + } + return ""; + } + +//-------------------------- INNER CLASSES -------------------------- + + public static class AttributesColumnModel extends DefaultTableColumnModel + { + public AttributesColumnModel() + { + super(); + TableColumn column1 = new TableColumn(0, 125, new DefaultTableCellRenderer(), null); + column1.setHeaderValue("Name"); + column1.setMinWidth(16); + addColumn(column1); + TableColumn column2 = new TableColumn(1, 125, new DefaultTableCellRenderer(), null); + column2.setHeaderValue("Value"); + column2.setMinWidth(16); + addColumn(column2); + } + } + + public static class AttributesListSelectionModel extends DefaultListSelectionModel + { + public AttributesListSelectionModel() + { + super(); + setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + } + } + + public static class AttributesHeaderRenderer implements TableCellRenderer + { + private final DefaultTableCellRenderer renderer; + + public AttributesHeaderRenderer(JTable table) + { + super(); + this.renderer = (DefaultTableCellRenderer)table.getTableHeader().getDefaultRenderer(); + this.renderer.setHorizontalAlignment(SwingConstants.LEADING); + } + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, + int row, int column) + { + return renderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + } + } +} diff --git a/src/org/infinity/resource/cre/browser/ItemSelectionModel.java b/src/org/infinity/resource/cre/browser/ItemSelectionModel.java new file mode 100644 index 000000000..e454697ed --- /dev/null +++ b/src/org/infinity/resource/cre/browser/ItemSelectionModel.java @@ -0,0 +1,166 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.browser; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.IntStream; + +import javax.swing.AbstractListModel; +import javax.swing.ComboBoxModel; + +import org.infinity.resource.cre.decoder.util.ItemInfo; +import org.infinity.resource.cre.decoder.util.ItemInfo.ItemPredicate; +import org.infinity.resource.key.ResourceEntry; + +/** + * {@code ComboBoxModel} for item selection combo boxes used in the Creature Animation Browser. + * The model allows to filter items by custom criteria. + */ +public class ItemSelectionModel extends AbstractListModel implements ComboBoxModel +{ + private final List itemList = new ArrayList<>(100); + + private boolean autoInit; + private ItemPredicate filter; + private Object selectedItem; + + public ItemSelectionModel(ItemPredicate filter, boolean autoInit) + { + super(); + this.autoInit = autoInit; + setFilter(filter); + } + + /** Returns whether content is automatically updated when the item filter is updated. */ + public boolean isAutoInit() { return autoInit; } + + /** Specify whether content is automatically updated when the item filter is updated. */ + public void setAutoInit(boolean b) + { + if (b != autoInit) { + autoInit = b; + } + } + + /** Returns the current item filter. */ + public ItemPredicate getFilter() { return filter; } + + /** Sets a new item filter. Item list will be updated automatically. */ + public void setFilter(ItemPredicate filter) + { + if (filter == null) { + filter = ItemInfo.FILTER_ALL; + } + if (!filter.equals(this.filter)) { + this.filter = filter; + if (isAutoInit()) { + reload(); + } + } + } + + /** Discards the current content and loads new content based on current settings. */ + public void reload() + { + init(); + } + + /** + * Returns the index-position of the specified object in the list. + * @param anItem a {@code ItemInfo} object, {@code ResourceEntry} object or {@code String} specifying a ITM resref. + * @return an int representing the index position, where 0 is the first position. Returns -1 + * if the item could not be found in the list. + */ + public int getIndexOf(Object anItem) + { + if (anItem instanceof ItemInfo) { + return itemList.indexOf(anItem); + } else if (anItem instanceof ResourceEntry) { + final ResourceEntry entry = (ResourceEntry)anItem; + return IntStream + .range(0, itemList.size()) + .filter(i -> entry.equals(itemList.get(i).getResourceEntry())) + .findAny() + .orElse(-1); + } else if (anItem != null) { + final String itemName = anItem.toString().trim(); + return IntStream + .range(0, itemList.size()) + .filter(i -> itemName.equalsIgnoreCase(itemList.get(i).getResourceEntry().getResourceRef())) + .findAny() + .orElse(-1); + } + return -1; + } + + /** Empties the list. */ + public void removeAllElements() + { + if (!itemList.isEmpty()) { + int oldSize = itemList.size(); + itemList.clear(); + selectedItem = null; + if (oldSize > 0) { + fireIntervalRemoved(this, 0, oldSize - 1); + } + } else { + selectedItem = null; + } + } + +//--------------------- Begin Interface ListModel --------------------- + + @Override + public int getSize() + { + return itemList.size(); + } + + @Override + public ItemInfo getElementAt(int index) + { + if (index >= 0 && index < itemList.size()) { + return itemList.get(index); + } else { + return null; + } + } + +//--------------------- End Interface ListModel --------------------- + +//--------------------- Begin Interface ComboBoxModel --------------------- + + @Override + public void setSelectedItem(Object anItem) + { + if ((selectedItem != null && !selectedItem.equals(anItem)) || + selectedItem == null && anItem != null) { + selectedItem = anItem; + fireContentsChanged(this, -1, -1); + } + } + + @Override + public Object getSelectedItem() + { + return selectedItem; + } + +//--------------------- End Interface ComboBoxModel --------------------- + + private void init() + { + removeAllElements(); + + itemList.add(ItemInfo.EMPTY); + itemList.addAll(ItemInfo.getItemList(getFilter(), true)); + if (!itemList.isEmpty()) { + fireIntervalAdded(this, 0, itemList.size() - 1); + } + + setSelectedItem(getElementAt(0)); + } +} diff --git a/src/org/infinity/resource/cre/browser/MediaPanel.java b/src/org/infinity/resource/cre/browser/MediaPanel.java new file mode 100644 index 000000000..75e68a9f8 --- /dev/null +++ b/src/org/infinity/resource/cre/browser/MediaPanel.java @@ -0,0 +1,802 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.browser; + +import java.awt.BorderLayout; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.List; +import java.util.Objects; + +import javax.swing.DefaultComboBoxModel; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSlider; +import javax.swing.SwingConstants; +import javax.swing.Timer; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import org.infinity.gui.ViewerUtil; +import org.infinity.gui.WindowBlocker; +import org.infinity.resource.cre.browser.icon.Icons; +import org.infinity.resource.cre.decoder.SpriteDecoder; +import org.infinity.resource.cre.decoder.SpriteDecoder.SpriteBamControl; +import org.infinity.resource.cre.decoder.util.Direction; +import org.infinity.resource.cre.decoder.util.Sequence; + +/** + * This panel provides controls for animation playback and related visual options. + */ +public class MediaPanel extends JPanel +{ + private static boolean isLoop; + + static { + isLoop = true; + } + + // mapping of slider value to direction + private final HashMap directionMap = new HashMap<>(); + private final Listeners listeners = new Listeners(); + private final CreatureBrowser browser; + + private JButton bHome, bEnd, bStepBack, bStepForward, bPlay, bStop; + private DefaultComboBoxModel modelSequences; + private JComboBox cbSequences; + private JCheckBox cbLoop; + private JSlider slDirection; + private JLabel lDirection; + private JLabel lFrameCur, lFrameMax; + private SpriteBamControl controller; + private Timer timer; + private int curFrame, curCycle; + + public MediaPanel(CreatureBrowser browser) + { + super(); + this.browser = Objects.requireNonNull(browser); + init(); + } + + /** Returns the associated {@code CreatureBrowser} instance. */ + public CreatureBrowser getBrowser() { return browser; } + + /** + * Discards the current animation state and initializes a new animation. + * @param preserveState whether current media control states (sequence, frame index, ...) should be preserved if possible. + */ + public void reset(boolean preserveState) + { + Sequence oldSequence = preserveState ? getSequence() : null; + Direction oldDir = preserveState ? getDirection(getCurrentDirection()) : null; + int oldFrameIdx = preserveState ? getCurrentFrame() : 0; + boolean isRunning = preserveState ? isRunning() : false; + + stop(); + modelSequences.removeAllElements(); + setCurrentDirection(0); + slDirection.setMinimum(0); + slDirection.setMaximum(0); + directionMap.clear(); + controller = null; + curFrame = curCycle = -1; + updateControls(); + + SpriteDecoder decoder = getBrowser().getDecoder(); + if (decoder == null) { + return; + } + + SettingsPanel settings = getBrowser().getSettingsPanel(); + decoder.setAutoApplyChanges(false); + decoder.setPaletteReplacementEnabled(settings.isPaletteReplacementEnabled()); + decoder.setTintEnabled(settings.isTintEnabled()); + decoder.setBlurEnabled(settings.isBlurEnabled()); + decoder.setTranslucencyEnabled(settings.isTranslucencyEnabled()); + decoder.setSelectionCircleEnabled(settings.isSelectionCircleEnabled()); + decoder.setSelectionCircleBitmap(settings.isOrnateSelectionCircle()); + decoder.setPersonalSpaceVisible(settings.isPersonalSpaceEnabled()); + decoder.setRenderAvatar(settings.isAvatarVisible()); + decoder.setRenderHelmet(settings.isHelmetVisible()); + decoder.setRenderShield(settings.isShieldVisible()); + decoder.setRenderWeapon(settings.isWeaponVisible()); + decoder.setBoundingBoxVisible(settings.isOverlayBordersVisible()); + decoder.applyAnimationChanges(); + decoder.setAutoApplyChanges(true); + + setController(decoder.createControl()); + getBrowser().getRenderPanel().setComposite(getBrowser().getSettingsPanel().getComposite()); + initSequences(decoder, oldSequence); + initDirection(decoder, oldDir); + setCurrentFrame(oldFrameIdx); + updateControls(); + setRunning(isRunning); + } + + /** Returns the currently assigned BAM controller for the creature animation. */ + public SpriteBamControl getController() { return controller; } + + /** Sets the BAM controller for the creature animation. */ + protected void setController(SpriteBamControl controller) + { + if (this.controller == null && controller != null || + this.controller != null && !this.controller.equals(controller)) { + pause(); + this.controller = controller; + if (this.controller != null) { + this.controller.setMode(SpriteBamControl.Mode.SHARED); + this.controller.setSharedPerCycle(false); + } + } + } + + /** Returns the current frame rate. Rounding errors may occur. */ + public int getFrameRate() + { + int retVal = 0; + if (timer != null && timer.getDelay() > 0) { + retVal = 1000 / timer.getDelay(); + } + return retVal; + } + + /** + * Sets the frame rate for playback. + * @param fps the frame rate in frames per seconds. + * @throws IllegalArgumentException if frame rate lies outside of supported range [1, 60]. + */ + public void setFrameRate(int fps) throws IllegalArgumentException + { + if (fps < 1 || fps > 60) { + throw new IllegalArgumentException("Unsupported frame rate: " + fps + " fps"); + } + int delay = 1000 / fps; + if (timer == null) { + timer = new Timer(delay, listeners); + } else { + timer.setDelay(delay); + } + } + + /** Returns the currently displayed BAM frame. */ + public int getCurrentFrame() + { + if (getController() != null) { + return getController().cycleGetFrameIndex(); + } + return -1; + } + + /** + * Sets the current BAM frame and updates display accordingly. Does nothing if no BAM controller is available. + * @param frameIdx the new frame index in the current cycle. Frame index is capped to the available range of cycle frames. + */ + public void setCurrentFrame(int frameIdx) + { + if (getController() != null) { + if (curFrame != frameIdx || curCycle != getController().cycleGet()) { + frameIdx = Math.max(0, Math.min(getController().cycleFrameCount() - 1, frameIdx)); + getController().cycleSetFrameIndex(frameIdx); + curFrame = getController().cycleGetFrameIndex(); + getBrowser().getRenderPanel().setFrame(getController()); + getBrowser().getRenderPanel().updateCanvas(); + updateLabels(); + } + } + } + + /** Returns the total number of frames in the current cycle. */ + public int getMaxFrame() + { + if (getController() != null) { + return getController().cycleFrameCount(); + } + return -1; + } + + /** Returns the selected cycle index. */ + public int getCurrentCycle() + { + if (getController() != null) { + return getController().cycleGet(); + } + return -1; + } + + /** + * Sets the active cycle and updates display accordingly. + * @param cycleIdx the new cycle index. + * @throws IndexOutOfBoundsException if the cycle index does not exist. + */ + public void setCurrentCycle(int cycleIdx) throws IndexOutOfBoundsException + { + if (getController() != null) { + if (curCycle != cycleIdx) { + if (!getController().cycleSet(cycleIdx)) { + throw new IndexOutOfBoundsException("Cycle index is out of bounds: " + cycleIdx); + } + setCurrentFrame(curFrame); + curCycle = getController().cycleGet(); + } + } + } + + /** Returns the current direction slider position as numeric value. */ + public int getCurrentDirection() { return slDirection.getValue(); } + + /** Sets the specified direction index if available. Throws an {@code IndexOutOfBoundsException} otherwise. */ + public void setCurrentDirection(int pos) throws IndexOutOfBoundsException + { + if (pos >= slDirection.getMinimum() && pos <= slDirection.getMaximum()) { + slDirection.setValue(pos); + } else { + throw new IndexOutOfBoundsException("Direction value out of bounds: " + pos); + } + } + + /** Sets the specified direction if available. Throws an {@code IllegalArgumentException} otherwise. */ + public void setCurrentDirection(Direction dir) throws IllegalArgumentException + { + if (dir != null) { + for (int i = 0; i < slDirection.getMaximum(); i++) { + if (dir.equals(getDirection(i))) { + slDirection.setValue(i); + return; + } + } + throw new IllegalArgumentException("Requested direction is not available: " + dir); + } else { + slDirection.setValue(0); + } + } + + /** Returns the currently selected animation sequence. Returns {@code null} if no sequence is active. */ + public Sequence getSequence() + { + return modelSequences.getElementAt(cbSequences.getSelectedIndex()); + } + + /** Sets the specified sequence and loads the associated animation. */ + public void setSequence(Sequence seq) throws IllegalArgumentException + { + int oldIdx = cbSequences.getSelectedIndex(); + int idx = modelSequences.getIndexOf(seq); + if (idx >= 0) { + if (idx != oldIdx) { + curCycle = curFrame = -1; + cbSequences.setSelectedIndex(idx); + } + } else { + throw new IllegalArgumentException("Specified sequence is not available"); + } + } + + public void loadSequence(Sequence seq) throws IllegalArgumentException + { + SpriteDecoder decoder = getBrowser().getDecoder(); + RenderPanel renderer = getBrowser().getRenderPanel(); + Direction oldDir = getDirection(getCurrentDirection()); + boolean isRunning = isRunning(); + pause(); + try { + if (!getBrowser().getDecoder().loadSequence(seq)) { + throw new Exception(); + } + } catch (Exception e) { + e.printStackTrace(); + throw new IllegalArgumentException("Could not load animation sequence"); + } + + setController(decoder.createControl()); + initDirection(decoder, oldDir); + if (curCycle >= 0 && curCycle < getController().cycleCount()) { + getController().cycleSet(curCycle); + if (curFrame >= 0 && curFrame < getController().cycleFrameCount()) { + getController().cycleSetFrameIndex(curFrame); + } + } + curCycle = getController().cycleGet(); + curFrame = getController().cycleGetFrameIndex(); + + renderer.setFrame(getController()); + renderer.updateCanvas(); + setRunning(isRunning); + } + + /** Returns whether playback is repeated when the end of the animation is reached. */ + public boolean isLooping() { return cbLoop.isSelected(); } + + /** Returns whether playback is enabled. */ + public boolean isRunning() + { + if (timer != null) { + return timer.isRunning(); + } + return false; + } + + /** Enables or disables playback of the currently active animation sequence. */ + public void setRunning(boolean b) + { + if (b != isRunning()) { + if (b) { + timer.start(); + } else { + timer.stop(); + } + String iconName = isRunning() ? Icons.ICON_PAUSE : Icons.ICON_PLAY; + bPlay.setIcon(Icons.getIcon(iconName)); + updateControls(); + } + } + + /** Advances current animation by one frame. Takes loop into account. Updates controls. */ + public void advanceFrame() + { + if (getMaxFrame() > 0) { + int frameIdx = getCurrentFrame() + 1; + if (isLooping()) { + frameIdx %= getMaxFrame(); + } + + if (frameIdx < getMaxFrame()) { + setCurrentFrame(frameIdx); + } else { + pause(); + } + } + } + + /** Starts playback of the active BAM cycle. Does nothing if playback is already enabled. */ + public void play() + { + if (!isRunning()) { + setRunning(true); + updateControls(); + } + } + + /** + * Stops playback of the active BAM cycle without resetting frame position. + * Does nothing if playback is already disabled. + */ + public void pause() + { + if (isRunning()) { + setRunning(false); + updateControls(); + } + } + + /** + * Stops playback of the active BAM cycle and resets frame position. + */ + public void stop() + { + setRunning(false); + setCurrentFrame(0); + updateControls(); + } + + private void init() + { + GridBagConstraints c = new GridBagConstraints(); + + // frame info + JLabel l1 = new JLabel("Frame:"); + JLabel l2 = new JLabel("/"); + lFrameCur = new JLabel("0"); + lFrameMax = new JLabel("0"); + + JPanel pRow1 = new JPanel(new GridBagLayout()); + c = ViewerUtil.setGBC(c, 0, 0, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); + pRow1.add(new JPanel(), c); + c = ViewerUtil.setGBC(c, 1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0); + pRow1.add(l1, c); + c = ViewerUtil.setGBC(c, 2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.NONE, new Insets(0, 4, 0, 0), 0, 0); + pRow1.add(lFrameCur, c); + c = ViewerUtil.setGBC(c, 3, 0, 1, 1, 0.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.NONE, new Insets(0, 4, 0, 0), 0, 0); + pRow1.add(l2, c); + c = ViewerUtil.setGBC(c, 4, 0, 1, 1, 0.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.NONE, new Insets(0, 4, 0, 0), 0, 0); + pRow1.add(lFrameMax, c); + c = ViewerUtil.setGBC(c, 5, 0, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); + pRow1.add(new JPanel(), c); + + + // playback controls + bHome = new JButton(Icons.getIcon(Icons.ICON_HOME)); + bHome.addActionListener(listeners); + + bStepBack = new JButton(Icons.getIcon(Icons.ICON_STEP_BACK)); + bStepBack.addActionListener(listeners); + + bPlay = new JButton(Icons.getIcon(Icons.ICON_PLAY)); + bPlay.addActionListener(listeners); + + bStop = new JButton(Icons.getIcon(Icons.ICON_STOP)); + bStop.addActionListener(listeners); + + bStepForward= new JButton(Icons.getIcon(Icons.ICON_STEP_FORWARD)); + bStepForward.addActionListener(listeners); + + bEnd = new JButton(Icons.getIcon(Icons.ICON_END)); + bEnd.addActionListener(listeners); + + JPanel pRow2 = new JPanel(new GridBagLayout()); + c = ViewerUtil.setGBC(c, 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0); + pRow2.add(bHome, c); + c = ViewerUtil.setGBC(c, 1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.NONE, new Insets(0, 8, 0, 0), 0, 0); + pRow2.add(bStepBack, c); + c = ViewerUtil.setGBC(c, 2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.NONE, new Insets(0, 8, 0, 0), 0, 0); + pRow2.add(bPlay, c); + c = ViewerUtil.setGBC(c, 3, 0, 1, 1, 0.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.NONE, new Insets(0, 8, 0, 0), 0, 0); + pRow2.add(bStop, c); + c = ViewerUtil.setGBC(c, 4, 0, 1, 1, 0.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.NONE, new Insets(0, 8, 0, 0), 0, 0); + pRow2.add(bStepForward, c); + c = ViewerUtil.setGBC(c, 5, 0, 1, 1, 0.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.NONE, new Insets(0, 8, 0, 0), 0, 0); + pRow2.add(bEnd, c); + + + // direction controls + slDirection = new JSlider(SwingConstants.HORIZONTAL); + slDirection.setSnapToTicks(true); + slDirection.setPaintLabels(true); + slDirection.setPaintTicks(true); + slDirection.setSnapToTicks(true); + slDirection.addChangeListener(listeners); + l1 = new JLabel("Direction:"); + lDirection = new JLabel("S"); + + JPanel pRow3 = new JPanel(new GridBagLayout()); + c = ViewerUtil.setGBC(c, 0, 0, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); + pRow3.add(new JPanel(), c); + c = ViewerUtil.setGBC(c, 1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.NONE, new Insets(4, 0, 0, 0), 0, 0); + pRow3.add(l1, c); + c = ViewerUtil.setGBC(c, 2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.NONE, new Insets(4, 4, 0, 0), 0, 0); + pRow3.add(lDirection, c); + c = ViewerUtil.setGBC(c, 3, 0, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); + pRow3.add(new JPanel(), c); + c = ViewerUtil.setGBC(c, 0, 1, 4, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(4, 0, 0, 0), 0, 0); + pRow3.add(slDirection, c); + + + // combining all rows + JPanel pColumn1 = new JPanel(new GridBagLayout()); + c = ViewerUtil.setGBC(c, 0, 0, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); + pColumn1.add(pRow1, c); + c = ViewerUtil.setGBC(c, 0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.NONE, new Insets(8, 0, 0, 0), 0, 0); + pColumn1.add(pRow2, c); + c = ViewerUtil.setGBC(c, 0, 2, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(8, 0, 0, 0), 0, 0); + pColumn1.add(pRow3, c); + + + // sequence selection, loop option + l1 = new JLabel("Sequence:"); + modelSequences = new DefaultComboBoxModel<>(); + cbSequences = new JComboBox<>(modelSequences); + cbSequences.setPrototypeDisplayValue(Sequence.ATTACK_2WEAPONS1); + cbSequences.addItemListener(listeners); + + cbLoop = new JCheckBox("Loop", isLoop); + + JPanel pColumn2 = new JPanel(new GridBagLayout()); + c = ViewerUtil.setGBC(c, 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0); + pColumn2.add(l1, c); + c = ViewerUtil.setGBC(c, 0, 1, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(8, 0, 0, 0), 0, 0); + pColumn2.add(cbSequences, c); + c = ViewerUtil.setGBC(c, 0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.NONE, new Insets(8, 0, 0, 0), 0, 0); + pColumn2.add(cbLoop, c); + + + // combining panels + JPanel panelMain = new JPanel(new GridBagLayout()); + c = ViewerUtil.setGBC(c, 0, 0, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); + panelMain.add(new JPanel(), c); + c = ViewerUtil.setGBC(c, 1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(8, 0, 8, 0), 0, 0); + panelMain.add(pColumn1, c); + c = ViewerUtil.setGBC(c, 2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(8, 32, 8, 0), 0, 0); + panelMain.add(pColumn2, c); + c = ViewerUtil.setGBC(c, 3, 0, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); + panelMain.add(new JPanel(), c); + + JScrollPane scrollMedia = new JScrollPane(panelMain); + scrollMedia.setBorder(panelMain.getBorder()); + + setLayout(new BorderLayout()); + add(scrollMedia, BorderLayout.CENTER); + + // default settings + curFrame = curCycle = -1; + setFrameRate(15); + updateControls(); + } + + /** Updates media controls based on the current animation state. */ + private void updateControls() + { + boolean loaded = getSequence() != null; + boolean running = isRunning(); + bHome.setEnabled(running || loaded && getCurrentFrame() > 0); + bEnd.setEnabled(running || loaded && getCurrentFrame() < getMaxFrame() - 1); + bStepBack.setEnabled(running || loaded && getCurrentFrame() > 0); + bStepForward.setEnabled(running || loaded && getCurrentFrame() < getMaxFrame() - 1); + bPlay.setEnabled(loaded); + bStop.setEnabled(running); + cbSequences.setEnabled(modelSequences.getSize() > 0); + cbLoop.setEnabled(loaded); + slDirection.setEnabled(loaded && slDirection.getMaximum() > slDirection.getMinimum()); + updateLabels(); + } + + /** Updates labels bvased on current control and animation state. */ + private void updateLabels() + { + lFrameCur.setText(Integer.toString(getCurrentFrame())); + lFrameMax.setText(Integer.toString(getMaxFrame() - 1)); + Direction dir = getDirection(getCurrentDirection()); + lDirection.setText(dir.toString()); + String text = ""; + if (getController() != null) { + int cycle = getController().getDecoder().getDirectionMap().getOrDefault(dir, -1); + text = "Cycle: " + cycle; + } + lDirection.setToolTipText(text); + } + + /** Initializes the sequence list with available animation sequences and preselects a suitable default sequence. */ + private void initSequences(SpriteDecoder decoder, Sequence defSeq) + { + modelSequences.removeAllElements(); + if (decoder != null) { + for (final Sequence seq : Sequence.values()) { + if (decoder.isSequenceAvailable(seq)) { + modelSequences.addElement(seq); + } + } + } + cbSequences.setEnabled(modelSequences.getSize() > 0); + + if (modelSequences.getSize() > 0) { + int idx = -1; + if (defSeq != null) { + // try given default sequence first + idx = modelSequences.getIndexOf(defSeq); + } + if (idx < 0) { + // try default sequence from list + for (final Sequence seq : Sequence.getDefaultSequences()) { + idx = modelSequences.getIndexOf(seq); + if (idx >= 0) { + break; + } + } + } + if (idx < 0) { + // fall back to first available sequence + idx = 0; + } + cbSequences.setSelectedIndex(idx); + } + } + + /** Initializes the direction slider and label with available directions. */ + private void initDirection(SpriteDecoder decoder, Direction defDir) + { + // discarding old data + slDirection.setMinimum(0); + slDirection.setMaximum(0); + directionMap.clear(); + + if (decoder != null) { + // collecting directions + List directions = new ArrayList<>(Direction.values().length * 2 + 1); + for (final Direction dir : decoder.getDirectionMap().keySet()) { + directions.add(Integer.valueOf(dir.getValue())); + } + + // sorting in descending order: maps relative slider positions to more natural directions + Collections.sort(directions, (a, b) -> b - a); + + int min = -directions.size() + 1; + int max = directions.size(); + // duplicate list entries + directions.addAll(new ArrayList<>(directions)); + // remove excess entries from left (negative) side + while (directions.size() > 1 && directions.get(0) > Direction.N.getValue()) { + directions.remove(0); + min++; + } + // remove excess entries from right (positive) side + while (directions.size() > 1 && directions.get(directions.size() - 1) < Direction.N.getValue()) { + directions.remove(directions.size() - 1); + max--; + } + + for (int i = min; i <= max; i++) { + int dirVal = directions.get(i - min); + Direction dir = Direction.from(dirVal); + directionMap.put(Integer.valueOf(i), dir); + } + + // initializing slider + slDirection.setMinimum(min); + slDirection.setMaximum(max); + // defining major ticks distance + Integer d1 = decoder.getDirectionMap().get(Direction.S); + if (d1 != null) { + Integer d2 = decoder.getDirectionMap().get(Direction.W); + if (d2 != null) { + slDirection.setMajorTickSpacing(d2 - d1); + } else { + d2 = decoder.getDirectionMap().get(Direction.N); + if (d2 != null) { + slDirection.setMajorTickSpacing((d2 - d1) / 2); + } + } + } + slDirection.setMinorTickSpacing(1); + + // defining direction labels + Hashtable labels = new Hashtable<>(); + for (int i = min; i <= max; i++) { + Direction dir = getDirection(i); + if (dir != null && (dir.getValue() % 4) == 0) { + labels.put(Integer.valueOf(i), new JLabel(dir.toString())); + } + } + slDirection.setLabelTable(labels); + + // restoring default direction + int value = 0; + defDir = decoder.getExistingDirection(defDir); + if (defDir != null) { + for (int i = slDirection.getMinimum(); i <= slDirection.getMaximum(); i++) { + if (defDir.equals(getDirection(i))) { + value = i; + break; + } + } + } + slDirection.setValue(value); + } + } + + /** Returns the {@code Direction} of the specified direction slider position. Defaults to {@code Direction.S}. */ + private Direction getDirection(int index) + { + return directionMap.getOrDefault(Integer.valueOf(index), Direction.S); + } + +//-------------------------- INNER CLASSES -------------------------- + + /** + * Listeners are outsourced to this class for cleaner code. + */ + private class Listeners implements ActionListener, ChangeListener, ItemListener + { + public Listeners() + { + } + + //--------------------- Begin Interface ActionListener --------------------- + + @Override + public void actionPerformed(ActionEvent e) + { + if (e.getSource() == timer) { + advanceFrame(); + } + else if (e.getSource() == bHome) { + setCurrentFrame(0); + updateControls(); + } + else if (e.getSource() == bEnd) { + setCurrentFrame(getMaxFrame() - 1); + updateControls(); + } + else if (e.getSource() == bStepBack) { + if (getCurrentFrame() > 0) { + setCurrentFrame(getCurrentFrame() - 1); + updateControls(); + } + } + else if (e.getSource() == bStepForward) { + if (getCurrentFrame() < getMaxFrame() - 1) { + setCurrentFrame(getCurrentFrame() + 1); + updateControls(); + } + } + else if (e.getSource() == bPlay) { + setRunning(!isRunning()); + } + else if (e.getSource() == bStop) { + stop(); + } + } + + //--------------------- End Interface ActionListener --------------------- + + //--------------------- Begin Interface ChangeListener --------------------- + @Override + public void stateChanged(ChangeEvent e) + { + if (e.getSource() == slDirection) { + Direction dir = getDirection(slDirection.getValue()); + lDirection.setText(dir.toString()); + int cycle = getBrowser().getDecoder().getDirectionMap().getOrDefault(dir, -1).intValue(); + if (cycle >= 0) { + setCurrentCycle(cycle); + } + updateControls(); + } + } + + //--------------------- End Interface ChangeListener --------------------- + + //--------------------- Begin Interface ItemListener --------------------- + @Override + public void itemStateChanged(ItemEvent e) + { + if (e.getSource() == cbSequences) { + if (e.getStateChange() == ItemEvent.SELECTED && + e.getItem() instanceof Sequence) { + final Sequence seq = (Sequence)e.getItem(); + try { + WindowBlocker.blockWindow(getBrowser(), true); + loadSequence(seq); + } catch (Exception ex) { + ex.printStackTrace(); + getBrowser().showErrorMessage(ex.getMessage(), "Sequence selection"); + } finally { + WindowBlocker.blockWindow(getBrowser(), false); + } + } + } + } + + //--------------------- End Interface ItemListener --------------------- + } +} diff --git a/src/org/infinity/resource/cre/browser/RenderPanel.java b/src/org/infinity/resource/cre/browser/RenderPanel.java new file mode 100644 index 000000000..6f21bf73c --- /dev/null +++ b/src/org/infinity/resource/cre/browser/RenderPanel.java @@ -0,0 +1,528 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.browser; + +import java.awt.AlphaComposite; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Composite; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Image; +import java.awt.Insets; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.event.ComponentEvent; +import java.awt.event.ComponentListener; +import java.util.Objects; + +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.UIManager; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import org.infinity.gui.RenderCanvas; +import org.infinity.gui.ViewerUtil; +import org.infinity.resource.cre.decoder.SpriteDecoder.SpriteBamControl; +import org.infinity.resource.graphics.ColorConvert; + +/** + * This panel handles drawing background and creature animations. + */ +public class RenderPanel extends JPanel +{ + private static final Color COLOR_TRANSPARENT = new Color(0, true); + private static final float POS_REL_X = 0.5f; + private static final float POS_REL_Y = 2.0f / 3.0f; + + private final Listeners listeners = new Listeners(); + private final CreatureBrowser browser; + // storage for scroll pane view size to track view size changes + private Dimension viewSize = new Dimension(); + + private JPanel pCanvas; + private RenderCanvas rcCanvas; + private JScrollPane spCanvas; + private Image backgroundImage; + private Color backgroundColor; + private Point backgroundCenter; + private Image frame; + private Rectangle frameBounds; + private Composite composite; + private float zoom; + private boolean isFiltering; + private boolean backgroundChanged; + // rectangle (position and size) of the BAM frame + private Rectangle frameRect; + // storage for background image content that is overwritten by the animation frame + private Image frameBackground; + + public RenderPanel(CreatureBrowser browser) + { + super(); + this.browser = Objects.requireNonNull(browser); + init(); + } + + /** Returns the associated {@code CreatureBrowser} instance. */ + public CreatureBrowser getBrowser() { return browser; } + + /** Returns the current background color of the panel. */ + public Color getBackgroundColor() + { + return (backgroundColor != null) ? backgroundColor : getDefaultBackgroundColor(); + } + + /** Sets the background color for the panel. */ + public void setBackgroundColor(Color c) + { + if (backgroundColor == null && c != null || + backgroundColor != null && !backgroundColor.equals(c)) { + this.backgroundColor = c; + pCanvas.setBackground(getBackgroundColor()); + backgroundChanged = true; + } + } + + /** Returns the background image used by the panel. Returns {@code null} if no image is defined. */ + public Image getBackgroundImage() + { + return backgroundImage; + } + + /** Sets the background image used by the panel. Specify {@code null} if no image should be displayed. */ + public void setBackgroundImage(Image image) + { + if (backgroundImage == null && image != null || + backgroundImage != null && !backgroundImage.equals(image)) { + if (backgroundImage != null) { + backgroundImage.flush(); + } + backgroundImage = image; + backgroundChanged = true; + + // clear frame background info + frameRect = null; + if (frameBackground != null) { + frameBackground.flush(); + } + frameBackground = null; + } + } + + /** + * Returns the center position for the creature animation on the background image. + * Returns a default center position if no explicit center is defined. + */ + public Point getBackgroundCenter() + { + if (backgroundCenter == null) { + if (backgroundImage != null) { + return new Point((int)(backgroundImage.getWidth(null) * POS_REL_X), + (int)(backgroundImage.getHeight(null) * POS_REL_Y)); + } else { + return new Point(); + } + } else { + return backgroundCenter; + } + } + + /** Sets the center position for the creature animation on the background image. */ + public void setBackgroundCenter(Point pt) + { + if (backgroundCenter == null && pt != null || + backgroundCenter != null && !backgroundCenter.equals(pt)) { + backgroundCenter = (pt != null) ? new Point(pt) : null; + } + } + + /** Returns the current zoom factor. */ + public float getZoom() { return zoom; } + + /** Sets a new zoom factor. Valid range: [0.01, 10.0] */ + public void setZoom(float zoom) throws IllegalArgumentException + { + if (zoom < 0.01f || zoom > 10.0f) { + throw new IllegalArgumentException("Zoom factor is out of range: " + zoom); + } + if (zoom != this.zoom) { + this.zoom = zoom; + } + } + + /** Returns whether bilinear filtering is enabled. */ + public boolean isFilterEnabled() + { + return isFiltering; + } + + /** Sets whether bilinear filtering is enabled {@code true} or nearest neighbor filtering is enabled {@code false}. */ + public void setFilterEnabled(boolean b) + { + if (b != isFiltering) { + isFiltering = b; + Object type = isFiltering ? RenderCanvas.TYPE_BILINEAR : RenderCanvas.TYPE_NEAREST_NEIGHBOR; + rcCanvas.setInterpolationType(type); + updateCanvas(); + } + } + + /** Returns the {@link Composite} object used to render the creature animation. */ + public Composite getComposite() + { + return (composite != null) ? composite : AlphaComposite.SrcOver; + } + + /** + * Sets the {@link Composite} object used to render the creature animation. + * Specify {@code null} to use the default {@code Composite}. + */ + public void setComposite(Composite c) + { + if (composite == null && c != null || + composite != null && !composite.equals(c)) { + composite = c; + updateCanvas(); + } + } + + /** Stores the active BAM frame and frame center from the specified {@code PseudoBamControl} object internally for display. */ + public void setFrame(SpriteBamControl ctrl) + { + if (ctrl != null) { + if (frameBounds == null) { + frameBounds = new Rectangle(); + } + frameBounds.setSize(ctrl.getSharedDimension()); + frameBounds.setLocation(ctrl.getSharedOrigin()); + if (frame == null || + frame.getWidth(null) != frameBounds.width || + frame.getHeight(null) != frameBounds.height) { + frame = ColorConvert.createCompatibleImage(frameBounds.width, frameBounds.height, true); + } else { + // clear old content + Graphics2D g = (Graphics2D)frame.getGraphics(); + try { + g.setComposite(AlphaComposite.Src); + g.setColor(COLOR_TRANSPARENT); + g.fillRect(0, 0, frame.getWidth(null), frame.getHeight(null)); + } finally { + g.dispose(); + } + } + ctrl.cycleGetFrame(frame); + } else { + frameBounds = null; + if (frame != null) { + frame.flush(); + } + frame = null; + } + } + + /** + * Attempts to center the viewport on the animation sprite. + * Does nothing if the whole canvas fits into the viewport. + */ + public void centerOnSprite() + { + Dimension dimExtent = spCanvas.getViewport().getExtentSize(); + Dimension dimCanvas = rcCanvas.getSize(); + if (dimCanvas.width > dimExtent.width || + dimCanvas.height > dimExtent.height) { + // calculating center point + int x = 0; + int y = 0; + if (backgroundCenter != null) { + x = backgroundCenter.x; + y = backgroundCenter.y; + } else if (frameBounds != null) { + x = -frameBounds.x; + y = -frameBounds.y; + } + if (frameBounds != null) { + // shift viewport center to frame center + x += frameBounds.x + frameBounds.width / 2; + y += frameBounds.y + frameBounds.height / 2; + } + + x = (int)(x * getZoom()); + y = (int)(y * getZoom()); + + // adjusting viewport + int cx = Math.max(0, x - (dimExtent.width / 2)); + int cy = Math.max(0, y - (dimExtent.height / 2)); + spCanvas.getViewport().setViewPosition(new Point(cx, cy)); + } + } + + /** Calculates the canvas dimension to fit background image and sprite frame. */ + private Dimension calculateImageDimension() + { + Dimension retVal = new Dimension(1, 1); + if (frameBounds != null) { + retVal.width = Math.max(retVal.width, frameBounds.width); + retVal.height = Math.max(retVal.height, frameBounds.height); + } + if (backgroundImage != null) { + retVal.width = Math.max(retVal.width, backgroundImage.getWidth(null)); + retVal.height = Math.max(retVal.height, backgroundImage.getHeight(null)); + } + return retVal; + } + + /** Ensures that the image associated with the {@code RenderCanvas} exists and is properly initialized. */ + private void ensureCanvasImage() + { + Image img; + Dimension dim = calculateImageDimension(); + if (rcCanvas.getImage() == null || + rcCanvas.getImage().getWidth(null) != dim.width || + rcCanvas.getImage().getHeight(null) != dim.height) { + img = ColorConvert.createCompatibleImage(dim.width, dim.height, true); + backgroundChanged = true; + } else { + img = rcCanvas.getImage(); + } + + if (backgroundChanged) { + Graphics2D g = (Graphics2D)img.getGraphics(); + try { + g.setComposite(AlphaComposite.Src); + int x = (backgroundImage != null) ? (dim.width - backgroundImage.getWidth(null)) / 2 : 0; + int y = (backgroundImage != null) ? (dim.height - backgroundImage.getHeight(null)) / 2 : 0; + if (backgroundImage == null || x > 0 || y > 0) { + g.setColor(getBackgroundColor()); + g.fillRect(0, 0, img.getWidth(null), img.getHeight(null)); + } + if (backgroundImage != null) { + g.drawImage(backgroundImage, x, y, null); + } + } finally { + g.dispose(); + } + rcCanvas.setImage(img); + frameRect = null; + frameBackground = null; + backgroundChanged = false; + } + + rcCanvas.setImage(img); + } + + /** Restores the background area overwritten by the previous animation frame. */ + private void restoreFrameBackground() + { + if (frameRect != null && frameBackground != null) { + Graphics2D g = (Graphics2D)rcCanvas.getImage().getGraphics(); + try { + g.setComposite(AlphaComposite.Src); + g.drawImage(frameBackground, frameRect.x, frameRect.y, null); + } finally { + g.dispose(); + } + } + } + + /** Stores the background area to be overwritten by the current animation frame. */ + private void storeFrameBackground() + { + if (frameBounds != null) { + if (frameRect == null) { + frameRect = new Rectangle(); + } + int x = (backgroundCenter != null) ? backgroundCenter.x : -frameBounds.x; + int y = (backgroundCenter != null) ? backgroundCenter.y : -frameBounds.y; + frameRect.x = x + frameBounds.x; + frameRect.y = y + frameBounds.y; + frameRect.width = frameBounds.width; + frameRect.height = frameBounds.height; + if (frameBackground == null || + frameBackground.getWidth(null) != frameRect.width || + frameBackground.getHeight(null) != frameRect.height) { + frameBackground = ColorConvert.createCompatibleImage(frameRect.width, frameRect.height, true); + } + Graphics2D g = (Graphics2D)frameBackground.getGraphics(); + try { + g.setComposite(AlphaComposite.Src); + g.drawImage(rcCanvas.getImage(), 0, 0, frameRect.width, frameRect.height, + frameRect.x, frameRect.y, frameRect.x + frameRect.width, frameRect.y + frameRect.height, + null); + } finally { + g.dispose(); + } + } else { + frameRect = null; + if (frameBackground != null) { + frameBackground.flush(); + } + frameBackground = null; + } + } + + /** Draws the current frame if available. */ + private void drawFrame() + { + if (frame != null) { + int x = (backgroundCenter != null) ? backgroundCenter.x : -frameBounds.x; + int y = (backgroundCenter != null) ? backgroundCenter.y : -frameBounds.y; + + Graphics2D g = (Graphics2D)rcCanvas.getImage().getGraphics(); + Point pos = new Point(x + frameBounds.x, y + frameBounds.y); + try { + // drawing markers + SpriteBamControl ctrl = getBrowser().getMediaPanel().getController(); + ctrl.getVisualMarkers(g, pos); + + // drawing frame + g.setComposite(getComposite()); + g.drawImage(frame, x + frameBounds.x, y + frameBounds.y, null); + } finally { + g.dispose(); + } + } + } + + /** Updates the display with the current background and BAM frame. */ + public void updateCanvas() + { + updateCanvas(false); + + // redraw canvas + repaint(); + } + + /** Internally used to update the canvas control. */ + protected void updateCanvas(boolean restore) + { + backgroundChanged |= restore; + + // recreate the canvas image if necessary + ensureCanvasImage(); + + // restore frame rect portion of background if necessary + restoreFrameBackground(); + + // store new frame rect portion of background + storeFrameBackground(); + + // draw frame + drawFrame(); + + // apply zoom factor + if (rcCanvas.getImage() != null) { + Dimension dim = new Dimension(rcCanvas.getImage().getWidth(null), rcCanvas.getImage().getHeight(null)); + dim.width *= getZoom(); + dim.height *= getZoom(); + rcCanvas.setPreferredSize(dim); + rcCanvas.setSize(dim); + } + + revalidate(); + } + + /** Returns the L&F-specific default background color of the {@code JPanel}. */ + private static Color getDefaultBackgroundColor() + { + return UIManager.getColor("Panel.background"); + } + + private void init() + { + GridBagConstraints c = new GridBagConstraints(); + + pCanvas = new JPanel(new GridBagLayout()); + rcCanvas = new RenderCanvas(null, true); + rcCanvas.addComponentListener(listeners); + + c = ViewerUtil.setGBC(c, 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER, + GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0); + pCanvas.add(rcCanvas, c); + + spCanvas = new JScrollPane(pCanvas); + spCanvas.setBorder(pCanvas.getBorder()); + spCanvas.getHorizontalScrollBar().setUnitIncrement(16); + spCanvas.getVerticalScrollBar().setUnitIncrement(16); + spCanvas.addComponentListener(listeners); + spCanvas.getViewport().addChangeListener(listeners); + + setLayout(new BorderLayout()); + add(spCanvas, BorderLayout.CENTER); + + // default settings + backgroundColor = null; + backgroundImage = null; + backgroundCenter = null; + frame = null; + frameBounds = null; + composite = null; + zoom = 1.0f; + isFiltering = false; + frameRect = null; + frameBackground = null; + } + +//-------------------------- INNER CLASSES -------------------------- + + private class Listeners implements ComponentListener, ChangeListener + { + public Listeners() + { + } + + //--------------------- Begin Interface ComponentListener --------------------- + + @Override + public void componentResized(ComponentEvent e) + { + if (e.getSource() == rcCanvas) { + // recenter view relative to sprite frame position and dimension + } + } + + @Override + public void componentMoved(ComponentEvent e) + { + } + + @Override + public void componentShown(ComponentEvent e) + { + } + + @Override + public void componentHidden(ComponentEvent e) + { + } + + //--------------------- End Interface ComponentListener --------------------- + + //--------------------- Begin Interface ChangeListener --------------------- + + @Override + public void stateChanged(ChangeEvent e) + { + if (e.getSource() == spCanvas.getViewport()) { + Dimension dimView = spCanvas.getViewport().getViewSize(); + if (!dimView.equals(viewSize)) { + // recenter view + viewSize.width = dimView.width; + viewSize.height = dimView.height; + centerOnSprite(); + } else { + viewSize.width = dimView.width; + viewSize.height = dimView.height; + } + } + } + + //--------------------- End Interface ChangeListener --------------------- + + } +} diff --git a/src/org/infinity/resource/cre/browser/SettingsPanel.java b/src/org/infinity/resource/cre/browser/SettingsPanel.java new file mode 100644 index 000000000..1562da58c --- /dev/null +++ b/src/org/infinity/resource/cre/browser/SettingsPanel.java @@ -0,0 +1,749 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.browser; + +import java.awt.AlphaComposite; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Composite; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Vector; +import java.util.stream.Collectors; + +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JColorChooser; +import javax.swing.JComboBox; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.colorchooser.AbstractColorChooserPanel; + +import org.infinity.gui.ViewerUtil; +import org.infinity.resource.Profile; +import org.infinity.resource.cre.browser.bg.Backgrounds; +import org.infinity.resource.cre.browser.bg.Backgrounds.BackgroundInfo; +import org.infinity.resource.cre.browser.icon.Icons; +import org.infinity.util.tuples.Monuple; + +/** + * This panel provides controls for visual settings and includes a table view of creature animation attributes. + */ +public class SettingsPanel extends JPanel +{ + // Available render canvas backgrounds + public static final List backgroundList = new ArrayList() {{ + add(Backgrounds.BG_COLOR_NONE); + add(Backgrounds.BG_CAVE_BG); + add(Backgrounds.BG_CITY_NIGHT_SOD); + add(Backgrounds.BG_WILDERNESS_BG); + add(Backgrounds.BG_WILDERNESS_IWD); + add(Backgrounds.BG_CITY_PST); + add(Backgrounds.BG_DUNGEON_PST); + add(Backgrounds.BG_COLOR_WHITE); + add(Backgrounds.BG_COLOR_BLACK); + add(Backgrounds.BG_COLOR_LIGHT_GRAY); + add(Backgrounds.BG_COLOR_GRAY); + // REMEMBER: has to be last entry in list + add(new Backgrounds.BackgroundInfo("Customize color...", Color.WHITE)); + }}; + + // Available items for zoom selection list + private static final Vector> zoomList = new Vector>() {{ + add(ItemString.with("50 %", 50)); + add(ItemString.with("100 % (original)", 100)); + add(ItemString.with("200 %", 200)); + add(ItemString.with("300 %", 300)); + add(ItemString.with("400 %", 400)); + add(ItemString.with("500 %", 500)); + }}; + + // Available items for frame rate selection list + private static final Vector> frameRateList = new Vector>() {{ + add(ItemString.with("1 frames/sec.", 1)); + add(ItemString.with("2 frames/sec.", 2)); + add(ItemString.with("5 frames/sec.", 5)); + add(ItemString.with("10 frames/sec.", 10)); + add(ItemString.with("15 frames/sec. (original)", 15)); + add(ItemString.with("20 frames/sec.", 20)); + add(ItemString.with("25 frames/sec.", 25)); + add(ItemString.with("30 frames/sec.", 30)); + add(ItemString.with("50 frames/sec.", 50)); + add(ItemString.with("60 frames/sec.", 60)); + }}; + + private static int indexZoom, indexFrameRate, indexBackground; + private static boolean isFiltering, isBlending, isTranslucent, isSelectionCircle, isOrnateSelectionCircle, isPersonalSpace, + isTintEnabled, isBlurEnabled, isPaletteReplacementEnabled, isShowBorders, + isShowAvatar, isShowHelmet, isShowShield, isShowWeapon; + + static { + resetSettings(); + } + + private final Listeners listeners = new Listeners(); + private final CreatureBrowser browser; + + private JComboBox> cbZoom; + private JComboBox> cbFrameRate; + private JComboBox cbBackground; + private JButton bCenter; + private JCheckBox cbFiltering, cbBlending, cbTranslucent, cbSelectionCircle, cbOrnateSelectionCircle, cbPersonalSpace, + cbTintEnabled, cbBlurEnabled, cbPaletteReplacementEnabled, cbShowBorders, + cbShowAvatar, cbShowHelmet, cbShowShield, cbShowWeapon; + private AttributesPanel panelAttributes; + + /** Returns a list of background info instances available for the specified game. */ + public static List getBackgrounds(Profile.Game game) + { + return backgroundList + .stream() + .filter(bi -> bi.getGames().contains((game != null) ? game : Profile.getGame())) + .collect(Collectors.toList()); + } + + /** Initializes global settings with sane defaults. */ + private static void resetSettings() + { + indexZoom = 1; // 100 % (original) + indexFrameRate = 4; // 15 fps (original) + indexBackground = 0; // System color + isFiltering = false; + isBlending = true; + isTranslucent = true; + isSelectionCircle = false; + isOrnateSelectionCircle = (Profile.getGame() == Profile.Game.PST) || (Profile.getGame() == Profile.Game.PSTEE); + isPersonalSpace = false; + isShowAvatar = true; + isShowHelmet = true; + isShowShield = true; + isShowWeapon = true; + isShowBorders = false; + isTintEnabled = true; + isBlurEnabled = true; + isPaletteReplacementEnabled = true; + } + + public SettingsPanel(CreatureBrowser browser) + { + super(); + this.browser = Objects.requireNonNull(browser); + resetSettings(); + init(); + } + + /** Returns the associated {@code CreatureBrowser} instance. */ + public CreatureBrowser getBrowser() { return browser; } + + /** Discards and reloads the current settings and attributes list. */ + public void reset() + { + applyBackgroundInfo(); + getAttributesPanel().reset(); + } + + /** Returns the currently selected zoom (in percent). */ + public int getZoom() + { + int retVal = 100; + ItemString is = cbZoom.getModel().getElementAt(indexZoom); + if (is != null) { + retVal = is.getData().intValue(); + } + return retVal; + } + + private void setZoomIndex(int index) + { + if (index != indexZoom) { + if (index >= 0 && index < cbZoom.getModel().getSize()) { + indexZoom = index; + cbZoom.setSelectedIndex(indexZoom); + getBrowser().getRenderPanel().setZoom((float)getZoom() / 100.0f); + getBrowser().getRenderPanel().updateCanvas(); + } else { + throw new IndexOutOfBoundsException(); + } + } + } + + /** Returns the currently selected frame rate. */ + public int getFrameRate() + { + int retVal = 15; + ItemString is = cbFrameRate.getModel().getElementAt(indexFrameRate); + if (is != null) { + retVal = is.getData().intValue(); + } + return retVal; + } + + private void setFrameRateIndex(int index) + { + if (index != indexFrameRate) { + if (index >= 0 && index < cbFrameRate.getModel().getSize()) { + indexFrameRate = index; + cbFrameRate.setSelectedIndex(indexFrameRate); + getBrowser().getMediaPanel().setFrameRate(getFrameRate()); + } else { + throw new IndexOutOfBoundsException(); + } + } + } + + /** Returns the selected {@code BackgroundInfo} object. */ + public Backgrounds.BackgroundInfo getBackgroundInfo() { return cbBackground.getModel().getElementAt(indexBackground); } + + private void setBackgroundInfoIndex(int index) + { + if (index != indexBackground || index == cbBackground.getModel().getSize() - 1) { + if (index >= 0 && index < cbBackground.getModel().getSize()) { + if (index == cbBackground.getModel().getSize() - 1) { + // special: define custom color + Backgrounds.BackgroundInfo info = cbBackground.getModel().getElementAt(index); + Color color = getCustomColor(info.getColor()); + if (color != null) { + info = cbBackground.getModel().getElementAt(index); + info.setColor(color); + info.setLabel(String.format("Customize color: RGB(%d,%d,%d)", color.getRed(), color.getGreen(), color.getBlue())); + } else { + cbBackground.setSelectedIndex(indexBackground); + return; + } + } + + indexBackground = index; + cbBackground.setSelectedIndex(indexBackground); + applyBackgroundInfo(); + } else { + throw new IndexOutOfBoundsException(); + } + } + } + + private void applyBackgroundInfo() + { + Backgrounds.BackgroundInfo info = getBackgroundInfo(); + if (info != null) { + getBrowser().getRenderPanel().setBackgroundColor(info.getColor()); + getBrowser().getRenderPanel().setBackgroundImage(info.getImage()); + getBrowser().getRenderPanel().setBackgroundCenter(info.getCenter()); + getBrowser().getRenderPanel().updateCanvas(); + } + } + + /** Returns whether bilinear filtering is enabled for sprite display. */ + public boolean isFilteringEnabled() { return isFiltering; } + + private void setFilteringEnabled(boolean b) + { + if (isFiltering != b) { + isFiltering = b; + cbFiltering.setSelected(isFiltering); + getBrowser().getRenderPanel().setFilterEnabled(isFiltering); + } + } + + /** Returns whether special blending effects are enabled for selected creature animations. */ + public boolean isBlendingEnabled() { return isBlending; } + + private void setBlendingEnabled(boolean b) + { + if (isBlending != b) { + isBlending = b; + cbBlending.setSelected(isBlending); + getBrowser().getRenderPanel().setComposite(getComposite()); + } + } + + /** Returns whether translucency is enabled for selected creature animations. */ + public boolean isTranslucencyEnabled() { return isTranslucent; } + + private void setTranslucencyEnabled(boolean b) + { + if (isTranslucent != b) { + isTranslucent = b; + cbTranslucent.setSelected(isTranslucent); + getBrowser().getMediaPanel().reset(true); + } + } + + /** Returns whether the selection circle is visible. */ + public boolean isSelectionCircleEnabled() { return isSelectionCircle; } + + private void setSelectionCircleEnabled(boolean b) + { + if (isSelectionCircle != b) { + isSelectionCircle = b; + cbSelectionCircle.setSelected(isSelectionCircle); + getBrowser().getDecoder().setSelectionCircleEnabled(isSelectionCircle); + getBrowser().getRenderPanel().updateCanvas(); + } + } + + /** Returns whether an ornate graphics is used to draw the selection circle. */ + public boolean isOrnateSelectionCircle() { return isOrnateSelectionCircle; } + + private void setOrnateSelectionCircle(boolean b) + { + if (isOrnateSelectionCircle != b) { + isOrnateSelectionCircle = b; + cbOrnateSelectionCircle.setSelected(isOrnateSelectionCircle); + getBrowser().getDecoder().setSelectionCircleBitmap(isOrnateSelectionCircle); + getBrowser().getRenderPanel().updateCanvas(); + } + } + + /** Returns whether personal space is visible. */ + public boolean isPersonalSpaceEnabled() { return isPersonalSpace; } + + private void setPersonalSpaceEnabled(boolean b) + { + if (isPersonalSpace != b) { + isPersonalSpace = b; + cbPersonalSpace.setSelected(isPersonalSpace); + getBrowser().getDecoder().setPersonalSpaceVisible(isPersonalSpace); + getBrowser().getRenderPanel().updateCanvas(); + } + } + + /** Returns whether tint effects (e.g. from opcodes 51/52) are enabled. */ + public boolean isTintEnabled() { return isTintEnabled; } + + private void setTintEnabled(boolean b) + { + if (isTintEnabled != b) { + isTintEnabled = b; + cbTintEnabled.setSelected(isTintEnabled); + getBrowser().getMediaPanel().reset(true); + } + } + + /** Returns whether blur effect (opcode 66) is enabled. */ + public boolean isBlurEnabled() { return isBlurEnabled; } + + private void setBlurEnabled(boolean b) + { + if (isBlurEnabled != b) { + isBlurEnabled = b; + cbBlurEnabled.setSelected(isBlurEnabled); + getBrowser().getMediaPanel().reset(true); + } + } + + /** Returns whether palette replacement (full palette or false colors) is enabled. */ + public boolean isPaletteReplacementEnabled() { return isPaletteReplacementEnabled; } + + private void setPaletteReplacementEnabled(boolean b) + { + if (isPaletteReplacementEnabled != b) { + isPaletteReplacementEnabled = b; + cbPaletteReplacementEnabled.setSelected(isPaletteReplacementEnabled); + getBrowser().getMediaPanel().reset(true); + } + } + + /** Returns whether the creature animation avatar is drawn. */ + public boolean isAvatarVisible() { return isShowAvatar; } + + private void setAvatarVisible(boolean b) + { + if (isShowAvatar != b) { + isShowAvatar = b; + cbShowAvatar.setSelected(isShowAvatar); + getBrowser().getMediaPanel().reset(true); + } + } + + /** Returns whether the helmet overlay is drawn. */ + public boolean isHelmetVisible() { return isShowHelmet; } + + private void setHelmetVisible(boolean b) + { + if (isShowHelmet != b) { + isShowHelmet = b; + cbShowHelmet.setSelected(isShowHelmet); + getBrowser().getMediaPanel().reset(true); + } + } + + /** Returns whether the shield overlay is drawn. */ + public boolean isShieldVisible() { return isShowShield; } + + private void setShieldVisible(boolean b) + { + if (isShowShield != b) { + isShowShield = b; + cbShowShield.setSelected(isShowShield); + getBrowser().getMediaPanel().reset(true); + } + } + + /** Returns whether the weapon overlay is drawn. */ + public boolean isWeaponVisible() { return isShowWeapon; } + + private void setWeaponVisible(boolean b) + { + if (isShowWeapon != b) { + isShowWeapon = b; + cbShowWeapon.setSelected(isShowWeapon); + getBrowser().getMediaPanel().reset(true); + } + } + + /** Returns whether colorered borders are drawn around sprite avatar and overlays. */ + public boolean isOverlayBordersVisible() { return isShowBorders; } + + private void setOverlayBordersVisible(boolean b) + { + if (isShowBorders != b) { + isShowBorders = b; + cbShowBorders.setSelected(isShowBorders); + getBrowser().getMediaPanel().reset(true); + } + } + + /** Returns the {@link Composite} object valid for the currently loaded {@code SpriteDecoder} instance. */ + public Composite getComposite() + { + Composite retVal = AlphaComposite.SrcOver; + if (getBrowser().getDecoder() != null && isBlendingEnabled()) { + retVal = getBrowser().getDecoder().getComposite(); + } + return retVal; + } + + /** Provides access to the creature animation attributes panel. */ + public AttributesPanel getAttributesPanel() { return panelAttributes; } + + private void init() + { + GridBagConstraints c = new GridBagConstraints(); + + // selection controls + JLabel l1 = new JLabel("Zoom:"); + cbZoom = new JComboBox<>(zoomList); + cbZoom.setPrototypeDisplayValue(zoomList.get(1)); + cbZoom.setSelectedIndex(indexZoom); + cbZoom.addActionListener(listeners); + + bCenter = new JButton(Icons.getIcon(Icons.ICON_CENTER)); + bCenter.setToolTipText("Center display on creature animation."); + bCenter.addActionListener(listeners); + + JLabel l2 = new JLabel("Frame rate:"); + cbFrameRate = new JComboBox<>(frameRateList); + cbFrameRate.setPrototypeDisplayValue(frameRateList.get(2)); + cbFrameRate.setSelectedIndex(indexFrameRate); + cbFrameRate.addActionListener(listeners); + + JLabel l3 = new JLabel("Background:"); + List bgList = getBackgrounds(Profile.getGame()); + cbBackground = new JComboBox<>(bgList.toArray(new Backgrounds.BackgroundInfo[bgList.size()])); + cbBackground.setPrototypeDisplayValue(backgroundList.get(1)); + cbBackground.setSelectedIndex(indexBackground); + cbBackground.addActionListener(listeners); + + JPanel panel1 = new JPanel(new GridBagLayout()); + c = ViewerUtil.setGBC(c, 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_END, + GridBagConstraints.VERTICAL, new Insets(0, 0, 0, 0), 0, 0); + panel1.add(l1, c); + c = ViewerUtil.setGBC(c, 1, 0, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(0, 4, 0, 0), 0, 0); + panel1.add(cbZoom, c); + c = ViewerUtil.setGBC(c, 2, 0, 1, 1, 0.0, 1.0, GridBagConstraints.LINE_START, + GridBagConstraints.VERTICAL, new Insets(0, 8, 0, 0), 0, 0); + panel1.add(bCenter, c); + + c = ViewerUtil.setGBC(c, 0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_END, + GridBagConstraints.VERTICAL, new Insets(8, 0, 0, 0), 0, 0); + panel1.add(l2, c); + c = ViewerUtil.setGBC(c, 1, 1, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(8, 4, 0, 0), 0, 0); + panel1.add(cbFrameRate, c); + + c = ViewerUtil.setGBC(c, 0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_END, + GridBagConstraints.VERTICAL, new Insets(8, 0, 0, 0), 0, 0); + panel1.add(l3, c); + c = ViewerUtil.setGBC(c, 1, 2, 2, 1, 1.0, 0.0, GridBagConstraints.LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(8, 4, 0, 0), 0, 0); + panel1.add(cbBackground, c); + + + // checkbox controls + cbFiltering = new JCheckBox("Enable filtering", isFiltering); + cbFiltering.setToolTipText("On: bilinear filtering, off: nearest neighbor filtering"); + cbFiltering.addActionListener(listeners); + + cbBlending = new JCheckBox("Enable blending", isBlending); + cbBlending.setToolTipText("Affects only creature animations with special blending attributes (e.g. movanic devas or wisps)."); + cbBlending.addActionListener(listeners); + + cbTranslucent = new JCheckBox("Enable translucency", isTranslucent); + cbTranslucent.setToolTipText("Affects only creature animations with translucency effect (e.g. ghosts or air elementals)."); + cbTranslucent.addActionListener(listeners); + + cbTintEnabled = new JCheckBox("Enable tint effect", isTintEnabled); + cbTintEnabled.setToolTipText("Includes color modifications defined by effect opcodes 8, 51 and 52."); + cbTintEnabled.addActionListener(listeners); + + cbBlurEnabled = new JCheckBox("Enable blur effect", isBlurEnabled); + cbBlurEnabled.addActionListener(listeners); + + cbPaletteReplacementEnabled = new JCheckBox("Enable palette replacement", isPaletteReplacementEnabled); + cbPaletteReplacementEnabled.setToolTipText("Enable full palette or false color palette replacement."); + cbPaletteReplacementEnabled.addActionListener(listeners); + + cbSelectionCircle = new JCheckBox("Show selection circle", isSelectionCircle); + cbSelectionCircle.addActionListener(listeners); + + cbOrnateSelectionCircle = new JCheckBox("Use ornate selection circle", isOrnateSelectionCircle); + cbOrnateSelectionCircle.setToolTipText("Enable to use the ornate selection circle graphics from PST."); + cbOrnateSelectionCircle.addActionListener(listeners); + + cbPersonalSpace = new JCheckBox("Show personal space", isPersonalSpace); + cbPersonalSpace.setToolTipText("Enable to visualize the search map cells blocked by the creature."); + cbPersonalSpace.addActionListener(listeners); + + cbShowAvatar = new JCheckBox("Show avatar overlay", isShowAvatar); + cbShowAvatar.setToolTipText("Includes the avatar, separate shadows and ground layers of buried creatures."); + cbShowAvatar.addActionListener(listeners); + + cbShowHelmet = new JCheckBox("Show helmet overlay", isShowHelmet); + cbShowHelmet.addActionListener(listeners); + + cbShowShield = new JCheckBox("Show shield overlay", isShowShield); + cbShowShield.setToolTipText("Includes shields and offhand weapons."); + cbShowShield.addActionListener(listeners); + + cbShowWeapon = new JCheckBox("Show weapon overlay", isShowWeapon); + cbShowWeapon.addActionListener(listeners); + + cbShowBorders = new JCheckBox("Show overlay borders", isShowBorders); + cbShowBorders.setToolTipText("Draw bounding boxes around individual segments of the creature animation."); + cbShowBorders.addActionListener(listeners); + + JPanel panel2 = new JPanel(new GridBagLayout()); + c = ViewerUtil.setGBC(c, 0, 0, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); + panel2.add(cbFiltering, c); + c = ViewerUtil.setGBC(c, 1, 0, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(0, 16, 0, 0), 0, 0); + panel2.add(cbBlending, c); + + c = ViewerUtil.setGBC(c, 0, 1, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(8, 0, 0, 0), 0, 0); + panel2.add(cbSelectionCircle, c); + c = ViewerUtil.setGBC(c, 1, 1, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(8, 16, 0, 0), 0, 0); + panel2.add(cbShowAvatar, c); + + c = ViewerUtil.setGBC(c, 0, 2, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(8, 0, 0, 0), 0, 0); + panel2.add(cbOrnateSelectionCircle, c); + c = ViewerUtil.setGBC(c, 1, 2, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(8, 16, 0, 0), 0, 0); + panel2.add(cbShowHelmet, c); + + c = ViewerUtil.setGBC(c, 0, 3, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(8, 0, 0, 0), 0, 0); + panel2.add(cbPersonalSpace, c); + c = ViewerUtil.setGBC(c, 1, 3, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(8, 16, 0, 0), 0, 0); + panel2.add(cbShowWeapon, c); + + c = ViewerUtil.setGBC(c, 0, 4, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(8, 0, 0, 0), 0, 0); + panel2.add(cbTranslucent, c); + c = ViewerUtil.setGBC(c, 1, 4, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(8, 16, 0, 0), 0, 0); + panel2.add(cbShowShield, c); + + c = ViewerUtil.setGBC(c, 0, 5, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(8, 0, 0, 0), 0, 0); + panel2.add(cbBlurEnabled, c); + c = ViewerUtil.setGBC(c, 1, 5, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(8, 16, 0, 0), 0, 0); + panel2.add(cbShowBorders, c); + + c = ViewerUtil.setGBC(c, 0, 6, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(8, 0, 0, 0), 0, 0); + panel2.add(cbTintEnabled, c); + c = ViewerUtil.setGBC(c, 1, 6, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(8, 16, 0, 0), 0, 0); + panel2.add(cbPaletteReplacementEnabled, c); + + + // attributes table panel + panelAttributes = new AttributesPanel(getBrowser()); + + + // combining panels + JPanel panelMain = new JPanel(new GridBagLayout()); + c = ViewerUtil.setGBC(c, 0, 0, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(8, 8, 0, 8), 0, 0); + panelMain.add(panel1, c); + + c = ViewerUtil.setGBC(c, 0, 1, 1, 1, 1.0, 0.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.HORIZONTAL, new Insets(16, 8, 0, 8), 0, 0); + panelMain.add(panel2, c); + + c = ViewerUtil.setGBC(c, 0, 2, 1, 1, 1.0, 1.0, GridBagConstraints.FIRST_LINE_START, + GridBagConstraints.BOTH, new Insets(16, 8, 8, 8), 0, 0); + panelMain.add(panelAttributes, c); + + JScrollPane scroll = new JScrollPane(panelMain); + scroll.setBorder(panelMain.getBorder()); + + setLayout(new BorderLayout()); + add(scroll, BorderLayout.CENTER); + } + + /** Returns a color from user input. */ + private Color getCustomColor(Color defColor) + { + final JColorChooser cc = new JColorChooser((defColor != null) ? defColor : Color.WHITE); + + // We only need the RGB panel + AbstractColorChooserPanel rgbPanel = null; + for (final AbstractColorChooserPanel panel : cc.getChooserPanels()) { + if (panel.getDisplayName().toUpperCase().contains("RGB")) { + rgbPanel = panel; + } + } + if (rgbPanel != null) { + for (final AbstractColorChooserPanel panel : cc.getChooserPanels()) { + if (panel != rgbPanel) { + cc.removeChooserPanel(panel); + } + } + } + + final Monuple retVal = Monuple.with(null); + JDialog dlg = null; + try { + // Returns color value without alpha component + dlg = JColorChooser.createDialog(getBrowser(), "Choose background color", true, cc, + evt -> retVal.setValue0(new Color(cc.getColor().getRGB(), false)), null); + dlg.setVisible(true); + } finally { + if (dlg != null) { + dlg.dispose(); + dlg = null; + } + } + return retVal.getValue0(); + } + +//-------------------------- INNER CLASSES -------------------------- + + private class Listeners implements ActionListener + { + public Listeners() + { + } + + //--------------------- Begin Interface ActionListener --------------------- + + @Override + public void actionPerformed(ActionEvent e) + { + if (e.getSource() == bCenter) { + getBrowser().getRenderPanel().centerOnSprite(); + } + else if (e.getSource() == cbZoom) { + setZoomIndex(cbZoom.getSelectedIndex()); + } + else if (e.getSource() == cbFrameRate) { + setFrameRateIndex(cbFrameRate.getSelectedIndex()); + } + else if (e.getSource() == cbBackground) { + setBackgroundInfoIndex(cbBackground.getSelectedIndex()); + } + else if (e.getSource() == cbFiltering) { + setFilteringEnabled(cbFiltering.isSelected()); + } + else if (e.getSource() == cbBlending) { + setBlendingEnabled(cbBlending.isSelected()); + } + else if (e.getSource() == cbTranslucent) { + setTranslucencyEnabled(cbTranslucent.isSelected()); + } + else if (e.getSource() == cbTintEnabled) { + setTintEnabled(cbTintEnabled.isSelected()); + } + else if (e.getSource() == cbBlurEnabled) { + setBlurEnabled(cbBlurEnabled.isSelected()); + } + else if (e.getSource() == cbPaletteReplacementEnabled) { + setPaletteReplacementEnabled(cbPaletteReplacementEnabled.isSelected()); + } + else if (e.getSource() == cbSelectionCircle) { + setSelectionCircleEnabled(cbSelectionCircle.isSelected()); + } + else if (e.getSource() == cbOrnateSelectionCircle) { + setOrnateSelectionCircle(cbOrnateSelectionCircle.isSelected()); + } + else if (e.getSource() == cbPersonalSpace) { + setPersonalSpaceEnabled(cbPersonalSpace.isSelected()); + } + else if (e.getSource() == cbShowAvatar) { + setAvatarVisible(cbShowAvatar.isSelected()); + } + else if (e.getSource() == cbShowHelmet) { + setHelmetVisible(cbShowHelmet.isSelected()); + } + else if (e.getSource() == cbShowShield) { + setShieldVisible(cbShowShield.isSelected()); + } + else if (e.getSource() == cbShowWeapon) { + setWeaponVisible(cbShowWeapon.isSelected()); + } + else if (e.getSource() == cbShowBorders) { + setOverlayBordersVisible(cbShowBorders.isSelected()); + } + } + + //--------------------- Begin Interface ActionListener --------------------- + } + + /** + * Helper class: String with attached user-defined data. + * + * @param type of the user-defined data + */ + private static class ItemString + { + private final T data; + private final String text; + + public static ItemString with(String text, T data) + { + return new ItemString<>(text, data); + } + + public ItemString(String text, T data) + { + this.text = (text != null) ? text : ""; + this.data = data; + } + + public T getData() { return data; } + + @Override + public String toString() + { + return text; + } + } +} diff --git a/src/org/infinity/resource/cre/browser/bg/Backgrounds.java b/src/org/infinity/resource/cre/browser/bg/Backgrounds.java new file mode 100644 index 000000000..717473dcc --- /dev/null +++ b/src/org/infinity/resource/cre/browser/bg/Backgrounds.java @@ -0,0 +1,183 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.browser.bg; + +import java.awt.Color; +import java.awt.Image; +import java.awt.Point; +import java.net.URL; +import java.util.EnumSet; +import java.util.Objects; + +import javax.imageio.ImageIO; +import javax.swing.UIManager; + +import org.infinity.resource.Profile; + +/** + * Backgrounds for the creature animation browser. + */ +public final class Backgrounds +{ + public static final BackgroundInfo BG_WILDERNESS_BG = new BackgroundInfo("Wilderness (BG2)", new Color(0x282f12), + "bg_wilderness.jpg", new Point(640, 632), + EnumSet.complementOf(EnumSet.of(Profile.Game.PST, Profile.Game.PSTEE))); + public static final BackgroundInfo BG_CAVE_BG = new BackgroundInfo("Cave (BG2)", new Color(0x010101), + "bg_cave.jpg", new Point(640, 554), + EnumSet.complementOf(EnumSet.of(Profile.Game.PST, Profile.Game.PSTEE))); + public static final BackgroundInfo BG_CITY_NIGHT_SOD = new BackgroundInfo("City at night (SoD)", new Color(0x12151c), + "sod_city_night.jpg", new Point(620, 530), + EnumSet.complementOf(EnumSet.of(Profile.Game.PST, Profile.Game.PSTEE))); + public static final BackgroundInfo BG_WILDERNESS_IWD = new BackgroundInfo("Wilderness (IWD)", new Color(0xd3deda), + "iwd_wilderness.jpg", new Point(640, 554), + EnumSet.complementOf(EnumSet.of(Profile.Game.PST, Profile.Game.PSTEE))); + public static final BackgroundInfo BG_CITY_PST = new BackgroundInfo("Sigil city (PST)", new Color(0x494131), + "pst_city.jpg", new Point(640, 580), + EnumSet.of(Profile.Game.PST, Profile.Game.PSTEE)); + public static final BackgroundInfo BG_DUNGEON_PST = new BackgroundInfo("Sigil dungeon (PST)", new Color(0x010101), + "pst_dungeon.jpg", new Point(640, 480), + EnumSet.of(Profile.Game.PST, Profile.Game.PSTEE)); + public static final BackgroundInfo BG_COLOR_NONE = new BackgroundInfo("System color", null); + public static final BackgroundInfo BG_COLOR_WHITE = new BackgroundInfo("White color", Color.WHITE); + public static final BackgroundInfo BG_COLOR_BLACK = new BackgroundInfo("Black color", Color.BLACK); + public static final BackgroundInfo BG_COLOR_LIGHT_GRAY = new BackgroundInfo("Light gray color", Color.LIGHT_GRAY); + public static final BackgroundInfo BG_COLOR_GRAY = new BackgroundInfo("Gray color", Color.GRAY); + public static final BackgroundInfo BG_COLOR_RED = new BackgroundInfo("Red color", Color.RED); + public static final BackgroundInfo BG_COLOR_GREEN = new BackgroundInfo("Green color", Color.GREEN); + public static final BackgroundInfo BG_COLOR_BLUE = new BackgroundInfo("Blue color", Color.BLUE); + public static final BackgroundInfo BG_COLOR_YELLOW = new BackgroundInfo("Yellow color", Color.YELLOW); + public static final BackgroundInfo BG_COLOR_MAGENTA = new BackgroundInfo("Magenta color", Color.MAGENTA); + public static final BackgroundInfo BG_COLOR_CYAN = new BackgroundInfo("Cyan color", Color.CYAN); + + /** + * Returns an {@link Image} object of the specified graphics filename. + * @param fileName The graphics filename. + * @return The {@link Image} object, or {@code null} on error. + */ + public static Image getImage(String fileName) + { + return getImage(null, fileName); + } + + /** + * Returns an {@link Image} object of the specified graphics filename. + * @param c A class located in the same package as the specified graphics file. The full package name + * of the class will be used to determine the correct path of the graphics file. + * @param fileName The graphics filename. + * @return The {@link Image} object, or {@code null} on error. + */ + public static Image getImage(Class c, String fileName) + { + Image retVal = null; + if (fileName == null || fileName.isEmpty()) { + return retVal; + } + + try { + URL url = getValidURL(c, fileName); + retVal = ImageIO.read(url); + } catch (Exception e) { + e.printStackTrace(); + } + return retVal; + } + + // Returns a URL instance that points to the specified filename + private static URL getValidURL(Class c, String fileName) + { + URL retVal = null; + if (fileName != null && !fileName.isEmpty()) { + if (c == null) { + retVal = ClassLoader.getSystemResource(fileName); + } + if (retVal == null) { + if (c == null) { + c = Backgrounds.class; + } + String basePath = c.getPackage().getName().replace('.', '/'); + String separator = (fileName.charAt(0) == '/') ? "" : "/"; + retVal = ClassLoader.getSystemResource(basePath + separator + fileName); + } + } + return retVal; + } + + private Backgrounds() {} + +//-------------------------- INNER CLASSES -------------------------- + + public static class BackgroundInfo + { + private final EnumSet games; + + private String label; + private Color color; + private String imageName; + private Point center; + + public BackgroundInfo(String label, Color bgColor) + { + this(label, bgColor, null, null, null); + } + + public BackgroundInfo(String label, Color bgColor, String imageName, Point center, EnumSet games) + { + this.label = Objects.requireNonNull(label, "Description label required"); + this.color = bgColor; + this.imageName = imageName; + this.center = center; + this.games = (games != null) ? games : EnumSet.allOf(Profile.Game.class); + } + + /** Returns the set of games which are compatible with this {@code BackgroundInfo}. */ + public EnumSet getGames() { return games; } + + /** Returns the descriptive label of the background entry. */ + public String getLabel() { return label; } + + public void setLabel(String l) + { + if (!this.label.equals(l)) { + this.label = Objects.requireNonNull(l, "Description label required"); + } + } + + /** Returns the background image filename. Returns {@code null} if background image has not been defined. */ + public String getImageName() { return imageName; } + + /** Sets a new background image filename. Specify {@code null} to clear background image. */ + public void setImageName(String name) { this.imageName = name; } + + /** Returns the background image. Returns {@code null} if no background image is defined. */ + public Image getImage() { return hasImage() ? Backgrounds.getImage(imageName) : null; } + + /** Returns the background color. */ + public Color getColor() { + Color retVal = (color != null) ? color : UIManager.getColor("Panel.background"); + if (retVal == null) { + retVal = Color.LIGHT_GRAY; + } + return retVal; + } + + /** Sets a new background color. Specify {@code null} to use system-dependent color. */ + public void setColor(Color col) { this.color = col; } + + /** Returns the center point of the associated image. Returns {@code null} if center has not been defined. */ + public Point getCenter() { return center; } + + /** Sets a new center point for the image. */ + public void setCenter(Point p) { this.center = p; } + + /** Returns whether the background is available for the current game. */ + public boolean isAvailable() { return (games == null) || games.contains(Profile.getGame()); } + + /** Returns whether the background provides an image. */ + public boolean hasImage() { return imageName != null; } + + @Override + public String toString() { return label; } + } +} diff --git a/src/org/infinity/resource/cre/browser/bg/bg_cave.jpg b/src/org/infinity/resource/cre/browser/bg/bg_cave.jpg new file mode 100644 index 000000000..ddaa6b526 Binary files /dev/null and b/src/org/infinity/resource/cre/browser/bg/bg_cave.jpg differ diff --git a/src/org/infinity/resource/cre/browser/bg/bg_wilderness.jpg b/src/org/infinity/resource/cre/browser/bg/bg_wilderness.jpg new file mode 100644 index 000000000..1a1f0a0c3 Binary files /dev/null and b/src/org/infinity/resource/cre/browser/bg/bg_wilderness.jpg differ diff --git a/src/org/infinity/resource/cre/browser/bg/iwd_wilderness.jpg b/src/org/infinity/resource/cre/browser/bg/iwd_wilderness.jpg new file mode 100644 index 000000000..bec5c5c43 Binary files /dev/null and b/src/org/infinity/resource/cre/browser/bg/iwd_wilderness.jpg differ diff --git a/src/org/infinity/resource/cre/browser/bg/pst_city.jpg b/src/org/infinity/resource/cre/browser/bg/pst_city.jpg new file mode 100644 index 000000000..fd6c9ccaf Binary files /dev/null and b/src/org/infinity/resource/cre/browser/bg/pst_city.jpg differ diff --git a/src/org/infinity/resource/cre/browser/bg/pst_dungeon.jpg b/src/org/infinity/resource/cre/browser/bg/pst_dungeon.jpg new file mode 100644 index 000000000..1c8a60df0 Binary files /dev/null and b/src/org/infinity/resource/cre/browser/bg/pst_dungeon.jpg differ diff --git a/src/org/infinity/resource/cre/browser/bg/sod_city_night.jpg b/src/org/infinity/resource/cre/browser/bg/sod_city_night.jpg new file mode 100644 index 000000000..23006fc18 Binary files /dev/null and b/src/org/infinity/resource/cre/browser/bg/sod_city_night.jpg differ diff --git a/src/org/infinity/resource/cre/browser/icon/Icons.java b/src/org/infinity/resource/cre/browser/icon/Icons.java new file mode 100644 index 000000000..956b16b04 --- /dev/null +++ b/src/org/infinity/resource/cre/browser/icon/Icons.java @@ -0,0 +1,111 @@ +// 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.cre.browser.icon; + +import java.awt.Image; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; + +import javax.swing.ImageIcon; + +public class Icons +{ + public static final String ICON_END = "btn_end.png"; + public static final String ICON_HOME = "btn_home.png"; + public static final String ICON_PAUSE = "btn_pause.png"; + public static final String ICON_PLAY = "btn_play.png"; + public static final String ICON_RESUME = "btn_resume.png"; + public static final String ICON_STEP_BACK = "btn_step_back.png"; + public static final String ICON_STEP_FORWARD = "btn_step_forward.png"; + public static final String ICON_STOP = "btn_stop.png"; + public static final String ICON_CENTER = "btn_center.png"; + + public static final String ICON_CIRCLE_GREEN = "circle_green.png"; + public static final String ICON_CIRCLE_BLUE = "circle_blue.png"; + public static final String ICON_CIRCLE_RED = "circle_red.png"; + public static final String ICON_CIRCLE_YELLOW = "circle_yellow.png"; + + private static final Map ICONMAP = new HashMap<>(20); + + /** + * Returns an ImageIcon object of the specified graphics filename. + * @param name The graphics filename, can include a path relative to the current class path. + * @return The ImageIcon object, or {@code null} on error. + */ + public static ImageIcon getIcon(String name) + { + return getIcon(null, name); + } + + /** + * Returns an ImageIcon object of the specified graphics filename. + * @param c A class located in the same package as the specified graphics file. The full package name + * of the class will be used to determine the correct path of the graphics file. + * @param fileName The graphics filename. + * @return The ImageIcon object, or {@code null} on error. + */ + public static ImageIcon getIcon(Class c, String fileName) + { + URL url = getValidURL(c, fileName); + if (url != null) { + ImageIcon icon = ICONMAP.get(url); + if (icon == null) { + icon = new ImageIcon(url); + ICONMAP.put(url, icon); + } + return icon; + } + return null; + } + + /** + * Returns an Image object of the specified graphics filename. + * @param fileName The graphics filename, can include a path relative to the current class path. + * @return The Image object, or {@code null} on error. + */ + public static Image getImage(String fileName) + { + return getImage(null, fileName); + } + + /** + * Returns an Image object of the specified graphics filename. + * @param c A class located in the same package as the specified graphics file. The full package name + * of the class will be used to determine the correct path of the graphics file. + * @param fileName The graphics filename. + * @return The Image object, or {@code null} on error. + */ + public static Image getImage(Class c, String fileName) + { + ImageIcon icon = getIcon(c, fileName); + if (icon != null) { + return icon.getImage(); + } + return null; + } + + // Returns a URL instance that points to the specified filename + private static URL getValidURL(Class c, String fileName) + { + URL retVal = null; + if (fileName != null && !fileName.isEmpty()) { + if (c == null) { + retVal = ClassLoader.getSystemResource(fileName); + } + if (retVal == null) { + if (c == null) { + c = Icons.class; + } + String basePath = c.getPackage().getName().replace('.', '/'); + String separator = (fileName.charAt(0) == '/') ? "" : "/"; + retVal = ClassLoader.getSystemResource(basePath + separator + fileName); + } + } + return retVal; + } + + protected Icons(){} +} diff --git a/src/org/infinity/resource/cre/browser/icon/btn_center.png b/src/org/infinity/resource/cre/browser/icon/btn_center.png new file mode 100644 index 000000000..2ee4dd60a Binary files /dev/null and b/src/org/infinity/resource/cre/browser/icon/btn_center.png differ diff --git a/src/org/infinity/resource/cre/browser/icon/btn_end.png b/src/org/infinity/resource/cre/browser/icon/btn_end.png new file mode 100644 index 000000000..0e76798f0 Binary files /dev/null and b/src/org/infinity/resource/cre/browser/icon/btn_end.png differ diff --git a/src/org/infinity/resource/cre/browser/icon/btn_home.png b/src/org/infinity/resource/cre/browser/icon/btn_home.png new file mode 100644 index 000000000..37aaed884 Binary files /dev/null and b/src/org/infinity/resource/cre/browser/icon/btn_home.png differ diff --git a/src/org/infinity/resource/cre/browser/icon/btn_pause.png b/src/org/infinity/resource/cre/browser/icon/btn_pause.png new file mode 100644 index 000000000..018649574 Binary files /dev/null and b/src/org/infinity/resource/cre/browser/icon/btn_pause.png differ diff --git a/src/org/infinity/resource/cre/browser/icon/btn_play.png b/src/org/infinity/resource/cre/browser/icon/btn_play.png new file mode 100644 index 000000000..7631865f6 Binary files /dev/null and b/src/org/infinity/resource/cre/browser/icon/btn_play.png differ diff --git a/src/org/infinity/resource/cre/browser/icon/btn_resume.png b/src/org/infinity/resource/cre/browser/icon/btn_resume.png new file mode 100644 index 000000000..ceea20d31 Binary files /dev/null and b/src/org/infinity/resource/cre/browser/icon/btn_resume.png differ diff --git a/src/org/infinity/resource/cre/browser/icon/btn_step_back.png b/src/org/infinity/resource/cre/browser/icon/btn_step_back.png new file mode 100644 index 000000000..3321ed9bf Binary files /dev/null and b/src/org/infinity/resource/cre/browser/icon/btn_step_back.png differ diff --git a/src/org/infinity/resource/cre/browser/icon/btn_step_forward.png b/src/org/infinity/resource/cre/browser/icon/btn_step_forward.png new file mode 100644 index 000000000..a53ccf2e1 Binary files /dev/null and b/src/org/infinity/resource/cre/browser/icon/btn_step_forward.png differ diff --git a/src/org/infinity/resource/cre/browser/icon/btn_stop.png b/src/org/infinity/resource/cre/browser/icon/btn_stop.png new file mode 100644 index 000000000..242508466 Binary files /dev/null and b/src/org/infinity/resource/cre/browser/icon/btn_stop.png differ diff --git a/src/org/infinity/resource/cre/browser/icon/circle_blue.png b/src/org/infinity/resource/cre/browser/icon/circle_blue.png new file mode 100644 index 000000000..607414cc0 Binary files /dev/null and b/src/org/infinity/resource/cre/browser/icon/circle_blue.png differ diff --git a/src/org/infinity/resource/cre/browser/icon/circle_green.png b/src/org/infinity/resource/cre/browser/icon/circle_green.png new file mode 100644 index 000000000..ff0a7e0fc Binary files /dev/null and b/src/org/infinity/resource/cre/browser/icon/circle_green.png differ diff --git a/src/org/infinity/resource/cre/browser/icon/circle_red.png b/src/org/infinity/resource/cre/browser/icon/circle_red.png new file mode 100644 index 000000000..dd5c5314f Binary files /dev/null and b/src/org/infinity/resource/cre/browser/icon/circle_red.png differ diff --git a/src/org/infinity/resource/cre/browser/icon/circle_yellow.png b/src/org/infinity/resource/cre/browser/icon/circle_yellow.png new file mode 100644 index 000000000..e476c85b4 Binary files /dev/null and b/src/org/infinity/resource/cre/browser/icon/circle_yellow.png differ diff --git a/src/org/infinity/resource/cre/decoder/AmbientDecoder.java b/src/org/infinity/resource/cre/decoder/AmbientDecoder.java new file mode 100644 index 000000000..f6303a56b --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/AmbientDecoder.java @@ -0,0 +1,166 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.decoder; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import org.infinity.resource.ResourceFactory; +import org.infinity.resource.cre.CreResource; +import org.infinity.resource.cre.decoder.tables.SpriteTables; +import org.infinity.resource.cre.decoder.util.AnimationInfo; +import org.infinity.resource.cre.decoder.util.DecoderAttribute; +import org.infinity.resource.cre.decoder.util.DirDef; +import org.infinity.resource.cre.decoder.util.SegmentDef; +import org.infinity.resource.cre.decoder.util.SeqDef; +import org.infinity.resource.cre.decoder.util.Sequence; +import org.infinity.resource.cre.decoder.util.SpriteUtils; +import org.infinity.resource.key.ResourceEntry; +import org.infinity.util.IniMap; +import org.infinity.util.IniMapSection; +import org.infinity.util.tuples.Couple; + +/** + * Creature animation decoder for processing type C000 (ambient) animations. + * Available ranges: [c000,cfff] + */ +public class AmbientDecoder extends SpriteDecoder +{ + /** The animation type associated with this class definition. */ + public static final AnimationInfo.Type ANIMATION_TYPE = AnimationInfo.Type.AMBIENT; + + public static final DecoderAttribute KEY_INVULNERABLE = DecoderAttribute.with("invulnerable", DecoderAttribute.DataType.BOOLEAN); + public static final DecoderAttribute KEY_PATH_SMOOTH = DecoderAttribute.with("path_smooth", DecoderAttribute.DataType.BOOLEAN); + public static final DecoderAttribute KEY_LIST_TYPE = DecoderAttribute.with("list_type", DecoderAttribute.DataType.INT); + + /** Creature behaves normally. */ + public static final int LISTTYPE_NORMAL = 0; + /** + * Creature behaves like a "flying" creatures: + * detected_by_infravision=0, {@link Sequence#WALK} transforms into {@link Sequence#STANCE}, + * hardcoded exceptions (e.g. polymorph, pathfinding, ...) + */ + public static final int LISTTYPE_FLYING = 2; + + private static final HashMap> suffixMap = new HashMap>() {{ + put(Sequence.WALK, Couple.with("G1", 0)); + put(Sequence.STANCE, Couple.with("G1", 8)); + put(Sequence.STAND, Couple.with("G1", 16)); + put(Sequence.GET_HIT, Couple.with("G1", 24)); + put(Sequence.DIE, Couple.with("G1", 32)); + put(Sequence.SLEEP, get(Sequence.DIE)); + put(Sequence.GET_UP, Couple.with("!G1", 32)); + put(Sequence.TWITCH, Couple.with("G1", 40)); + }}; + + /** + * A helper method that parses the specified data array and generates a {@link IniMap} instance out of it. + * @param data a String array containing table values for a specific table entry. + * @return a {@code IniMap} instance with the value derived from the specified data array. + * Returns {@code null} if no data could be derived. + */ + public static IniMap processTableData(String[] data) + { + IniMap retVal = null; + if (data == null || data.length < 16) { + return retVal; + } + + String resref = SpriteTables.valueToString(data, SpriteTables.COLUMN_RESREF, ""); + if (resref.isEmpty()) { + return retVal; + } + int falseColor = SpriteTables.valueToInt(data, SpriteTables.COLUMN_CLOWN, 0); + + List lines = SpriteUtils.processTableDataGeneral(data, ANIMATION_TYPE); + lines.add("[ambient]"); + lines.add("false_color=" + falseColor); + lines.add("resref=" + resref); + + retVal = IniMap.from(lines); + + return retVal; + } + + public AmbientDecoder(int animationId, IniMap ini) throws Exception + { + super(ANIMATION_TYPE, animationId, ini); + } + + public AmbientDecoder(CreResource cre) throws Exception + { + super(ANIMATION_TYPE, cre); + } + + /** Returns whether the creature is invulnerable by default. */ + public boolean isInvulnerable() { return getAttribute(KEY_INVULNERABLE); } + protected void setInvulnerable(boolean b) { setAttribute(KEY_INVULNERABLE, b); } + + /** ??? */ + public boolean isSmoothPath() { return getAttribute(KEY_PATH_SMOOTH); } + protected void setSmoothPath(boolean b) { setAttribute(KEY_PATH_SMOOTH, b); } + + /** ??? */ + public int getListType() { return getAttribute(KEY_LIST_TYPE); } + protected void setListType(int v) { setAttribute(KEY_LIST_TYPE, v); } + + @Override + public List getAnimationFiles(boolean essential) + { + String resref = getAnimationResref(); + ArrayList retVal = new ArrayList() {{ + add(resref + "G1.BAM"); + add(resref + "G1E.BAM"); + if (!essential) { + add(resref + "G2.BAM"); + add(resref + "G2E.BAM"); + } + }}; + return retVal; + } + + @Override + public boolean isSequenceAvailable(Sequence seq) + { + return (getSequenceDefinition(seq) != null); + } + + @Override + protected void init() throws Exception + { + // setting properties + initDefaults(getAnimationInfo()); + IniMapSection section = getSpecificIniSection(); + setFalseColor(section.getAsInteger(KEY_FALSE_COLOR.getName(), 0) != 0); + setInvulnerable(section.getAsInteger(KEY_INVULNERABLE.getName(), 0) != 0); + setSmoothPath(section.getAsInteger(KEY_PATH_SMOOTH.getName(), 0) != 0); + setListType(section.getAsInteger(KEY_LIST_TYPE.getName(), 0)); + } + + @Override + protected SeqDef getSequenceDefinition(Sequence seq) + { + SeqDef retVal = null; + String resref = getAnimationResref(); + Couple data = suffixMap.get(seq); + if (data != null) { + String suffix = data.getValue0(); + SegmentDef.Behavior behavior = SegmentDef.getBehaviorOf(suffix); + suffix = SegmentDef.fixBehaviorSuffix(suffix); + ResourceEntry entry = ResourceFactory.getResourceEntry(resref + suffix + ".BAM"); + ResourceEntry entryE = ResourceFactory.getResourceEntry(resref + suffix + "E.BAM"); + int cycle = data.getValue1().intValue(); + int cycleE = cycle + SeqDef.DIR_REDUCED_W.length; + if (SpriteUtils.bamCyclesExist(entry, cycle, SeqDef.DIR_REDUCED_W.length) && + SpriteUtils.bamCyclesExist(entryE, cycleE, SeqDef.DIR_REDUCED_E.length)) { + retVal = SeqDef.createSequence(seq, SeqDef.DIR_REDUCED_W, false, entry, cycle, null, behavior); + SeqDef tmp = SeqDef.createSequence(seq, SeqDef.DIR_REDUCED_E, false, entryE, cycleE, null, behavior); + retVal.addDirections(tmp.getDirections().toArray(new DirDef[tmp.getDirections().size()])); + } + } + return retVal; + } +} diff --git a/src/org/infinity/resource/cre/decoder/AmbientStaticDecoder.java b/src/org/infinity/resource/cre/decoder/AmbientStaticDecoder.java new file mode 100644 index 000000000..7547327a1 --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/AmbientStaticDecoder.java @@ -0,0 +1,144 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.decoder; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import org.infinity.resource.ResourceFactory; +import org.infinity.resource.cre.CreResource; +import org.infinity.resource.cre.decoder.tables.SpriteTables; +import org.infinity.resource.cre.decoder.util.AnimationInfo; +import org.infinity.resource.cre.decoder.util.DecoderAttribute; +import org.infinity.resource.cre.decoder.util.DirDef; +import org.infinity.resource.cre.decoder.util.SegmentDef; +import org.infinity.resource.cre.decoder.util.SeqDef; +import org.infinity.resource.cre.decoder.util.Sequence; +import org.infinity.resource.cre.decoder.util.SpriteUtils; +import org.infinity.resource.key.ResourceEntry; +import org.infinity.util.IniMap; +import org.infinity.util.IniMapSection; +import org.infinity.util.tuples.Couple; + +/** + * Creature animation decoder for processing type B000 (ambient_static) animations. + * Available ranges: [b000,bfff] + */ +public class AmbientStaticDecoder extends SpriteDecoder +{ + /** The animation type associated with this class definition. */ + public static final AnimationInfo.Type ANIMATION_TYPE = AnimationInfo.Type.AMBIENT_STATIC; + + public static final DecoderAttribute KEY_INVULNERABLE = DecoderAttribute.with("invulnerable", DecoderAttribute.DataType.BOOLEAN); + + private static final HashMap> suffixMap = new HashMap>() {{ + put(Sequence.STANCE, Couple.with("G1", 0)); + put(Sequence.STAND, Couple.with("G1", 8)); + put(Sequence.GET_HIT, Couple.with("G1", 16)); + put(Sequence.DIE, Couple.with("G1", 24)); + put(Sequence.SLEEP, get(Sequence.DIE)); + put(Sequence.GET_UP, Couple.with("!G1", 24)); + put(Sequence.TWITCH, Couple.with("G1", 32)); + }}; + + /** + * A helper method that parses the specified data array and generates a {@link IniMap} instance out of it. + * @param data a String array containing table values for a specific table entry. + * @return a {@code IniMap} instance with the value derived from the specified data array. + * Returns {@code null} if no data could be derived. + */ + public static IniMap processTableData(String[] data) + { + IniMap retVal = null; + if (data == null || data.length < 16) { + return retVal; + } + + String resref = SpriteTables.valueToString(data, SpriteTables.COLUMN_RESREF, ""); + if (resref.isEmpty()) { + return retVal; + } + int falseColor = SpriteTables.valueToInt(data, SpriteTables.COLUMN_CLOWN, 0); + + List lines = SpriteUtils.processTableDataGeneral(data, ANIMATION_TYPE); + lines.add("[ambient_static]"); + lines.add("false_color=" + falseColor); + lines.add("resref=" + resref); + + retVal = IniMap.from(lines); + + return retVal; + } + + public AmbientStaticDecoder(int animationId, IniMap ini) throws Exception + { + super(ANIMATION_TYPE, animationId, ini); + } + + public AmbientStaticDecoder(CreResource cre) throws Exception + { + super(ANIMATION_TYPE, cre); + } + + /** Returns whether the creature is invulnerable by default. */ + public boolean isInvulnerable() { return getAttribute(KEY_INVULNERABLE); } + protected void setInvulnerable(boolean b) { setAttribute(KEY_INVULNERABLE, b); } + + @Override + public List getAnimationFiles(boolean essential) + { + String resref = getAnimationResref(); + ArrayList retVal = new ArrayList() {{ + add(resref + "G1.BAM"); + add(resref + "G1E.BAM"); + if (!essential) { + add(resref + "G2.BAM"); + add(resref + "G2E.BAM"); + } + }}; + return retVal; + } + + @Override + public boolean isSequenceAvailable(Sequence seq) + { + return (getSequenceDefinition(seq) != null); + } + + @Override + protected void init() throws Exception + { + // setting properties + initDefaults(getAnimationInfo()); + IniMapSection section = getSpecificIniSection(); + setFalseColor(section.getAsInteger(KEY_FALSE_COLOR.getName(), 0) != 0); + setInvulnerable(section.getAsInteger(KEY_INVULNERABLE.getName(), 0) != 0); + } + + @Override + protected SeqDef getSequenceDefinition(Sequence seq) + { + SeqDef retVal = null; + String resref = getAnimationResref(); + Couple data = suffixMap.get(seq); + if (data != null) { + String suffix = data.getValue0(); + SegmentDef.Behavior behavior = SegmentDef.getBehaviorOf(suffix); + suffix = SegmentDef.fixBehaviorSuffix(suffix); + ResourceEntry entry = ResourceFactory.getResourceEntry(resref + suffix + ".BAM"); + int cycle = data.getValue1().intValue(); + ResourceEntry entryE = ResourceFactory.getResourceEntry(resref + suffix + "E.BAM"); + int cycleE = cycle + SeqDef.DIR_REDUCED_W.length; + if (SpriteUtils.bamCyclesExist(entry, cycle, SeqDef.DIR_REDUCED_W.length) && + SpriteUtils.bamCyclesExist(entryE, cycleE, SeqDef.DIR_REDUCED_E.length)) { + retVal = SeqDef.createSequence(seq, SeqDef.DIR_REDUCED_W, false, entry, cycle, null, behavior); + SeqDef tmp = SeqDef.createSequence(seq, SeqDef.DIR_REDUCED_E, false, entryE, cycleE, null, behavior); + retVal.addDirections(tmp.getDirections().toArray(new DirDef[tmp.getDirections().size()])); + } + } + return retVal; + } +} diff --git a/src/org/infinity/resource/cre/decoder/CharacterBaseDecoder.java b/src/org/infinity/resource/cre/decoder/CharacterBaseDecoder.java new file mode 100644 index 000000000..dac991e14 --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/CharacterBaseDecoder.java @@ -0,0 +1,238 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.decoder; + +import java.util.Locale; +import java.util.regex.Pattern; + +import org.infinity.datatype.IsNumeric; +import org.infinity.resource.Profile; +import org.infinity.resource.cre.CreResource; +import org.infinity.resource.cre.decoder.util.AnimationInfo; +import org.infinity.resource.cre.decoder.util.DecoderAttribute; +import org.infinity.resource.cre.decoder.util.ItemInfo; +import org.infinity.util.IniMap; +import org.infinity.util.IniMapSection; +import org.infinity.util.Misc; + +/** + * Common base for processing creature animations with different armor levels. + */ +public abstract class CharacterBaseDecoder extends SpriteDecoder +{ + public static final DecoderAttribute KEY_CAN_LIE_DOWN = DecoderAttribute.with("can_lie_down", DecoderAttribute.DataType.BOOLEAN); + public static final DecoderAttribute KEY_DOUBLE_BLIT = DecoderAttribute.with("double_blit", DecoderAttribute.DataType.BOOLEAN); + public static final DecoderAttribute KEY_EQUIP_HELMET = DecoderAttribute.with("equip_helmet", DecoderAttribute.DataType.BOOLEAN); + public static final DecoderAttribute KEY_ARMOR_MAX_CODE = DecoderAttribute.with("armor_max_code", DecoderAttribute.DataType.INT); + public static final DecoderAttribute KEY_HEIGHT_CODE = DecoderAttribute.with("height_code", DecoderAttribute.DataType.STRING); + public static final DecoderAttribute KEY_HEIGHT_CODE_HELMET = DecoderAttribute.with("height_code_helmet", DecoderAttribute.DataType.STRING); + + /** Available attack types associated with attack sequences. */ + public enum AttackType { + ONE_HANDED, TWO_HANDED, TWO_WEAPON, THROWING, + BOW, CROSSBOW, SLING + } + + public CharacterBaseDecoder(AnimationInfo.Type type, int animationId, IniMap ini) throws Exception + { + super(type, animationId, ini); + } + + public CharacterBaseDecoder(AnimationInfo.Type type, CreResource cre) throws Exception + { + super(type, cre); + } + + /** Returns whether the creature falls down when dead/unconscious. */ + public boolean canLieDown() { return getAttribute(KEY_CAN_LIE_DOWN); } + protected void setCanLieDown(boolean b) { setAttribute(KEY_CAN_LIE_DOWN, b); } + + /** unused */ + public boolean isDoubleBlit() { return getAttribute(KEY_DOUBLE_BLIT); } + protected void setDoubleBlit(boolean b) { setAttribute(KEY_DOUBLE_BLIT, b); } + + /** + * Returns the maximum armor code value used as suffix in animation filenames. + * Highest code value is usually used by ArmorSpecificResref(). + */ + public int getMaxArmorCode() { return getAttribute(KEY_ARMOR_MAX_CODE); } + protected void setMaxArmorCode(int v) { setAttribute(KEY_ARMOR_MAX_CODE, Math.max(0, v)); } + + /** Returns whether helmet overlay is shown. */ + public boolean isHelmetEquipped() { return getAttribute(KEY_EQUIP_HELMET); } + protected void setHelmetEquipped(boolean b) { setAttribute(KEY_EQUIP_HELMET, b); } + + /** Returns the height code prefix for helmet overlay sprites. Falls back to generic height code if needed. */ + public String getHelmetHeightCode() + { + String retVal = getAttribute(KEY_HEIGHT_CODE_HELMET); + if (retVal.isEmpty()) { + retVal = getHeightCode(); + } + return retVal; + } + + protected void setHelmetHeightCode(String s) { setAttribute(KEY_HEIGHT_CODE_HELMET, s); } + + /** Returns the creature animation height code prefix. */ + public String getHeightCode() { return getAttribute(KEY_HEIGHT_CODE); } + protected void setHeightCode(String s) + { + if (s == null || s.isEmpty()) { + // heuristically determine height code + s = guessHeightCode(); + } + setAttribute(KEY_HEIGHT_CODE, s); + } + + /** Returns the armor code based on equipped armor of the current creature. */ + public int getArmorCode() + { + int retVal = 1; + ItemInfo itm = getCreatureInfo().getEquippedArmor(); + if (itm != null) { + String code = itm.getAppearance(); + if (!code.isEmpty()) { + retVal = Math.max(1, Math.min(getMaxArmorCode(), Misc.toNumber(code.substring(0, 1), 1))); + } + } + return retVal; + } + + /** + * Determines the attack type based on the specified item resource. + * @param itm the item resource. + * @param abilityIndex the item-specific ability to check (e.g. throwing or melee for throwing axes) + * @param preferTwoWeapon whether {@code AttackType.TwoWeapon} should be returned if a melee one-handed weapon is detected. + * @return attack type associated with the item resource. + */ + public AttackType getAttackType(ItemInfo itm, int abilityIndex, boolean preferTwoWeapon) + { + AttackType retVal = AttackType.ONE_HANDED; + if (itm == null) { + return retVal; + } + + // collecting data + boolean isTwoHanded = (itm.getFlags() & (1 << 1)) != 0; + if (Profile.isEnhancedEdition()) { + // include fake two-handed weapons (e.g. monk fists) + isTwoHanded |= (itm.getFlags() & (1 << 12)) != 0; + } + int abilType = -1; + abilityIndex = Math.max(0, Math.min(itm.getAbilityCount() - 1, abilityIndex)); + if (abilityIndex >= 0) { + abilType = itm.getAbility(abilityIndex).getAbilityType(); + } + + switch (itm.getCategory()) { + case 15: // Bows + retVal = AttackType.BOW; + break; + case 27: // Crossbows + retVal = AttackType.CROSSBOW; + break; + case 18: // Slings + retVal = AttackType.SLING; + break; + default: + if (abilType == 1) { // melee + if (isTwoHanded) { + retVal = AttackType.TWO_HANDED; + } else { + retVal = (preferTwoWeapon) ? AttackType.TWO_WEAPON : AttackType.ONE_HANDED; + } + } else { // assume ranged + retVal = AttackType.THROWING; + } + } + + return retVal; + } + + /** + * Attempts to determine the correct height code. + * @return the "guessed" height code. Returns empty string if code could not be determined. + */ + protected String guessHeightCode() + { + String retVal = ""; + boolean isCharacter = (getAnimationType() == AnimationInfo.Type.CHARACTER); + String c2 = isCharacter ? "Q" : "P"; + + // try resref naming scheme + String resref = getAnimationResref().toUpperCase(Locale.ENGLISH); + if (resref.length() >= 3 && Pattern.matches(".[DEGHIO][FM].?", resref)) { + char race = resref.charAt(1); + char gender = resref.charAt(2); + if (gender == 'M' || gender == 'F') { + switch (race) { + case 'H': // human + case 'O': // half-orc + if (isCharacter) { + retVal = "W" + c2 + ((gender == 'F') ? "N" : "L"); + } else { + retVal = "W" + c2 + "L"; + } + break; + case 'E': // elf/half-elf + retVal = "W" + c2 + "M"; + break; + case 'D': // dwarf/gnome + case 'G': // gnome (?) + case 'I': // halfling + retVal = "W" + c2 + "S"; + break; + } + } + } + + // try associated CRE data + if (retVal.isEmpty()) { + CreResource cre = getCreResource(); + if (cre != null) { + boolean isFemale = ((IsNumeric)cre.getAttribute(CreResource.CRE_GENDER)).getValue() == 2; + int race = ((IsNumeric)cre.getAttribute(CreResource.CRE_RACE)).getValue(); + switch (race) { + case 1: // human + case 7: // half-orc + if (isCharacter) { + retVal = "W" + c2 + (isFemale ? "N" : "L"); + } else { + retVal = "W" + c2 + "L"; + } + break; + case 2: // elf + case 3: // half-elf + retVal = "W" + c2 + "M"; + break; + case 4: // dwarf + case 5: // halfling + case 6: // gnome + retVal = "W" + c2 + "S"; + break; + } + } + } + + return retVal; + } + + @Override + protected void init() throws Exception + { + // setting properties + initDefaults(getAnimationInfo()); + IniMapSection section = getSpecificIniSection(); + setCanLieDown(section.getAsInteger(KEY_CAN_LIE_DOWN.getName(), 0) != 0); + setFalseColor(section.getAsInteger(KEY_FALSE_COLOR.getName(), 0) != 0); + setDetectedByInfravision(section.getAsInteger(KEY_DETECTED_BY_INFRAVISION.getName(), 0) != 0); + setDoubleBlit(section.getAsInteger(KEY_DOUBLE_BLIT.getName(), 0) != 0); + setMaxArmorCode(section.getAsInteger(KEY_ARMOR_MAX_CODE.getName(), 0)); + setHelmetEquipped(section.getAsInteger(KEY_EQUIP_HELMET.getName(), 0) != 0); + setHeightCode(section.getAsString(KEY_HEIGHT_CODE.getName(), "")); + setHelmetHeightCode(section.getAsString(KEY_HEIGHT_CODE_HELMET.getName(), "")); + } +} diff --git a/src/org/infinity/resource/cre/decoder/CharacterDecoder.java b/src/org/infinity/resource/cre/decoder/CharacterDecoder.java new file mode 100644 index 000000000..fff5cb232 --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/CharacterDecoder.java @@ -0,0 +1,492 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.decoder; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.EnumMap; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; + +import org.infinity.resource.ResourceFactory; +import org.infinity.resource.cre.CreResource; +import org.infinity.resource.cre.decoder.tables.SpriteTables; +import org.infinity.resource.cre.decoder.util.AnimationInfo; +import org.infinity.resource.cre.decoder.util.DecoderAttribute; +import org.infinity.resource.cre.decoder.util.DirDef; +import org.infinity.resource.cre.decoder.util.ItemInfo; +import org.infinity.resource.cre.decoder.util.SegmentDef; +import org.infinity.resource.cre.decoder.util.SeqDef; +import org.infinity.resource.cre.decoder.util.Sequence; +import org.infinity.resource.cre.decoder.util.SpriteUtils; +import org.infinity.resource.key.ResourceEntry; +import org.infinity.util.IniMap; +import org.infinity.util.IniMapSection; +import org.infinity.util.tuples.Couple; + +/** + * Creature animation decoder for processing type 5000/6000 (character) animations. + * Available ranges: [5000,53ff], [5500,55ff], [6000,63ff], [6500,65ff] + */ +public class CharacterDecoder extends CharacterBaseDecoder +{ + /** The animation type associated with this class definition. */ + public static final AnimationInfo.Type ANIMATION_TYPE = AnimationInfo.Type.CHARACTER; + + public static final DecoderAttribute KEY_SPLIT_BAMS = DecoderAttribute.with("split_bams", DecoderAttribute.DataType.BOOLEAN); + public static final DecoderAttribute KEY_HEIGHT_CODE_SHIELD = DecoderAttribute.with("height_code_shield", DecoderAttribute.DataType.STRING); + public static final DecoderAttribute KEY_RESREF_PAPERDOLL = DecoderAttribute.with("resref_paperdoll", DecoderAttribute.DataType.STRING); + public static final DecoderAttribute KEY_RESREF_ARMOR_BASE = DecoderAttribute.with("resref_armor_base", DecoderAttribute.DataType.STRING); + public static final DecoderAttribute KEY_RESREF_ARMOR_SPECIFIC = DecoderAttribute.with("resref_armor_specific", DecoderAttribute.DataType.STRING); + + /** Assigns BAM suffix and cycle index to a specific animation sequence (unsplit version). */ + private static final HashMap> suffixMapUnsplit = + new HashMap>() {{ + put(Sequence.ATTACK_SLASH_1H, Couple.with("A1", 0)); + put(Sequence.ATTACK_SLASH_2H, Couple.with("A2", 0)); + put(Sequence.ATTACK_BACKSLASH_1H, Couple.with("A3", 0)); + put(Sequence.ATTACK_BACKSLASH_2H, Couple.with("A4", 0)); + put(Sequence.ATTACK_JAB_1H, Couple.with("A5", 0)); + put(Sequence.ATTACK_JAB_2H, Couple.with("A6", 0)); + put(Sequence.ATTACK_2WEAPONS1, Couple.with("A7", 0)); + put(Sequence.ATTACK_OVERHEAD, Couple.with("A8", 0)); + put(Sequence.ATTACK_2WEAPONS2, Couple.with("A9", 0)); + put(Sequence.ATTACK_BOW, Couple.with("SA", 0)); + put(Sequence.ATTACK_SLING, Couple.with("SS", 0)); + put(Sequence.ATTACK_CROSSBOW, Couple.with("SX", 0)); + put(Sequence.SPELL, Couple.with("CA", 0)); + put(Sequence.SPELL1, get(Sequence.SPELL)); + put(Sequence.SPELL2, Couple.with("CA", 18)); + put(Sequence.SPELL3, Couple.with("CA", 36)); + put(Sequence.SPELL4, Couple.with("CA", 54)); + put(Sequence.CAST, Couple.with("CA", 9)); + put(Sequence.CAST1, get(Sequence.CAST)); + put(Sequence.CAST2, Couple.with("CA", 27)); + put(Sequence.CAST3, Couple.with("CA", 45)); + put(Sequence.CAST4, Couple.with("CA", 63)); + put(Sequence.WALK, Couple.with("G1", 0)); + put(Sequence.STANCE, Couple.with("G1", 9)); + put(Sequence.STANCE2, Couple.with("G1", 27)); + put(Sequence.STAND, Couple.with("G1", 18)); + put(Sequence.STAND2, Couple.with("G1", 63)); + put(Sequence.STAND3, Couple.with("G1", 72)); + put(Sequence.GET_HIT, Couple.with("G1", 36)); + put(Sequence.DIE, Couple.with("G1", 45)); + put(Sequence.TWITCH, Couple.with("G1", 54)); + put(Sequence.SLEEP, Couple.with("G1", 81)); + put(Sequence.GET_UP, Couple.with("!G1", 81)); + put(Sequence.SLEEP2, Couple.with("G1", 90)); + put(Sequence.GET_UP2, Couple.with("!G1", 90)); + }}; + + /** Assigns BAM suffix and cycle index to a specific animation sequence (split version). */ + private static final HashMap> suffixMapSplit = + new HashMap>() {{ + put(Sequence.ATTACK_SLASH_1H, Couple.with("A1", 0)); + put(Sequence.ATTACK_SLASH_2H, Couple.with("A2", 0)); + put(Sequence.ATTACK_BACKSLASH_1H, Couple.with("A3", 0)); + put(Sequence.ATTACK_BACKSLASH_2H, Couple.with("A4", 0)); + put(Sequence.ATTACK_JAB_1H, Couple.with("A5", 0)); + put(Sequence.ATTACK_JAB_2H, Couple.with("A6", 0)); + put(Sequence.ATTACK_2WEAPONS1, Couple.with("A7", 0)); + put(Sequence.ATTACK_OVERHEAD, Couple.with("A8", 0)); + put(Sequence.ATTACK_2WEAPONS2, Couple.with("A9", 0)); + put(Sequence.ATTACK_BOW, Couple.with("SA", 0)); + put(Sequence.ATTACK_SLING, Couple.with("SS", 0)); + put(Sequence.ATTACK_CROSSBOW, Couple.with("SX", 0)); + put(Sequence.SPELL, Couple.with("CA", 0)); + put(Sequence.SPELL1, get(Sequence.SPELL)); + put(Sequence.SPELL2, Couple.with("CA", 18)); + put(Sequence.SPELL3, Couple.with("CA", 36)); + put(Sequence.SPELL4, Couple.with("CA", 54)); + put(Sequence.CAST, Couple.with("CA", 9)); + put(Sequence.CAST1, get(Sequence.CAST)); + put(Sequence.CAST2, Couple.with("CA", 27)); + put(Sequence.CAST3, Couple.with("CA", 45)); + put(Sequence.CAST4, Couple.with("CA", 63)); + put(Sequence.WALK, Couple.with("G11", 0)); + put(Sequence.STANCE, Couple.with("G1", 9)); + put(Sequence.STANCE2, Couple.with("G13", 27)); + put(Sequence.STAND, Couple.with("G12", 18)); + put(Sequence.STAND2, Couple.with("G17", 63)); + put(Sequence.STAND3, Couple.with("G18", 72)); + put(Sequence.GET_HIT, Couple.with("G15", 36)); + put(Sequence.DIE, Couple.with("G15", 45)); + put(Sequence.TWITCH, Couple.with("G16", 54)); + put(Sequence.SLEEP, Couple.with("G19", 81)); + put(Sequence.GET_UP, Couple.with("!G19", 81)); + put(Sequence.SLEEP2, Couple.with("G19", 90)); + put(Sequence.GET_UP2, Couple.with("!G19", 90)); + }}; + + /** Set of invalid attack type / animation sequence combinations. */ + private static final EnumMap> forbiddenSequences = + new EnumMap>(AttackType.class) {{ + put(AttackType.ONE_HANDED, EnumSet.of(Sequence.ATTACK_SLASH_2H, Sequence.ATTACK_BACKSLASH_2H, Sequence.ATTACK_JAB_2H, + Sequence.ATTACK_2WEAPONS1, Sequence.ATTACK_2WEAPONS2, Sequence.ATTACK_OVERHEAD, + Sequence.ATTACK_BOW, Sequence.ATTACK_SLING, Sequence.ATTACK_CROSSBOW, Sequence.STANCE2)); + put(AttackType.TWO_HANDED, EnumSet.of(Sequence.ATTACK_SLASH_1H, Sequence.ATTACK_BACKSLASH_1H, Sequence.ATTACK_JAB_1H, + Sequence.ATTACK_2WEAPONS1, Sequence.ATTACK_2WEAPONS2, Sequence.ATTACK_OVERHEAD, + Sequence.ATTACK_BOW, Sequence.ATTACK_SLING, Sequence.ATTACK_CROSSBOW, Sequence.STANCE)); + put(AttackType.TWO_WEAPON, EnumSet.of(Sequence.ATTACK_SLASH_1H, Sequence.ATTACK_BACKSLASH_1H, Sequence.ATTACK_JAB_1H, + Sequence.ATTACK_SLASH_2H, Sequence.ATTACK_BACKSLASH_2H, Sequence.ATTACK_JAB_2H, + Sequence.ATTACK_OVERHEAD, Sequence.ATTACK_BOW, Sequence.ATTACK_SLING, Sequence.ATTACK_CROSSBOW, + Sequence.STANCE2)); + put(AttackType.THROWING, EnumSet.of(Sequence.ATTACK_SLASH_1H, Sequence.ATTACK_BACKSLASH_1H, Sequence.ATTACK_JAB_1H, + Sequence.ATTACK_SLASH_2H, Sequence.ATTACK_BACKSLASH_2H, Sequence.ATTACK_JAB_2H, + Sequence.ATTACK_2WEAPONS1, Sequence.ATTACK_2WEAPONS2, Sequence.ATTACK_BOW, + Sequence.ATTACK_SLING, Sequence.ATTACK_CROSSBOW, Sequence.STANCE2)); + put(AttackType.BOW, EnumSet.of(Sequence.ATTACK_SLASH_1H, Sequence.ATTACK_BACKSLASH_1H, Sequence.ATTACK_JAB_1H, + Sequence.ATTACK_SLASH_2H, Sequence.ATTACK_BACKSLASH_2H, Sequence.ATTACK_JAB_2H, + Sequence.ATTACK_2WEAPONS1, Sequence.ATTACK_2WEAPONS2, Sequence.ATTACK_OVERHEAD, + Sequence.ATTACK_SLING, Sequence.ATTACK_CROSSBOW, Sequence.STANCE2)); + put(AttackType.SLING, EnumSet.of(Sequence.ATTACK_SLASH_1H, Sequence.ATTACK_BACKSLASH_1H, Sequence.ATTACK_JAB_1H, + Sequence.ATTACK_SLASH_2H, Sequence.ATTACK_BACKSLASH_2H, Sequence.ATTACK_JAB_2H, + Sequence.ATTACK_2WEAPONS1, Sequence.ATTACK_2WEAPONS2, Sequence.ATTACK_OVERHEAD, + Sequence.ATTACK_BOW, Sequence.ATTACK_CROSSBOW, Sequence.STANCE2)); + put(AttackType.CROSSBOW, EnumSet.of(Sequence.ATTACK_SLASH_1H, Sequence.ATTACK_BACKSLASH_1H, Sequence.ATTACK_JAB_1H, + Sequence.ATTACK_SLASH_2H, Sequence.ATTACK_BACKSLASH_2H, Sequence.ATTACK_JAB_2H, + Sequence.ATTACK_2WEAPONS1, Sequence.ATTACK_2WEAPONS2, Sequence.ATTACK_OVERHEAD, + Sequence.ATTACK_BOW, Sequence.ATTACK_SLING, Sequence.STANCE2)); + }}; + + /** + * A helper method that parses the specified data array and generates a {@link IniMap} instance out of it. + * @param data a String array containing table values for a specific table entry. + * @return a {@code IniMap} instance with the value derived from the specified data array. + * Returns {@code null} if no data could be derived. + */ + public static IniMap processTableData(String[] data) + { + IniMap retVal = null; + if (data == null || data.length < 16) { + return retVal; + } + + String resref = SpriteTables.valueToString(data, SpriteTables.COLUMN_RESREF, ""); + if (resref.isEmpty()) { + return retVal; + } + int equipHelmet = SpriteTables.valueToInt(data, SpriteTables.COLUMN_HELMET, 0); + int splitBams = SpriteTables.valueToInt(data, SpriteTables.COLUMN_SPLIT, 0); + int falseColor = SpriteTables.valueToInt(data, SpriteTables.COLUMN_CLOWN, 0); + String heightCode = SpriteTables.valueToString(data, SpriteTables.COLUMN_HEIGHT, ""); + String heightCodeHelmet = heightCode; + String heightCodeShield = SpriteTables.valueToString(data, SpriteTables.COLUMN_HEIGHT_SHIELD, ""); + String resrefSpecific = SpriteTables.valueToString(data, SpriteTables.COLUMN_RESREF2, ""); + + List lines = SpriteUtils.processTableDataGeneral(data, ANIMATION_TYPE); + lines.add("[character]"); + lines.add("equip_helmet=" + equipHelmet); + lines.add("split_bams=" + splitBams); + lines.add("false_color=" + falseColor); + lines.add("resref=" + resref); + if (!heightCode.isEmpty()) { + lines.add("height_code=" + heightCode); + } + if (!heightCodeHelmet.isEmpty()) { + lines.add("height_code_helmet=" + heightCodeHelmet); + } + if (!heightCodeShield.isEmpty()) { + lines.add("height_code_shield=" + heightCodeShield); + } + lines.add("resref_armor_base=" + resref.charAt(resref.length() - 1)); + if (!resrefSpecific.isEmpty()) { + lines.add("resref_armor_specific=" + resrefSpecific.charAt(resrefSpecific.length() - 1)); + } + + retVal = IniMap.from(lines); + + return retVal; + } + + public CharacterDecoder(int animationId, IniMap ini) throws Exception + { + super(ANIMATION_TYPE, animationId, ini); + } + + public CharacterDecoder(CreResource cre) throws Exception + { + super(ANIMATION_TYPE, cre); + } + + /** Returns the correct sequence map for the current settings. */ + private HashMap> getSuffixMap() + { + return isSplittedBams() ? suffixMapSplit : suffixMapUnsplit; + } + + /** Returns whether animations are spread over various subfiles. */ + public boolean isSplittedBams() { return getAttribute(KEY_SPLIT_BAMS); } + protected void setSplittedBams(boolean b) { setAttribute(KEY_SPLIT_BAMS, b); } + + /** Returns the height code prefix for shield overlay sprites. Falls back to generic height code if needed. */ + public String getShieldHeightCode() + { + String retVal = getAttribute(KEY_HEIGHT_CODE_SHIELD); + if (retVal.isEmpty()) { + retVal = getHeightCode(); + } + return retVal; + } + + protected void setShieldHeightCode(String s) + { + if (s != null && !s.isEmpty()) { + // Discard if shield height code refers to non-existing overlays + List resList = ResourceFactory.getResources(s + "..G1\\.BAM"); + if (resList.isEmpty()) { + s = ""; + } + } + setAttribute(KEY_HEIGHT_CODE_SHIELD, s); + } + + /** Returns the paperdoll resref. */ + public String getPaperdollResref() { return getAttribute(KEY_RESREF_PAPERDOLL); } + protected void setPaperdollResref(String s) { setAttribute(KEY_RESREF_PAPERDOLL, s); } + + /** + * Returns the animation resref for lesser armor types. + * Returns the same value as {@link #getAnimationResref()} if no base armor code is available. + */ + public String getArmorBaseResref() { return getAttribute(KEY_RESREF_ARMOR_BASE); } + protected void setArmorBaseResref(String s) + { + if (s.isEmpty()) { + s = getAnimationResref(); + } else { + s = getAnimationResref().substring(0, 3) + s.substring(0, 1); + } + setAttribute(KEY_RESREF_ARMOR_BASE, s); + } + + /** + * Returns the animation resref for greater armor types. + * Returns the same value as {@link #getAnimationResref()} if no specific armor code is available. + */ + public String getArmorSpecificResref() { return getAttribute(KEY_RESREF_ARMOR_SPECIFIC); } + protected void setArmorSpecificResref(String s) + { + if (s.isEmpty()) { + s = getAnimationResref(); + } else { + s = getAnimationResref().substring(0, 3) + s.substring(0, 1); + } + setAttribute(KEY_RESREF_ARMOR_SPECIFIC, s); + } + + /** + * Sets the maximum armor code value uses as suffix in animation filenames. + * Specify -1 to detect value automatically. + */ + @Override + protected void setMaxArmorCode(int v) + { + if (v < 0) { + // autodetection: requires fully initialized resref definitions + final String[] resrefs = { getArmorBaseResref(), getArmorSpecificResref() }; + if (getArmorBaseResref().equalsIgnoreCase(getArmorSpecificResref())) { + resrefs[1] = null; + } + for (final String resref : resrefs) { + if (resref != null && !resref.isEmpty()) { + for (int i = 1; i < 10; i++) { + if (ResourceFactory.resourceExists(resref + i + "G1.BAM")) { + v = Math.max(v, i); + } + } + } + } + } + super.setMaxArmorCode(v); + } + + @Override + public List getAnimationFiles(boolean essential) + { + ArrayList retVal = null; + String resref1 = getAnimationResref(); + String resref2 = getArmorSpecificResref(); + + if (essential) { + HashSet files = new HashSet<>(); + for (final HashMap.Entry> entry : getSuffixMap().entrySet()) { + String suffix = SegmentDef.fixBehaviorSuffix(entry.getValue().getValue0()); + if (suffix.startsWith("G")) { + for (int i = 1; i <= getMaxArmorCode(); i++) { + String resref = resref2 + i + suffix + ".BAM"; + if (!ResourceFactory.resourceExists(resref)) { + resref = resref1 + i + suffix + ".BAM"; + } + files.add(resref); + } + } + } + retVal = new ArrayList<>(Arrays.asList(files.toArray(new String[files.size()]))); + } else { + // collecting suffixes + HashSet actionSet = new HashSet<>(); + for (final HashMap.Entry> entry : getSuffixMap().entrySet()) { + String suffix = SegmentDef.fixBehaviorSuffix(entry.getValue().getValue0()); + actionSet.add(suffix); + } + + // generating file list + retVal = new ArrayList() {{ + for (int i = 1; i <= getMaxArmorCode(); i++) { + for (final String a : actionSet) { + String resref = resref2 + i + a + ".BAM"; + if (!ResourceFactory.resourceExists(resref)) { + resref = resref1 + i + a + ".BAM"; + } + add(resref); + } + } + }}; + } + + return retVal; + } + + @Override + public boolean isSequenceAvailable(Sequence seq) + { + return (getSequenceDefinition(seq) != null); + } + + @Override + protected void init() throws Exception + { + super.init(); + IniMapSection section = getSpecificIniSection(); + setSplittedBams(section.getAsInteger(KEY_SPLIT_BAMS.getName(), 0) != 0); + setShieldHeightCode(section.getAsString(KEY_HEIGHT_CODE_SHIELD.getName(), "")); + setPaperdollResref(section.getAsString(KEY_RESREF_PAPERDOLL.getName(), "")); + setArmorBaseResref(section.getAsString(KEY_RESREF_ARMOR_BASE.getName(), "")); + setArmorSpecificResref(section.getAsString(KEY_RESREF_ARMOR_SPECIFIC.getName(), "")); + if (getMaxArmorCode() == 0) { + setMaxArmorCode(-1); + } + } + + @Override + protected SeqDef getSequenceDefinition(Sequence seq) + { + SeqDef retVal = null; + + if (!getSuffixMap().containsKey(seq)) { + return retVal; + } + + // getting armor level + int armorCode = getArmorCode(); + if (armorCode > getMaxArmorCode()) { + return retVal; + } + + // preparing shield slot data + boolean isLefthandedWeapon = false; + String prefixShield = getShieldHeightCode(); + String codeShield = ""; + if (!prefixShield.isEmpty()) { + ItemInfo itmShield = getCreatureInfo().getEquippedShield(); + if (itmShield != null) { + codeShield = itmShield.getAppearance().trim(); + isLefthandedWeapon = !codeShield.isEmpty() && ItemInfo.testAll(itmShield, ItemInfo.FILTER_WEAPON_MELEE_LEFT_HANDED); + } + } + + // getting attack type + ItemInfo itmWeapon = getCreatureInfo().getEquippedWeapon(); + int itmAbility = getCreatureInfo().getSelectedWeaponAbility(); + AttackType attackType = getAttackType(itmWeapon, itmAbility, isLefthandedWeapon); + + EnumSet sequences = forbiddenSequences.get(attackType); + if (sequences != null && sequences.contains(seq)) { + // sequence not allowed for selected weapon + return retVal; + } + + ArrayList> resrefList = new ArrayList<>(); + + // adding creature resref + String creSuffix = getSuffixMap().get(seq).getValue0(); + SegmentDef.Behavior behavior = SegmentDef.getBehaviorOf(creSuffix); + creSuffix = SegmentDef.fixBehaviorSuffix(creSuffix); + String creResref = (armorCode > 1) ? getArmorSpecificResref() : getArmorBaseResref(); + if (!ResourceFactory.resourceExists(creResref + armorCode + creSuffix + ".BAM")) { + creResref = getArmorBaseResref(); + if (!ResourceFactory.resourceExists(creResref + armorCode + creSuffix + ".BAM")) { + creResref = getAnimationResref(); + } + } + creResref += armorCode; + resrefList.add(Couple.with(creResref, SegmentDef.SpriteType.AVATAR)); + + String prefix; + // adding helmet overlay + if (isHelmetEquipped()) { + prefix = getHelmetHeightCode(); + if (!prefix.isEmpty()) { + ItemInfo itmHelmet = getCreatureInfo().getEquippedHelmet(); + if (itmHelmet != null) { + String code = itmHelmet.getAppearance().trim(); + if (code.length() == 2) { + resrefList.add(Couple.with(prefix + code, SegmentDef.SpriteType.HELMET)); + } + } + } + } + + // adding shield overlay + if (!prefixShield.isEmpty() && !codeShield.isEmpty()) { + resrefList.add(Couple.with(prefixShield + codeShield, SegmentDef.SpriteType.SHIELD)); + } + + // adding weapon overlay + prefix = getHeightCode(); + if (itmWeapon != null && !prefix.isEmpty()) { + String code = itmWeapon.getAppearance().trim(); + if (code.length() == 2) { + resrefList.add(Couple.with(prefix + code, SegmentDef.SpriteType.WEAPON)); + } + } + + retVal = new SeqDef(seq); + for (final Couple data: resrefList) { + // getting BAM suffix and cycle index + prefix = data.getValue0(); + SegmentDef.SpriteType spriteType = data.getValue1(); + HashMap> curSuffixMap = (spriteType == SegmentDef.SpriteType.AVATAR) ? getSuffixMap() : suffixMapUnsplit; + String suffix = SegmentDef.fixBehaviorSuffix(curSuffixMap.get(seq).getValue0()); + int cycleIdx = curSuffixMap.get(seq).getValue1().intValue(); + + // enabling left-handed weapon overlay if available + if (spriteType == SegmentDef.SpriteType.SHIELD && isLefthandedWeapon) { + if (ResourceFactory.resourceExists(prefix + "O" + suffix + ".BAM")) { + prefix += "O"; + } + } + + // defining sequences + ResourceEntry entry = ResourceFactory.getResourceEntry(prefix + suffix + ".BAM"); + if (SpriteUtils.bamCyclesExist(entry, cycleIdx, SeqDef.DIR_FULL_W.length)) { + SeqDef tmp = SeqDef.createSequence(seq, SeqDef.DIR_FULL_W, false, entry, cycleIdx, spriteType, behavior); + retVal.addDirections(tmp.getDirections().toArray(new DirDef[tmp.getDirections().size()])); + tmp = SeqDef.createSequence(seq, SeqDef.DIR_FULL_E, true, entry, cycleIdx + 1, spriteType, behavior); + retVal.addDirections(tmp.getDirections().toArray(new DirDef[tmp.getDirections().size()])); + } + } + + if (retVal.isEmpty()) { + retVal = null; + } + + return retVal; + } +} diff --git a/src/org/infinity/resource/cre/decoder/CharacterOldDecoder.java b/src/org/infinity/resource/cre/decoder/CharacterOldDecoder.java new file mode 100644 index 000000000..2b2204d98 --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/CharacterOldDecoder.java @@ -0,0 +1,394 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.decoder; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.EnumMap; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; + +import org.infinity.resource.ResourceFactory; +import org.infinity.resource.cre.CreResource; +import org.infinity.resource.cre.decoder.tables.SpriteTables; +import org.infinity.resource.cre.decoder.util.AnimationInfo; +import org.infinity.resource.cre.decoder.util.CycleDef; +import org.infinity.resource.cre.decoder.util.DecoderAttribute; +import org.infinity.resource.cre.decoder.util.DirDef; +import org.infinity.resource.cre.decoder.util.Direction; +import org.infinity.resource.cre.decoder.util.ItemInfo; +import org.infinity.resource.cre.decoder.util.SegmentDef; +import org.infinity.resource.cre.decoder.util.SeqDef; +import org.infinity.resource.cre.decoder.util.Sequence; +import org.infinity.resource.cre.decoder.util.SpriteUtils; +import org.infinity.resource.key.ResourceEntry; +import org.infinity.util.IniMap; +import org.infinity.util.IniMapSection; +import org.infinity.util.tuples.Couple; + +/** + * Creature animation decoder for processing type 5000/6000 (character_old) animations. + * Available ranges: [5400,54ff], [5600,5fff], [6400,64ff], [6600,6fff] + */ +public class CharacterOldDecoder extends CharacterBaseDecoder +{ + /** The animation type associated with this class definition. */ + public static final AnimationInfo.Type ANIMATION_TYPE = AnimationInfo.Type.CHARACTER_OLD; + + public static final DecoderAttribute KEY_HIDE_WEAPONS = DecoderAttribute.with("hide_weapons", DecoderAttribute.DataType.BOOLEAN); + public static final DecoderAttribute KEY_SHADOW = DecoderAttribute.with("shadow", DecoderAttribute.DataType.STRING); + + /** Assigns BAM suffix and cycle index to a specific animation sequence. */ + private static final HashMap> suffixMap = + new HashMap>() {{ + put(Sequence.ATTACK_SLASH_1H, Couple.with("A1", 0)); + put(Sequence.ATTACK_SLASH_2H, Couple.with("A2", 0)); + put(Sequence.ATTACK_BACKSLASH_1H, Couple.with("A3", 0)); + put(Sequence.ATTACK_BACKSLASH_2H, Couple.with("A4", 0)); + put(Sequence.ATTACK_JAB_1H, Couple.with("A5", 0)); + put(Sequence.ATTACK_JAB_2H, Couple.with("A6", 0)); + put(Sequence.ATTACK_BOW, Couple.with("SA", 0)); + put(Sequence.ATTACK_CROSSBOW, Couple.with("SX", 0)); + put(Sequence.SPELL, Couple.with("CA", 0)); + put(Sequence.SPELL1, get(Sequence.SPELL)); + put(Sequence.SPELL2, Couple.with("CA", 16)); + put(Sequence.SPELL3, Couple.with("CA", 32)); + put(Sequence.SPELL4, Couple.with("CA", 48)); + put(Sequence.CAST, Couple.with("CA", 8)); + put(Sequence.CAST1, get(Sequence.CAST)); + put(Sequence.CAST2, Couple.with("CA", 24)); + put(Sequence.CAST3, Couple.with("CA", 40)); + put(Sequence.CAST4, Couple.with("CA", 56)); + put(Sequence.WALK, Couple.with("G1", 0)); + put(Sequence.STANCE, Couple.with("G1", 8)); + put(Sequence.STANCE2, Couple.with("G1", 24)); + put(Sequence.STAND, Couple.with("G1", 16)); + put(Sequence.STAND2, Couple.with("G1", 32)); + put(Sequence.GET_HIT, Couple.with("G1", 40)); + put(Sequence.DIE, Couple.with("G1", 48)); + put(Sequence.SLEEP, get(Sequence.DIE)); + put(Sequence.GET_UP, Couple.with("!G1", 48)); + put(Sequence.TWITCH, Couple.with("G1", 56)); + }}; + + /** BAM suffix and cycle index for extended walk directions. */ + private static Couple walkExtra = Couple.with("W2", 0); + + /** Set of invalid attack type / animation sequence combinations. */ + private static final EnumMap> forbiddenSequences = + new EnumMap>(AttackType.class) {{ + put(AttackType.ONE_HANDED, EnumSet.of(Sequence.ATTACK_SLASH_2H, Sequence.ATTACK_BACKSLASH_2H, Sequence.ATTACK_JAB_2H, + Sequence.ATTACK_BOW, Sequence.ATTACK_CROSSBOW, Sequence.STANCE2)); + put(AttackType.TWO_HANDED, EnumSet.of(Sequence.ATTACK_SLASH_1H, Sequence.ATTACK_BACKSLASH_1H, Sequence.ATTACK_JAB_1H, + Sequence.ATTACK_BOW, Sequence.ATTACK_CROSSBOW, Sequence.STANCE)); + put(AttackType.TWO_WEAPON, EnumSet.allOf(Sequence.class)); + put(AttackType.THROWING, EnumSet.of(Sequence.ATTACK_BACKSLASH_1H, Sequence.ATTACK_JAB_1H, + Sequence.ATTACK_SLASH_2H, Sequence.ATTACK_BACKSLASH_2H, Sequence.ATTACK_JAB_2H, + Sequence.ATTACK_BOW, Sequence.ATTACK_CROSSBOW, Sequence.STANCE2)); + put(AttackType.BOW, EnumSet.of(Sequence.ATTACK_SLASH_1H, Sequence.ATTACK_BACKSLASH_1H, Sequence.ATTACK_JAB_1H, + Sequence.ATTACK_SLASH_2H, Sequence.ATTACK_BACKSLASH_2H, Sequence.ATTACK_JAB_2H, + Sequence.ATTACK_CROSSBOW, Sequence.STANCE2)); + put(AttackType.SLING, get(AttackType.THROWING)); + put(AttackType.CROSSBOW, EnumSet.of(Sequence.ATTACK_SLASH_1H, Sequence.ATTACK_BACKSLASH_1H, Sequence.ATTACK_JAB_1H, + Sequence.ATTACK_SLASH_2H, Sequence.ATTACK_BACKSLASH_2H, Sequence.ATTACK_JAB_2H, + Sequence.ATTACK_BOW, Sequence.STANCE2)); + }}; + + // the default shadow of the animation + private static final String SHADOW_RESREF = "CSHD"; + // special shadow for (armored) Sarevok + private static final String SHADOW_RESREF_SAREVOK = "SSHD"; + + /** + * A helper method that parses the specified data array and generates a {@link IniMap} instance out of it. + * @param data a String array containing table values for a specific table entry. + * @return a {@code IniMap} instance with the value derived from the specified data array. + * Returns {@code null} if no data could be derived. + */ + public static IniMap processTableData(String[] data) + { + IniMap retVal = null; + if (data == null || data.length < 16) { + return retVal; + } + + String resref = SpriteTables.valueToString(data, SpriteTables.COLUMN_RESREF, ""); + if (resref.isEmpty()) { + return retVal; + } + int equipHelmet = SpriteTables.valueToInt(data, SpriteTables.COLUMN_HELMET, 0); + int falseColor = SpriteTables.valueToInt(data, SpriteTables.COLUMN_CLOWN, 0); + int hideWeapons = SpriteTables.valueToInt(data, SpriteTables.COLUMN_WEAPON, 1) != 0 ? 0 : 1; + String heightCode = SpriteTables.valueToString(data, SpriteTables.COLUMN_HEIGHT, ""); + String heightCodeHelmet = heightCode; + String shadow = SpriteTables.valueToString(data, SpriteTables.COLUMN_RESREF2, ""); + + List lines = SpriteUtils.processTableDataGeneral(data, ANIMATION_TYPE); + lines.add("[character_old]"); + lines.add("equip_helmet=" + equipHelmet); + lines.add("false_color=" + falseColor); + lines.add("hide_weapons=" + hideWeapons); + lines.add("resref=" + resref); + if (!heightCode.isEmpty()) { + lines.add("height_code=" + heightCode); + } + if (!heightCodeHelmet.isEmpty()) { + lines.add("height_code_helmet=" + heightCodeHelmet); + } + if (!shadow.isEmpty()) { + lines.add("shadow=" + shadow); + } + + retVal = IniMap.from(lines); + + return retVal; + } + + public CharacterOldDecoder(int animationId, IniMap ini) throws Exception + { + super(ANIMATION_TYPE, animationId, ini); + } + + public CharacterOldDecoder(CreResource cre) throws Exception + { + super(ANIMATION_TYPE, cre); + } + + /** Returns whether weapon animation overlays are suppressed. */ + public boolean isWeaponsHidden() { return getAttribute(KEY_HIDE_WEAPONS); } + protected void setWeaponsHidden(boolean b) { setAttribute(KEY_HIDE_WEAPONS, b); } + + /** Returns a separate shadow sprite resref. */ + public String getShadowResref() { return getAttribute(KEY_SHADOW); } + protected void setShadowResref(String s) + { + String shadow; + // taking care of harcoded shadows + switch (getAnimationId()) { + case 0x6400: // Drizzt + shadow = SHADOW_RESREF; + break; + case 0x6404: // Sarevok + shadow = SHADOW_RESREF_SAREVOK; + break; + default: + shadow = !s.isEmpty() ? s : SHADOW_RESREF; + } + setAttribute(KEY_SHADOW, shadow); + } + + /** + * Sets the maximum armor code value uses as suffix in animation filenames. + * Specify -1 to detect value automatically. + */ + @Override + protected void setMaxArmorCode(int v) + { + if (v < 0) { + // autodetection + for (int i = 1; i < 10 && v < 0; i++) { + String resref = getAnimationResref(); + if (!resref.isEmpty() && !ResourceFactory.resourceExists(resref + i + "G1.BAM")) { + v = i - 1; + } + } + } + super.setMaxArmorCode(v); + } + + @Override + public List getAnimationFiles(boolean essential) + { + ArrayList retVal = null; + String resref = getAnimationResref(); + + if (essential) { + HashSet files = new HashSet<>(); + for (final HashMap.Entry> entry : suffixMap.entrySet()) { + String suffix = SegmentDef.fixBehaviorSuffix(entry.getValue().getValue0()); + if (suffix.startsWith("G")) { + for (int i = 1; i <= getMaxArmorCode(); i++) { + files.add(resref + i + suffix + ".BAM"); + } + } + } + retVal = new ArrayList<>(Arrays.asList(files.toArray(new String[files.size()]))); + } else { + // collecting suffixes + HashSet actionSet = new HashSet<>(); + for (final HashMap.Entry> entry : suffixMap.entrySet()) { + String suffix = SegmentDef.fixBehaviorSuffix(entry.getValue().getValue0()); + actionSet.add(suffix); + } + actionSet.add(walkExtra.getValue0()); + + // generating file list + retVal = new ArrayList() {{ + for (int i = 1; i <= getMaxArmorCode(); i++) { + for (final String a : actionSet) { + add(resref + i + a + ".BAM"); + add(resref + i + a + "E.BAM"); + } + } + }}; + } + + return retVal; + } + + @Override + public boolean isSequenceAvailable(Sequence seq) + { + return (getSequenceDefinition(seq) != null); + } + + @Override + protected void init() throws Exception + { + super.init(); + IniMapSection section = getSpecificIniSection(); + setWeaponsHidden(section.getAsInteger(KEY_HIDE_WEAPONS.getName(), 0) != 0); + setShadowResref(section.getAsString(KEY_SHADOW.getName(), "")); + if (getMaxArmorCode() == 0) { + setMaxArmorCode(-1); + } + } + + @Override + protected SeqDef getSequenceDefinition(Sequence seq) + { + SeqDef retVal = null; + + if (!suffixMap.containsKey(seq)) { + return retVal; + } + + // getting armor level + int armorCode = getArmorCode(); + if (armorCode > getMaxArmorCode()) { + return retVal; + } + + // getting attack type + ItemInfo itmWeapon = getCreatureInfo().getEquippedWeapon(); + int itmAbility = getCreatureInfo().getSelectedWeaponAbility(); + AttackType attackType = getAttackType(itmWeapon, itmAbility, false); + + EnumSet sequences = forbiddenSequences.get(attackType); + if (sequences != null && sequences.contains(seq)) { + // sequence not allowed for selected weapon + return retVal; + } + + ArrayList> resrefList = new ArrayList<>(); + + // getting BAM suffix and cycle index + String suffix = suffixMap.get(seq).getValue0(); + SegmentDef.Behavior behavior = SegmentDef.getBehaviorOf(suffix); + suffix = SegmentDef.fixBehaviorSuffix(suffix); + int cycleIdx = suffixMap.get(seq).getValue1().intValue(); + + // adding creature shadow + if (!getShadowResref().isEmpty()) { + resrefList.add(Couple.with(getShadowResref(), SegmentDef.SpriteType.AVATAR)); + } + + // adding creature sprite + resrefList.add(Couple.with(getAnimationResref() + armorCode, SegmentDef.SpriteType.AVATAR)); + + // adding helmet overlay + if (isHelmetEquipped()) { + String prefix = getHelmetHeightCode(); + if (!prefix.isEmpty()) { + ItemInfo itmHelmet = getCreatureInfo().getEquippedHelmet(); + if (itmHelmet != null) { + String code = itmHelmet.getAppearance().trim(); + if (code.length() == 2) { + resrefList.add(Couple.with(prefix + code, SegmentDef.SpriteType.HELMET)); + } + } + } + } + + if (!isWeaponsHidden()) { + // adding shield overlay + String prefix = getHeightCode(); + if (!prefix.isEmpty()) { + ItemInfo itmShield = getCreatureInfo().getEquippedShield(); + if (itmShield != null) { + String code = itmShield.getAppearance().trim(); + if (!code.isEmpty()) { + resrefList.add(Couple.with(prefix + code, SegmentDef.SpriteType.SHIELD)); + } + } + } + + // adding weapon overlay + prefix = getHeightCode(); + if (itmWeapon != null && !prefix.isEmpty()) { + String code = itmWeapon.getAppearance().trim(); + if (code.length() == 2) { + if (ResourceFactory.resourceExists(prefix + code + suffix + ".BAM")) { + resrefList.add(Couple.with(prefix + code, SegmentDef.SpriteType.WEAPON)); + } else { + // weapon animation is crucial + return retVal; + } + } + } + } + + retVal = new SeqDef(seq); + for (final Couple data : resrefList) { + String prefix = data.getValue0(); + SegmentDef.SpriteType spriteType = data.getValue1(); + // defining sequences + ResourceEntry entry = ResourceFactory.getResourceEntry(prefix + suffix + ".BAM"); + ResourceEntry entryE = ResourceFactory.getResourceEntry(prefix + suffix + "E.BAM"); + if (entry != null) { + if (seq == Sequence.WALK) { + // special: uses full set of directions spread over two BAM files + String suffix2 = walkExtra.getValue0(); + int cycleIdx2 = walkExtra.getValue1().intValue(); + ResourceEntry entry2 = ResourceFactory.getResourceEntry(prefix + suffix2 + ".BAM"); + ResourceEntry entry2E = ResourceFactory.getResourceEntry(prefix + suffix2 + "E.BAM"); + if (SpriteUtils.bamCyclesExist(entry, cycleIdx, SeqDef.DIR_REDUCED_W.length) && + SpriteUtils.bamCyclesExist(entry2, cycleIdx2, SeqDef.DIR_REDUCED_W.length) && + SpriteUtils.bamCyclesExist(entryE, cycleIdx + SeqDef.DIR_REDUCED_W.length, SeqDef.DIR_REDUCED_E.length) && + SpriteUtils.bamCyclesExist(entry2E, cycleIdx2 + SeqDef.DIR_REDUCED_W.length, SeqDef.DIR_REDUCED_E.length)) { + // defining western directions + Direction[] dirX = new Direction[] { Direction.SSW, Direction.WSW, Direction.WNW, Direction.NNW, Direction.NNE }; + for (int i = 0; i < SeqDef.DIR_REDUCED_W.length; i++) { + retVal.addDirections(new DirDef(SeqDef.DIR_REDUCED_W[i], false, new CycleDef(entry, cycleIdx + i, spriteType, behavior))); + retVal.addDirections(new DirDef(dirX[i], false, new CycleDef(entry2, cycleIdx2 + i, spriteType, behavior))); + } + // defining eastern directions + dirX = new Direction[] { Direction.ENE, Direction.ESE, Direction.SSE, }; + for (int i = 0; i < SeqDef.DIR_REDUCED_E.length; i++) { + retVal.addDirections(new DirDef(SeqDef.DIR_REDUCED_E[i], false, new CycleDef(entryE, cycleIdx + SeqDef.DIR_REDUCED_W.length + i, spriteType, behavior))); + retVal.addDirections(new DirDef(dirX[i], false, new CycleDef(entry2E, cycleIdx2 + SeqDef.DIR_REDUCED_W.length + i, spriteType, behavior))); + } + } + } else { + if (SpriteUtils.bamCyclesExist(entry, cycleIdx, SeqDef.DIR_REDUCED_W.length) && + SpriteUtils.bamCyclesExist(entry, cycleIdx + SeqDef.DIR_REDUCED_W.length, SeqDef.DIR_REDUCED_E.length)) { + SeqDef tmp = SeqDef.createSequence(seq, SeqDef.DIR_REDUCED_W, false, entry, cycleIdx, spriteType, behavior); + retVal.addDirections(tmp.getDirections().toArray(new DirDef[tmp.getDirections().size()])); + tmp = SeqDef.createSequence(seq, SeqDef.DIR_REDUCED_E, false, entryE, cycleIdx + SeqDef.DIR_REDUCED_W.length, spriteType, behavior); + retVal.addDirections(tmp.getDirections().toArray(new DirDef[tmp.getDirections().size()])); + } + } + } + } + + if (retVal.isEmpty()) { + retVal = null; + } + + return retVal; + } +} diff --git a/src/org/infinity/resource/cre/decoder/EffectDecoder.java b/src/org/infinity/resource/cre/decoder/EffectDecoder.java new file mode 100644 index 000000000..27c1cb339 --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/EffectDecoder.java @@ -0,0 +1,305 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.decoder; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.Random; + +import org.infinity.resource.ResourceFactory; +import org.infinity.resource.cre.CreResource; +import org.infinity.resource.cre.decoder.tables.SpriteTables; +import org.infinity.resource.cre.decoder.util.AnimationInfo; +import org.infinity.resource.cre.decoder.util.CycleDef; +import org.infinity.resource.cre.decoder.util.DecoderAttribute; +import org.infinity.resource.cre.decoder.util.DirDef; +import org.infinity.resource.cre.decoder.util.Direction; +import org.infinity.resource.cre.decoder.util.SegmentDef; +import org.infinity.resource.cre.decoder.util.SeqDef; +import org.infinity.resource.cre.decoder.util.Sequence; +import org.infinity.resource.cre.decoder.util.SpriteUtils; +import org.infinity.resource.graphics.BamV1Decoder.BamV1Control; +import org.infinity.resource.key.ResourceEntry; +import org.infinity.util.IniMap; +import org.infinity.util.IniMapSection; +import org.infinity.util.tuples.Couple; + +/** + * Creature animation decoder for processing type 0000 (effect) animations. + * Available ranges: [0000,0fff] + */ +public class EffectDecoder extends SpriteDecoder +{ + /** The animation type associated with this class definition. */ + public static final AnimationInfo.Type ANIMATION_TYPE = AnimationInfo.Type.EFFECT; + + public static final DecoderAttribute KEY_SHADOW = DecoderAttribute.with("shadow", DecoderAttribute.DataType.STRING); + public static final DecoderAttribute KEY_PALLETIZED = DecoderAttribute.with("palletized", DecoderAttribute.DataType.BOOLEAN); + public static final DecoderAttribute KEY_RANDOM_RENDER = DecoderAttribute.with("random_render", DecoderAttribute.DataType.BOOLEAN); + public static final DecoderAttribute KEY_DELTA_Z = DecoderAttribute.with("delta_z", DecoderAttribute.DataType.INT); + public static final DecoderAttribute KEY_ALT_PALETTE = DecoderAttribute.with("alt_palette", DecoderAttribute.DataType.STRING); + public static final DecoderAttribute KEY_HEIGHT_CODE_SHIELD = DecoderAttribute.with("height_code_shield", DecoderAttribute.DataType.STRING); + public static final DecoderAttribute KEY_HEIGHT_CODE_HELMET = DecoderAttribute.with("height_code_helmet", DecoderAttribute.DataType.STRING); + public static final DecoderAttribute KEY_HEIGHT_CODE = DecoderAttribute.with("height_code", DecoderAttribute.DataType.STRING); + public static final DecoderAttribute KEY_RESREF_PAPERDOLL = DecoderAttribute.with("resref_paperdoll", DecoderAttribute.DataType.STRING); + public static final DecoderAttribute KEY_RESREF_ARMOR_BASE = DecoderAttribute.with("resref_armor_base", DecoderAttribute.DataType.STRING); + public static final DecoderAttribute KEY_RESREF_ARMOR_SPECIFIC = DecoderAttribute.with("resref_armor_specific", DecoderAttribute.DataType.STRING); + // Note: these attribute are artificial to store hardcoded information + // The cycle to play back (if >= 0) + public static final DecoderAttribute KEY_CYCLE = DecoderAttribute.with("cycle", DecoderAttribute.DataType.INT); + // A secondary resref to consider if random_render == 1 + public static final DecoderAttribute KEY_RESREF2 = DecoderAttribute.with("resref2", DecoderAttribute.DataType.STRING); + + protected final BeforeSourceBam FN_BEFORE_SRC_BAM = new BeforeSourceBam() { + @Override + public void accept(BamV1Control control, SegmentDef sd) + { + if (isPaletteReplacementEnabled() && sd.getSpriteType() == SegmentDef.SpriteType.AVATAR) { + int[] palette = getNewPaletteData(sd.getEntry()); + if (palette != null) { + SpriteUtils.applyNewPalette(control, palette); + } + } + + if (isFalseColor()) { + SpriteUtils.fixShadowColor(control, isTransparentShadow()); + if (isPaletteReplacementEnabled()) { + applyFalseColors(control, sd); + } + } + + if (isTintEnabled()) { + applyColorTint(control, sd); + } + + if ((isTranslucencyEnabled() && isTranslucent()) || + (isBlurEnabled() && isBlurred())) { + int minVal = (isBlurEnabled() && isBlurred()) ? 64 : 255; + applyTranslucency(control, minVal); + } + } + }; + + /** + * A helper method that parses the specified data array and generates a {@link IniMap} instance out of it. + * @param data a String array containing table values for a specific table entry. + * @return a {@code IniMap} instance with the value derived from the specified data array. + * Returns {@code null} if no data could be derived. + */ + public static IniMap processTableData(String[] data) + { + IniMap retVal = null; + if (data == null || data.length < 16) { + return retVal; + } + + String resref = SpriteTables.valueToString(data, SpriteTables.COLUMN_RESREF, ""); + if (resref.isEmpty()) { + return retVal; + } + String shadow = SpriteTables.valueToString(data, SpriteTables.COLUMN_RESREF2, ""); + int translucent = SpriteTables.valueToInt(data, SpriteTables.COLUMN_TRANSLUCENT, 0); + int falseColor = SpriteTables.valueToInt(data, SpriteTables.COLUMN_CLOWN, 0); + int random = SpriteTables.valueToInt(data, SpriteTables.COLUMN_SPLIT, 0); + int cycle = SpriteTables.valueToInt(data, SpriteTables.COLUMN_HELMET, -1); + String altPalette = SpriteTables.valueToString(data, SpriteTables.COLUMN_PALETTE2, ""); + String resref2 = SpriteTables.valueToString(data, SpriteTables.COLUMN_HEIGHT, ""); + + List lines = SpriteUtils.processTableDataGeneral(data, ANIMATION_TYPE); + lines.add("[effect]"); + lines.add("resref=" + resref); + if (!shadow.isEmpty()) { + lines.add("shadow=" + shadow); + } + lines.add("translucent=" + translucent); + lines.add("false_color=" + falseColor); + lines.add("random_render=" + random); + if (cycle >= 0) { + lines.add("cycle=" + cycle); + } + if (!altPalette.isEmpty()) { + lines.add("alt_palette=" + altPalette); + } + if (!resref2.isEmpty()) { + lines.add("resref2=" + resref2); + } + + retVal = IniMap.from(lines); + + return retVal; + } + + public EffectDecoder(int animationId, IniMap ini) throws Exception + { + super(ANIMATION_TYPE, animationId, ini); + } + + public EffectDecoder(CreResource cre) throws Exception + { + super(ANIMATION_TYPE, cre); + } + + /** Returns a separate shadow sprite resref. */ + public String getShadowResref() { return getAttribute(KEY_SHADOW); } + protected void setShadowResref(String s) { setAttribute(KEY_SHADOW, s); } + + /** Returns a secondary sprite resref to consider if RenderRandom() is set. */ + public String getSecondaryResref() { return getAttribute(KEY_RESREF2); } + protected void setSecondaryResref(String s) { setAttribute(KEY_RESREF2, s); } + + /** Returns a replacement palette resref (BMP). */ + public String getAltPalette() { return getAttribute(KEY_ALT_PALETTE); } + protected void setAltPalette(String s) { setAttribute(KEY_ALT_PALETTE, s); } + + /** Returns the height code prefix for shield overlay sprites. */ + public String getShieldHeightCode() { return getAttribute(KEY_HEIGHT_CODE_SHIELD); } + protected void setShieldHeightCode(String s) { setAttribute(KEY_HEIGHT_CODE_SHIELD, s); } + + /** Returns the height code prefix for helmet overlay sprites. */ + public String getHelmetHeightCode() { return getAttribute(KEY_HEIGHT_CODE_HELMET); } + protected void setHelmetHeightCode(String s) { setAttribute(KEY_HEIGHT_CODE_HELMET, s); } + + /** Returns the creature animation height code prefix. */ + public String getHeightCode() { return getAttribute(KEY_HEIGHT_CODE); } + protected void setHeightCode(String s) { setAttribute(KEY_HEIGHT_CODE, s); } + + /** Returns the paperdoll resref. */ + public String getPaperdollResref() { return getAttribute(KEY_RESREF_PAPERDOLL); } + protected void setPaperdollResref(String s) { setAttribute(KEY_RESREF_PAPERDOLL, s); } + + /** Returns animation resref suffix (last letter) for lesser armor types. */ + public String getArmorBaseResref() { return getAttribute(KEY_RESREF_ARMOR_BASE); } + protected void setArmorBaseResref(String s) { setAttribute(KEY_RESREF_ARMOR_BASE, s); } + + /** Returns animation resref suffix (last letter) for greater armor types. */ + public String getArmorSpecificResref() { return getAttribute(KEY_RESREF_ARMOR_SPECIFIC); } + protected void setArmorSpecificResref(String s) { setAttribute(KEY_RESREF_ARMOR_SPECIFIC, s); } + + /** unused */ + public boolean isPalettized() { return getAttribute(KEY_PALLETIZED); } + protected void setPalettized(boolean b) { setAttribute(KEY_PALLETIZED, b); } + + /** Returns whether a randomly chosen animation cycle is drawn. */ + public boolean isRenderRandom() { return getAttribute(KEY_RANDOM_RENDER); } + protected void setRenderRandom(boolean b) { setAttribute(KEY_RANDOM_RENDER, b); } + + /** Returns the BAM cycle index to use. -1 indicates no specific BAM cycle. */ + public int getCycle() { return getAttribute(KEY_CYCLE); } + protected void setCycle(int v) { setAttribute(KEY_CYCLE, v); } + + /** ??? */ + public int getDeltaZ() { return getAttribute(KEY_DELTA_Z); } + protected void setDeltaZ(int v) { setAttribute(KEY_DELTA_Z, v); } + + @Override + public String getNewPalette() + { + String retVal = getAltPalette(); + if (retVal == null || retVal.isEmpty()) { + retVal = super.getNewPalette(); + } + return retVal; + } + + @Override + public List getAnimationFiles(boolean essential) + { + ArrayList retVal = new ArrayList<>(); + retVal.add(getAnimationResref() + ".BAM"); + return retVal; + } + + @Override + public boolean isSequenceAvailable(Sequence seq) + { + return (getSequenceDefinition(seq) != null); + } + + @Override + protected void init() throws Exception + { + // setting properties + initDefaults(getAnimationInfo()); + IniMapSection section = getSpecificIniSection(); + setShadowResref(section.getAsString(KEY_SHADOW.getName(), "")); + setPalettized(section.getAsInteger(KEY_PALLETIZED.getName(), 0) != 0); + setTranslucent(section.getAsInteger(KEY_TRANSLUCENT.getName(), 0) != 0); + setRenderRandom(section.getAsInteger(KEY_RANDOM_RENDER.getName(), 0) != 0); + setFalseColor(section.getAsInteger(KEY_FALSE_COLOR.getName(), 0) != 0); + setCycle(section.getAsInteger(KEY_CYCLE.getName(), -1)); + setDeltaZ(section.getAsInteger(KEY_DELTA_Z.getName(), 0)); + setAltPalette(section.getAsString(KEY_ALT_PALETTE.getName(), "")); + setShieldHeightCode(section.getAsString(KEY_HEIGHT_CODE_SHIELD.getName(), "")); + setHelmetHeightCode(section.getAsString(KEY_HEIGHT_CODE_HELMET.getName(), "")); + setHeightCode(section.getAsString(KEY_HEIGHT_CODE.getName(), "")); + setPaperdollResref(section.getAsString(KEY_RESREF_PAPERDOLL.getName(), "")); + setArmorBaseResref(section.getAsString(KEY_RESREF_ARMOR_BASE.getName(), "")); + setArmorSpecificResref(section.getAsString(KEY_RESREF_ARMOR_SPECIFIC.getName(), "")); + } + + @Override + protected void createSequence(Sequence seq, Direction[] directions) throws Exception + { + SeqDef sd = Objects.requireNonNull(getSequenceDefinition(seq), "Sequence not available: " + (seq != null ? seq : "(null)")); + if (directions == null) { + directions = Direction.values(); + } + createAnimation(sd, Arrays.asList(directions), FN_BEFORE_SRC_BAM, FN_BEFORE_SRC_FRAME, FN_AFTER_SRC_FRAME, FN_AFTER_DST_FRAME); + } + + @Override + protected SeqDef getSequenceDefinition(Sequence seq) + { + SeqDef retVal = null; + if (seq != Sequence.STAND) { + return retVal; + } + + ArrayList> creResList = new ArrayList<>(); + if (!getShadowResref().isEmpty()) { + ResourceEntry shdEntry = ResourceFactory.getResourceEntry(getShadowResref() + ".BAM"); + if (shdEntry != null) { + creResList.add(Couple.with(shdEntry, 0)); + } + } + + Random rnd = new Random(); + String resref = getAnimationResref(); + if (isRenderRandom()) { + if (!getSecondaryResref().isEmpty() && (Math.abs(rnd.nextInt()) % 3) == 0) { + resref = getSecondaryResref(); + } + } + + ResourceEntry resEntry = ResourceFactory.getResourceEntry(resref + ".BAM"); + BamControl ctrl = SpriteUtils.loadBamController(resEntry); + if (ctrl != null) { + int cycle = 0; + if (isRenderRandom()) { + cycle = Math.abs(rnd.nextInt()) % ctrl.cycleCount(); + } else if (getCycle() >= 0) { + cycle = getCycle(); + } + creResList.add(Couple.with(resEntry, 0)); + + retVal = new SeqDef(seq); + for (final Couple data : creResList) { + resEntry = data.getValue0(); + cycle = data.getValue1().intValue(); + if (SpriteUtils.bamCyclesExist(resEntry, cycle, 1)) { + retVal.addDirections(new DirDef(Direction.S, false, new CycleDef(resEntry, cycle))); + } + } + } + + if (retVal.isEmpty()) { + retVal = null; + } + + return retVal; + } +} diff --git a/src/org/infinity/resource/cre/decoder/FlyingDecoder.java b/src/org/infinity/resource/cre/decoder/FlyingDecoder.java new file mode 100644 index 000000000..78178c0ea --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/FlyingDecoder.java @@ -0,0 +1,119 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.decoder; + +import java.util.ArrayList; +import java.util.List; + +import org.infinity.resource.ResourceFactory; +import org.infinity.resource.cre.CreResource; +import org.infinity.resource.cre.decoder.tables.SpriteTables; +import org.infinity.resource.cre.decoder.util.AnimationInfo; +import org.infinity.resource.cre.decoder.util.DirDef; +import org.infinity.resource.cre.decoder.util.SeqDef; +import org.infinity.resource.cre.decoder.util.Sequence; +import org.infinity.resource.cre.decoder.util.SpriteUtils; +import org.infinity.resource.key.ResourceEntry; +import org.infinity.util.IniMap; +import org.infinity.util.IniMapSection; + +/** + * Creature animation decoder for processing type D000 (flying) animations. + * Available ranges: [d000,dfff] + */ +public class FlyingDecoder extends SpriteDecoder +{ + /** The animation type associated with this class definition. */ + public static final AnimationInfo.Type ANIMATION_TYPE = AnimationInfo.Type.FLYING; + + /** + * A helper method that parses the specified data array and generates a {@link IniMap} instance out of it. + * @param data a String array containing table values for a specific table entry. + * @return a {@code IniMap} instance with the value derived from the specified data array. + * Returns {@code null} if no data could be derived. + */ + public static IniMap processTableData(String[] data) + { + IniMap retVal = null; + if (data == null || data.length < 16) { + return retVal; + } + + String resref = SpriteTables.valueToString(data, SpriteTables.COLUMN_RESREF, ""); + if (resref.isEmpty()) { + return retVal; + } + int falseColor = SpriteTables.valueToInt(data, SpriteTables.COLUMN_CLOWN, 0); + + List lines = SpriteUtils.processTableDataGeneral(data, ANIMATION_TYPE); + lines.add("[flying]"); + lines.add("false_color=" + falseColor); + lines.add("resref=" + resref); + + retVal = IniMap.from(lines); + + return retVal; + } + + public FlyingDecoder(int animationId, IniMap ini) throws Exception + { + super(ANIMATION_TYPE, animationId, ini); + } + + public FlyingDecoder(CreResource cre) throws Exception + { + super(ANIMATION_TYPE, cre); + } + + @Override + public List getAnimationFiles(boolean essential) + { + ArrayList retVal = new ArrayList<>(); + retVal.add(getAnimationResref() + ".BAM"); + return retVal; + } + + @Override + public boolean isSequenceAvailable(Sequence seq) + { + return (getSequenceDefinition(seq) != null); + } + + @Override + protected void init() throws Exception + { + // setting properties + initDefaults(getAnimationInfo()); + IniMapSection section = getSpecificIniSection(); + setFalseColor(section.getAsInteger(KEY_FALSE_COLOR.getName(), 0) != 0); + } + + @Override + protected SeqDef getSequenceDefinition(Sequence seq) + { + SeqDef retVal = null; + + int cycleIndex = 0; + switch (seq) { + case STAND: + cycleIndex = 0; + break; + case WALK: + cycleIndex = 9; + break; + default: + return retVal; + } + + ResourceEntry entry = ResourceFactory.getResourceEntry(getAnimationResref() + ".BAM"); + if (SpriteUtils.bamCyclesExist(entry, cycleIndex, SeqDef.DIR_FULL_W.length)) { + retVal = SeqDef.createSequence(seq, SeqDef.DIR_FULL_W, false, entry, cycleIndex, null); + SeqDef tmp = SeqDef.createSequence(seq, SeqDef.DIR_FULL_E, true, entry, cycleIndex + 1, null); + retVal.addDirections(tmp.getDirections().toArray(new DirDef[tmp.getDirections().size()])); + } + + return retVal; + } +} diff --git a/src/org/infinity/resource/cre/decoder/MonsterAnkhegDecoder.java b/src/org/infinity/resource/cre/decoder/MonsterAnkhegDecoder.java new file mode 100644 index 000000000..32d61dbdb --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/MonsterAnkhegDecoder.java @@ -0,0 +1,182 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.decoder; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import org.infinity.resource.ResourceFactory; +import org.infinity.resource.cre.CreResource; +import org.infinity.resource.cre.decoder.tables.SpriteTables; +import org.infinity.resource.cre.decoder.util.AnimationInfo; +import org.infinity.resource.cre.decoder.util.DecoderAttribute; +import org.infinity.resource.cre.decoder.util.DirDef; +import org.infinity.resource.cre.decoder.util.Direction; +import org.infinity.resource.cre.decoder.util.SegmentDef; +import org.infinity.resource.cre.decoder.util.SeqDef; +import org.infinity.resource.cre.decoder.util.Sequence; +import org.infinity.resource.cre.decoder.util.SpriteUtils; +import org.infinity.resource.key.ResourceEntry; +import org.infinity.util.IniMap; +import org.infinity.util.IniMapSection; +import org.infinity.util.tuples.Couple; + +/** + * Creature animation decoder for processing type 3000 (monster_ankheg) animations. + * Available ranges: [3000,3fff] + */ +public class MonsterAnkhegDecoder extends SpriteDecoder +{ + /** The animation type associated with this class definition. */ + public static final AnimationInfo.Type ANIMATION_TYPE = AnimationInfo.Type.MONSTER_ANKHEG; + + public static final DecoderAttribute KEY_MIRROR = DecoderAttribute.with("mirror", DecoderAttribute.DataType.BOOLEAN); + public static final DecoderAttribute KEY_EXTEND_DIRECTION = DecoderAttribute.with("extend_direction", DecoderAttribute.DataType.BOOLEAN); + + private static final HashMap> suffixMap = new HashMap>() {{ + // Note: int value indicates direction segment multiplier + put(Sequence.DIE, Couple.with("G1", 1)); + put(Sequence.SLEEP, get(Sequence.DIE)); + put(Sequence.GET_UP, Couple.with("!G1", 1)); + put(Sequence.TWITCH, Couple.with("G1", 2)); + put(Sequence.STAND_EMERGED, Couple.with("G1", 3)); + put(Sequence.STAND_HIDDEN, Couple.with("G2", 0)); + put(Sequence.EMERGE, Couple.with("G2", 1)); + put(Sequence.HIDE, Couple.with("G2", 2)); + put(Sequence.ATTACK, Couple.with("G3", 0)); + put(Sequence.SPELL, Couple.with("G3", 1)); + }}; + + /** + * A helper method that parses the specified data array and generates a {@link IniMap} instance out of it. + * @param data a String array containing table values for a specific table entry. + * @return a {@code IniMap} instance with the value derived from the specified data array. + * Returns {@code null} if no data could be derived. + */ + public static IniMap processTableData(String[] data) + { + IniMap retVal = null; + if (data == null || data.length < 16) { + return retVal; + } + + String resref = SpriteTables.valueToString(data, SpriteTables.COLUMN_RESREF, ""); + if (resref.isEmpty()) { + return retVal; + } + int falseColor = SpriteTables.valueToInt(data, SpriteTables.COLUMN_CLOWN, 0); + + List lines = SpriteUtils.processTableDataGeneral(data, ANIMATION_TYPE); + lines.add("[monster_ankheg]"); + lines.add("false_color=" + falseColor); + lines.add("resref=" + resref); + + retVal = IniMap.from(lines); + + return retVal; + } + + public MonsterAnkhegDecoder(int animationId, IniMap ini) throws Exception + { + super(ANIMATION_TYPE, animationId, ini); + } + + public MonsterAnkhegDecoder(CreResource cre) throws Exception + { + super(ANIMATION_TYPE, cre); + } + + /** Returns whether eastern directions are calculated. */ + public boolean hasMirroredDirections() { return getAttribute(KEY_MIRROR); } + protected void setMirroredDirections(boolean b) { setAttribute(KEY_MIRROR, b); } + + /** Returns whether the animation provides the full set of directions. */ + public boolean hasExtendedDirections() { return getAttribute(KEY_EXTEND_DIRECTION); } + protected void setExtendedDirections(boolean b) { setAttribute(KEY_EXTEND_DIRECTION, b); } + + @Override + public List getAnimationFiles(boolean essential) + { + String resref = getAnimationResref(); + ArrayList retVal = new ArrayList() {{ + add(resref + "DG1.BAM"); + if (!hasMirroredDirections()) { add(resref + "DG1E.BAM"); } + add(resref + "DG2.BAM"); + if (!hasMirroredDirections()) { add(resref + "DG2E.BAM"); } + add(resref + "DG3.BAM"); + if (!hasMirroredDirections()) { add(resref + "DG3E.BAM"); } + add(resref + "G1.BAM"); + if (!hasMirroredDirections()) { add(resref + "G1E.BAM"); } + add(resref + "G2.BAM"); + if (!hasMirroredDirections()) { add(resref + "G2E.BAM"); } + add(resref + "G3.BAM"); + if (!hasMirroredDirections()) { add(resref + "G3E.BAM"); } + }}; + return retVal; + } + + @Override + public boolean isSequenceAvailable(Sequence seq) + { + return (getSequenceDefinition(seq) != null); + } + + @Override + protected void init() throws Exception + { + // setting properties + initDefaults(getAnimationInfo()); + IniMapSection section = getSpecificIniSection(); + setMirroredDirections(section.getAsInteger(KEY_MIRROR.getName(), 0) != 0); + setExtendedDirections(section.getAsInteger(KEY_EXTEND_DIRECTION.getName(), 0) != 0); + setDetectedByInfravision(section.getAsInteger(KEY_DETECTED_BY_INFRAVISION.getName(), 0) != 0); + setFalseColor(section.getAsInteger(KEY_FALSE_COLOR.getName(), 0) != 0); + } + + @Override + protected SeqDef getSequenceDefinition(Sequence seq) + { + SeqDef retVal = null; + String resref = getAnimationResref(); + + Direction[] dirWest = hasExtendedDirections() ? SeqDef.DIR_FULL_W : SeqDef.DIR_REDUCED_W; + Direction[] dirEast = hasExtendedDirections() ? SeqDef.DIR_FULL_E : SeqDef.DIR_REDUCED_E; + int seg = dirWest.length; + if (!hasMirroredDirections()) { + seg += dirEast.length; + } + + String suffixE = hasMirroredDirections() ? "" : "E"; + int eastOfs = hasMirroredDirections() ? 1 : dirWest.length; + Couple data = suffixMap.get(seq); + if (data == null) { + return retVal; + } + + retVal = new SeqDef(seq); + SegmentDef.Behavior behavior = SegmentDef.getBehaviorOf(data.getValue0()); + String suffix = SegmentDef.fixBehaviorSuffix(data.getValue0()); + for (final String type : new String[] {"D", ""}) { + ResourceEntry entry = ResourceFactory.getResourceEntry(resref + type + suffix + ".BAM"); + int cycle = data.getValue1().intValue() * seg; + ResourceEntry entryE = ResourceFactory.getResourceEntry(resref + type + suffix + suffixE + ".BAM"); + int cycleE = cycle + eastOfs; + + if (SpriteUtils.bamCyclesExist(entry, cycle, dirWest.length) && + SpriteUtils.bamCyclesExist(entryE, cycleE, dirEast.length)) { + SeqDef tmp = SeqDef.createSequence(seq, dirWest, false, entry, cycle, null, behavior); + retVal.addDirections(tmp.getDirections().toArray(new DirDef[tmp.getDirections().size()])); + tmp = SeqDef.createSequence(seq, dirEast, hasMirroredDirections(), entryE, cycleE, null, behavior); + retVal.addDirections(tmp.getDirections().toArray(new DirDef[tmp.getDirections().size()])); + } + } + + if (retVal.isEmpty()) { + retVal = null; + } + return retVal; + } +} diff --git a/src/org/infinity/resource/cre/decoder/MonsterDecoder.java b/src/org/infinity/resource/cre/decoder/MonsterDecoder.java new file mode 100644 index 000000000..d646cf1db --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/MonsterDecoder.java @@ -0,0 +1,348 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.decoder; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; + +import org.infinity.resource.ResourceFactory; +import org.infinity.resource.cre.CreResource; +import org.infinity.resource.cre.decoder.tables.SpriteTables; +import org.infinity.resource.cre.decoder.util.AnimationInfo; +import org.infinity.resource.cre.decoder.util.DecoderAttribute; +import org.infinity.resource.cre.decoder.util.DirDef; +import org.infinity.resource.cre.decoder.util.Direction; +import org.infinity.resource.cre.decoder.util.ItemInfo; +import org.infinity.resource.cre.decoder.util.SegmentDef; +import org.infinity.resource.cre.decoder.util.SeqDef; +import org.infinity.resource.cre.decoder.util.Sequence; +import org.infinity.resource.cre.decoder.util.SpriteUtils; +import org.infinity.resource.key.ResourceEntry; +import org.infinity.util.IniMap; +import org.infinity.util.IniMapSection; +import org.infinity.util.tuples.Couple; + +/** + * Creature animation decoder for processing type 7000 (monster) animations. + * Available ranges: (using notation slot/range where slot can be a formula with range definitions as [x,y]) + * (0x7002 | ([0x00,0x1f] << 4))/0xd + * (0x7004 | ([0x20,0x2f] << 4))/0xb + * (0x7000 | ([0x30,0x3f] << 4))/0xf + * (0x7003 | ([0x40,0x4f] << 4))/0xc + * (0x7002 | ([0x50,0x5f] << 4))/0xd + * (0x7003 | ([0x70,0x7f] << 4))/0xc + * (0x7005 | ([0x90,0xaf] << 4))/0xa + * (0x7007 | ([0xb0,0xbf] << 4))/0x8 + * (0x7002 | ([0xc0,0xcf] << 4))/0xd + * (0x7002 | ([0xe0,0xef] << 4))/0xd + * (0x7000 | ([0xf0,0xff] << 4))/0xf + */ +public class MonsterDecoder extends SpriteDecoder +{ + /** The animation type associated with this class definition. */ + public static final AnimationInfo.Type ANIMATION_TYPE = AnimationInfo.Type.MONSTER; + + public static final DecoderAttribute KEY_CAN_LIE_DOWN = DecoderAttribute.with("can_lie_down", DecoderAttribute.DataType.BOOLEAN); + public static final DecoderAttribute KEY_PATH_SMOOTH = DecoderAttribute.with("path_smooth", DecoderAttribute.DataType.BOOLEAN); + public static final DecoderAttribute KEY_SPLIT_BAMS = DecoderAttribute.with("split_bams", DecoderAttribute.DataType.BOOLEAN); + public static final DecoderAttribute KEY_GLOW_LAYER = DecoderAttribute.with("glow_layer", DecoderAttribute.DataType.STRING); + public static final DecoderAttribute KEY_PALETTE1 = DecoderAttribute.with("palette1", DecoderAttribute.DataType.STRING); + public static final DecoderAttribute KEY_PALETTE2 = DecoderAttribute.with("palette2", DecoderAttribute.DataType.STRING); + + /** Assigns BAM suffix and cycle index to a specific animation sequence (unsplit version). */ + private static final HashMap> suffixMapUnsplit = new HashMap>() {{ + put(Sequence.WALK, Couple.with("G1", 0)); + put(Sequence.STANCE, Couple.with("G1", 9)); + put(Sequence.STAND, Couple.with("G1", 18)); + put(Sequence.GET_HIT, Couple.with("G1", 27)); + put(Sequence.DIE, Couple.with("G1", 36)); + put(Sequence.TWITCH, Couple.with("G1", 45)); + put(Sequence.SLEEP, Couple.with("G1", 54)); + put(Sequence.GET_UP, Couple.with("G1", 63)); + put(Sequence.ATTACK, Couple.with("G2", 0)); + put(Sequence.ATTACK_2, Couple.with("G2", 9)); + put(Sequence.ATTACK_3, Couple.with("G2", 18)); + put(Sequence.ATTACK_4, Couple.with("G2", 27)); + put(Sequence.ATTACK_5, Couple.with("G2", 36)); + put(Sequence.SPELL, Couple.with("G2", 45)); + put(Sequence.CAST, Couple.with("G2", 54)); + }}; + + /** Assigns BAM suffix and cycle index to a specific animation sequence (split version). */ + private static final HashMap> suffixMapSplit = new HashMap>() {{ + put(Sequence.WALK, Couple.with("G11", 0)); + put(Sequence.STANCE, Couple.with("G1", 9)); + put(Sequence.STAND, Couple.with("G12", 18)); + put(Sequence.GET_HIT, Couple.with("G13", 27)); + put(Sequence.DIE, Couple.with("G14", 36)); + put(Sequence.SLEEP, get(Sequence.DIE)); + put(Sequence.GET_UP, Couple.with("!G14", 36)); + put(Sequence.TWITCH, Couple.with("G15", 45)); + put(Sequence.ATTACK, Couple.with("G2", 0)); + put(Sequence.ATTACK_2, Couple.with("G21", 9)); + put(Sequence.ATTACK_3, Couple.with("G22", 18)); + put(Sequence.ATTACK_4, Couple.with("G23", 27)); + put(Sequence.ATTACK_5, Couple.with("G24", 36)); + put(Sequence.SPELL, Couple.with("G25", 45)); + put(Sequence.CAST, Couple.with("G26", 54)); + }}; + + /** Replacement sequences if original sequence definition is missing (unsplit version). */ + private static final HashMap> replacementMapUnsplit = new HashMap>() {{ + put(Sequence.DIE, suffixMapUnsplit.get(Sequence.SLEEP)); + put(Sequence.SLEEP, suffixMapUnsplit.get(Sequence.DIE)); + put(Sequence.GET_UP, Couple.with("!" + suffixMapUnsplit.get(Sequence.DIE).getValue0(), suffixMapUnsplit.get(Sequence.DIE).getValue1())); + }}; + + /** Replacement sequences if original sequence definition is missing (split version). */ + private static final HashMap> replacementMapSplit = new HashMap>() {{ + // not needed + }}; + + + /** + * A helper method that parses the specified data array and generates a {@link IniMap} instance out of it. + * @param data a String array containing table values for a specific table entry. + * @return a {@code IniMap} instance with the value derived from the specified data array. + * Returns {@code null} if no data could be derived. + */ + public static IniMap processTableData(String[] data) + { + IniMap retVal = null; + if (data == null || data.length < 16) { + return retVal; + } + + String resref = SpriteTables.valueToString(data, SpriteTables.COLUMN_RESREF, ""); + if (resref.isEmpty()) { + return retVal; + } + int falseColor = SpriteTables.valueToInt(data, SpriteTables.COLUMN_CLOWN, 0); + int splitBams = SpriteTables.valueToInt(data, SpriteTables.COLUMN_SPLIT, 0); + int translucent = SpriteTables.valueToInt(data, SpriteTables.COLUMN_TRANSLUCENT, 0); + String palette1 = SpriteTables.valueToString(data, SpriteTables.COLUMN_PALETTE, ""); + String palette2 = SpriteTables.valueToString(data, SpriteTables.COLUMN_PALETTE2, ""); + + List lines = SpriteUtils.processTableDataGeneral(data, ANIMATION_TYPE); + lines.add("[monster]"); + lines.add("false_color=" + falseColor); + lines.add("split_bams=" + splitBams); + lines.add("translucent=" + translucent); + lines.add("resref=" + resref); + if (!palette1.isEmpty()) { + lines.add("palette1=" + palette1); + } + if (!palette2.isEmpty()) { + lines.add("palette2=" + palette2); + } + + retVal = IniMap.from(lines); + + return retVal; + } + + public MonsterDecoder(int animationId, IniMap ini) throws Exception + { + super(ANIMATION_TYPE, animationId, ini); + } + + public MonsterDecoder(CreResource cre) throws Exception + { + super(ANIMATION_TYPE, cre); + } + + /** Returns the correct sequence map for the current settings. */ + private HashMap> getSuffixMap() + { + return isSplittedBams() ? suffixMapSplit : suffixMapUnsplit; + } + + /** Returns whether the creature falls down when dead/unconscious. */ + public boolean canLieDown() { return getAttribute(KEY_CAN_LIE_DOWN); } + protected void setCanLieDown(boolean b) { setAttribute(KEY_CAN_LIE_DOWN, b); } + + /** ??? */ + public boolean isSmoothPath() { return getAttribute(KEY_PATH_SMOOTH); } + protected void setSmoothPath(boolean b) { setAttribute(KEY_PATH_SMOOTH, b); } + + /** Returns whether animations are spread over various subfiles. */ + public boolean isSplittedBams() { return getAttribute(KEY_SPLIT_BAMS); } + protected void setSplittedBams(boolean b) { setAttribute(KEY_SPLIT_BAMS, b); } + + /** + * Returns the solid background layer of blended/glowing creature animations. + * (Note: currently not supported by the engine.) + */ + public String getGlowLayer() { return getAttribute(KEY_GLOW_LAYER); } + protected void setGlowLayer(String s) { setAttribute(KEY_GLOW_LAYER, s); } + + /** + * Returns the first replacement palette (BMP) for the creature animation. + * Falls back to new palette from general attributes. + */ + public String getPalette1() + { + String retVal = getAttribute(KEY_PALETTE1); + if (retVal.isEmpty()) { + retVal = getNewPalette(); + } + return retVal; + } + + protected void setPalette1(String s) { setAttribute(KEY_PALETTE1, s); } + + /** + * Returns the second replacement palette (BMP) for the creature animation. + * Falls back to palette1 or new palette from general attributes. + */ + public String getPalette2() + { + String retVal = getAttribute(KEY_PALETTE2); + if (retVal.isEmpty()) { + retVal = getPalette1(); + } + return retVal; + } + + protected void setPalette2(String s) { setAttribute(KEY_PALETTE2, s); } + + @Override + public List getAnimationFiles(boolean essential) + { + // collecting suffixes + String resref = getAnimationResref(); + HashSet files = new HashSet<>(); + for (final HashMap.Entry> entry : getSuffixMap().entrySet()) { + String suffix = SegmentDef.fixBehaviorSuffix(entry.getValue().getValue0()); + files.add(resref + suffix + ".BAM"); + } + + // generating file list + ArrayList retVal = new ArrayList<>(Arrays.asList(files.toArray(new String[files.size()]))); + + return retVal; + } + + @Override + public boolean isSequenceAvailable(Sequence seq) + { + return (getSequenceDefinition(seq) != null); + } + + @Override + protected void init() throws Exception + { + // setting properties + initDefaults(getAnimationInfo()); + IniMapSection section = getSpecificIniSection(); + setCanLieDown(section.getAsInteger(KEY_CAN_LIE_DOWN.getName(), 0) != 0); + setFalseColor(section.getAsInteger(KEY_FALSE_COLOR.getName(), 0) != 0); + setDetectedByInfravision(section.getAsInteger(KEY_DETECTED_BY_INFRAVISION.getName(), 0) != 0); + setSmoothPath(section.getAsInteger(KEY_PATH_SMOOTH.getName(), 0) != 0); + setSplittedBams(section.getAsInteger(KEY_SPLIT_BAMS.getName(), 0) != 0); + setTranslucent(section.getAsInteger(KEY_TRANSLUCENT.getName(), 0) != 0); + String s = section.getAsString(KEY_GLOW_LAYER.getName(), ""); + if (s.isEmpty()) { + // KEY_GLOW_LAYER maybe incorrectly assigned to "general" section + s = getGeneralIniSection(getAnimationInfo()).getAsString(KEY_GLOW_LAYER.getName(), ""); + } + setGlowLayer(s); + setPalette1(section.getAsString(KEY_PALETTE1.getName(), "")); + setPalette2(section.getAsString(KEY_PALETTE2.getName(), "")); + } + + @Override + protected SeqDef getSequenceDefinition(Sequence seq) + { + SeqDef retVal = null; + + Couple data = getSuffixMap().get(seq); + if (data == null) { + return retVal; + } + + ArrayList> creResList = new ArrayList<>(); + + // processing creature sprite + String resref = getAnimationResref(); + String suffix = data.getValue0(); + int ofs = data.getValue1().intValue(); + ResourceEntry bamEntry = ResourceFactory.getResourceEntry(resref + SegmentDef.fixBehaviorSuffix(suffix) + ".BAM"); + if (!SpriteUtils.bamCyclesExist(bamEntry, ofs, 1)) { + data = (isSplittedBams() ? replacementMapSplit: replacementMapUnsplit).get(seq); + if (data == null) { + return retVal; + } + suffix = data.getValue0(); + bamEntry = ResourceFactory.getResourceEntry(resref + SegmentDef.fixBehaviorSuffix(suffix) + ".BAM"); + if (!SpriteUtils.bamCyclesExist(bamEntry, ofs, 1)) { + return retVal; + } + } + SegmentDef.Behavior behavior = SegmentDef.getBehaviorOf(suffix); + suffix = SegmentDef.fixBehaviorSuffix(suffix); + creResList.add(Couple.with(resref + suffix + ".BAM", SegmentDef.SpriteType.AVATAR)); + + // processing weapon overlay + ItemInfo itmWeapon = getCreatureInfo().getEquippedWeapon(); + if (itmWeapon != null) { + String weapon = itmWeapon.getAppearance().trim(); + if (!weapon.isEmpty()) { + Couple wdata = suffixMapUnsplit.get(seq); + if (wdata != null) { + creResList.add(Couple.with(resref + wdata.getValue0() + weapon + ".BAM", SegmentDef.SpriteType.WEAPON)); + } + } + } + + retVal = new SeqDef(seq); + for (final Couple creInfo : creResList) { + ResourceEntry entry = ResourceFactory.getResourceEntry(creInfo.getValue0()); + SegmentDef.SpriteType type = creInfo.getValue1(); + if (SpriteUtils.bamCyclesExist(entry, ofs, SeqDef.DIR_FULL_W.length)) { + SeqDef tmp = SeqDef.createSequence(seq, SeqDef.DIR_FULL_W, false, entry, ofs, type, behavior); + retVal.addDirections(tmp.getDirections().toArray(new DirDef[tmp.getDirections().size()])); + tmp = SeqDef.createSequence(seq, SeqDef.DIR_FULL_E, true, entry, ofs + 1, type, behavior); + retVal.addDirections(tmp.getDirections().toArray(new DirDef[tmp.getDirections().size()])); + } else if (entry != null && SpriteUtils.getBamCycles(entry) == 1) { + // fallback solution: just use first bam cycle (required by a few animations) + for (final Direction dir : SeqDef.DIR_FULL_W) { + SeqDef tmp = SeqDef.createSequence(seq, new Direction[] {dir}, false, entry, 0, type, behavior); + retVal.addDirections(tmp.getDirections().toArray(new DirDef[tmp.getDirections().size()])); + } + for (final Direction dir : SeqDef.DIR_FULL_E) { + SeqDef tmp = SeqDef.createSequence(seq, new Direction[] {dir}, true, entry, 0, type, behavior); + retVal.addDirections(tmp.getDirections().toArray(new DirDef[tmp.getDirections().size()])); + } + } + } + + if (retVal.isEmpty()) { + retVal = null; + } + + return retVal; + } + + @Override + protected int[] getNewPaletteData(ResourceEntry bamRes) + { + if (bamRes != null) { + String resref = bamRes.getResourceRef(); + if (resref.length() >= 6) { + switch (resref.charAt(5)) { + case '1': + return SpriteUtils.loadReplacementPalette(getPalette1()); + case '2': + return SpriteUtils.loadReplacementPalette(getPalette2()); + } + } + } + return super.getNewPaletteData(bamRes); + } +} diff --git a/src/org/infinity/resource/cre/decoder/MonsterIcewindDecoder.java b/src/org/infinity/resource/cre/decoder/MonsterIcewindDecoder.java new file mode 100644 index 000000000..5b5464e6e --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/MonsterIcewindDecoder.java @@ -0,0 +1,208 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.decoder; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import org.infinity.resource.ResourceFactory; +import org.infinity.resource.cre.CreResource; +import org.infinity.resource.cre.decoder.tables.SpriteTables; +import org.infinity.resource.cre.decoder.util.AnimationInfo; +import org.infinity.resource.cre.decoder.util.DecoderAttribute; +import org.infinity.resource.cre.decoder.util.DirDef; +import org.infinity.resource.cre.decoder.util.ItemInfo; +import org.infinity.resource.cre.decoder.util.SegmentDef; +import org.infinity.resource.cre.decoder.util.SeqDef; +import org.infinity.resource.cre.decoder.util.Sequence; +import org.infinity.resource.cre.decoder.util.SpriteUtils; +import org.infinity.resource.key.ResourceEntry; +import org.infinity.util.IniMap; +import org.infinity.util.IniMapSection; + +/** + * Creature animation decoder for processing type E000 (monster_icewind) animations. + * Available ranges: [e000,efff] + */ +public class MonsterIcewindDecoder extends SpriteDecoder +{ + /** The animation type associated with this class definition. */ + public static final AnimationInfo.Type ANIMATION_TYPE = AnimationInfo.Type.MONSTER_ICEWIND; + + public static final DecoderAttribute KEY_WEAPON_LEFT_HAND = DecoderAttribute.with("weapon_left_hand", DecoderAttribute.DataType.BOOLEAN); + + private static final HashMap seqMap = new HashMap() {{ + put(Sequence.ATTACK, "A1"); + put(Sequence.ATTACK_2, "A2"); + put(Sequence.ATTACK_3, "A3"); + put(Sequence.ATTACK_4, "A4"); + put(Sequence.CAST, "CA"); + put(Sequence.DIE, "DE"); + put(Sequence.GET_HIT, "GH"); + put(Sequence.GET_UP, "GU"); + put(Sequence.STANCE, "SC"); + put(Sequence.STAND, "SD"); + put(Sequence.SLEEP, "SL"); + put(Sequence.SPELL, "SP"); + put(Sequence.TWITCH, "TW"); + put(Sequence.WALK, "WK"); + }}; + + private static final HashMap replacementMap = new HashMap() {{ + put(Sequence.DIE, seqMap.get(Sequence.SLEEP)); + put(Sequence.SLEEP, seqMap.get(Sequence.DIE)); + put(Sequence.GET_UP, "!" + seqMap.get(Sequence.DIE)); + }}; + + /** + * A helper method that parses the specified data array and generates a {@link IniMap} instance out of it. + * @param data a String array containing table values for a specific table entry. + * @return a {@code IniMap} instance with the value derived from the specified data array. + * Returns {@code null} if no data could be derived. + */ + public static IniMap processTableData(String[] data) + { + IniMap retVal = null; + if (data == null || data.length < 16) { + return retVal; + } + + String resref = SpriteTables.valueToString(data, SpriteTables.COLUMN_RESREF, ""); + if (resref.isEmpty()) { + return retVal; + } + int translucent = SpriteTables.valueToInt(data, SpriteTables.COLUMN_TRANSLUCENT, 0); + int leftHanded = SpriteTables.valueToInt(data, SpriteTables.COLUMN_WEAPON, 0); + + List lines = SpriteUtils.processTableDataGeneral(data, ANIMATION_TYPE); + lines.add("[monster_icewind]"); + lines.add("weapon_left_hand=" + leftHanded); + lines.add("translucent=" + translucent); + lines.add("resref=" + resref); + + retVal = IniMap.from(lines); + + return retVal; + } + + public MonsterIcewindDecoder(int animationId, IniMap ini) throws Exception + { + super(ANIMATION_TYPE, animationId, ini); + } + + public MonsterIcewindDecoder(CreResource cre) throws Exception + { + super(ANIMATION_TYPE, cre); + } + + /** ??? */ + public boolean isWeaponInLeftHand() { return getAttribute(KEY_WEAPON_LEFT_HAND); } + protected void setWeaponInLeftHand(boolean b) { setAttribute(KEY_WEAPON_LEFT_HAND, b); } + + @Override + public List getAnimationFiles(boolean essential) + { + ArrayList retVal = new ArrayList<>(); + String resref = getAnimationResref(); + + final String[] defOvls = essential ? new String[] { "" } + : new String[] { "", "A", "B", "C", "D", "F", "H", "M", "Q", "S", "W" }; + final String[] defSeqs = essential ? new String[] { "DE", "GH", "SD", "WK" } + : new String[] { "A1", "A2", "A3", "A4", "CA", "DE", "GH", "GU", "SC", "SD", "SL", "SP", "TW", "WK" }; + for (final String ovl : defOvls) { + for (final String seq : defSeqs) { + String bamFile = resref + ovl + seq + ".BAM"; + if (ResourceFactory.resourceExists(bamFile)) { + retVal.add(bamFile); + } + bamFile = resref + ovl + seq + "E.BAM"; + if (ResourceFactory.resourceExists(bamFile)) { + retVal.add(bamFile); + } + } + } + + return retVal; + } + + @Override + public boolean isSequenceAvailable(Sequence seq) + { + return (getSequenceDefinition(seq) != null); + } + + @Override + protected void init() throws Exception + { + // setting properties + initDefaults(getAnimationInfo()); + IniMapSection section = getSpecificIniSection(); + setWeaponInLeftHand(section.getAsInteger(KEY_WEAPON_LEFT_HAND.getName(), 0) != 0); + setTranslucent(section.getAsInteger(KEY_TRANSLUCENT.getName(), 0) != 0); + setDetectedByInfravision(section.getAsInteger(KEY_DETECTED_BY_INFRAVISION.getName(), 0) != 0); + } + + @Override + protected SeqDef getSequenceDefinition(Sequence seq) + { + SeqDef retVal = null; + if (!seqMap.containsKey(seq)) { + return retVal; + } + + String resref = getAnimationResref(); + + // getting weapon code from CRE resource + String weapon = ""; + ItemInfo itmWeapon = getCreatureInfo().getEquippedWeapon(); + if (itmWeapon != null) { + weapon = itmWeapon.getAppearance(); + if (!weapon.isEmpty()) { + weapon = weapon.substring(0, 1).trim(); + } + weapon = weapon.trim(); + } + + // checking availability of sequence + String suffix = seqMap.get(seq); + if (!ResourceFactory.resourceExists(resref + SegmentDef.fixBehaviorSuffix(suffix) + ".BAM")) { + suffix = replacementMap.get(seq); + if (!ResourceFactory.resourceExists(resref + SegmentDef.fixBehaviorSuffix(suffix) + ".BAM")) { + return retVal; + } + } + + SegmentDef.Behavior behavior = SegmentDef.getBehaviorOf(suffix); + suffix = SegmentDef.fixBehaviorSuffix(suffix); + + retVal = new SeqDef(seq); + String[] ovls = weapon.isEmpty() ? new String[] {""} : new String[] {"", weapon}; + for (final String ovl : ovls) { + SegmentDef.SpriteType spriteType = (!weapon.isEmpty() && ovl.equals(weapon)) ? SegmentDef.SpriteType.WEAPON : SegmentDef.SpriteType.AVATAR; + ResourceEntry entry = ResourceFactory.getResourceEntry(resref + ovl + suffix + ".BAM"); + if (entry != null) { + ResourceEntry entryE = ResourceFactory.getResourceEntry(resref + ovl + suffix + "E.BAM"); + if (SpriteUtils.bamCyclesExist(entry, 0, SeqDef.DIR_REDUCED_W.length)) { + SeqDef tmp = SeqDef.createSequence(seq, SeqDef.DIR_REDUCED_W, false, entry, 0, spriteType, behavior); + retVal.addDirections(tmp.getDirections().toArray(new DirDef[tmp.getDirections().size()])); + if (SpriteUtils.bamCyclesExist(entryE, 0, SeqDef.DIR_REDUCED_E.length)) { + tmp = SeqDef.createSequence(seq, SeqDef.DIR_REDUCED_E, false, entryE, SeqDef.DIR_REDUCED_W.length, spriteType, behavior); + } else { + // fallback: mirror eastern directions + tmp = SeqDef.createSequence(seq, SeqDef.DIR_REDUCED_E, true, entry, 1, spriteType, behavior); + } + retVal.addDirections(tmp.getDirections().toArray(new DirDef[tmp.getDirections().size()])); + } + } + } + + if (retVal.isEmpty()) { + retVal = null; + } + + return retVal; + } +} diff --git a/src/org/infinity/resource/cre/decoder/MonsterLarge16Decoder.java b/src/org/infinity/resource/cre/decoder/MonsterLarge16Decoder.java new file mode 100644 index 000000000..f86c694c5 --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/MonsterLarge16Decoder.java @@ -0,0 +1,148 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.decoder; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import org.infinity.resource.ResourceFactory; +import org.infinity.resource.cre.CreResource; +import org.infinity.resource.cre.decoder.tables.SpriteTables; +import org.infinity.resource.cre.decoder.util.AnimationInfo; +import org.infinity.resource.cre.decoder.util.DirDef; +import org.infinity.resource.cre.decoder.util.Direction; +import org.infinity.resource.cre.decoder.util.SegmentDef; +import org.infinity.resource.cre.decoder.util.SeqDef; +import org.infinity.resource.cre.decoder.util.Sequence; +import org.infinity.resource.cre.decoder.util.SpriteUtils; +import org.infinity.resource.key.ResourceEntry; +import org.infinity.util.IniMap; +import org.infinity.util.IniMapSection; +import org.infinity.util.tuples.Couple; + +/** + * Creature animation decoder for processing type A000 (monster_large16) animations. + * Available ranges: [a000,afff] + */ +public class MonsterLarge16Decoder extends SpriteDecoder +{ + /** The animation type associated with this class definition. */ + public static final AnimationInfo.Type ANIMATION_TYPE = AnimationInfo.Type.MONSTER_LARGE_16; + + private static final HashMap> suffixMap = new HashMap>() {{ + put(Sequence.WALK, Couple.with("G1", 0)); + put(Sequence.STAND, Couple.with("G2", 0)); + put(Sequence.STANCE, Couple.with("G2", 16)); + put(Sequence.GET_HIT, Couple.with("G2", 32)); + put(Sequence.DIE, Couple.with("G2", 48)); + put(Sequence.SLEEP, get(Sequence.DIE)); + put(Sequence.GET_UP, Couple.with("!G2", 48)); + put(Sequence.TWITCH, Couple.with("G2", 64)); + put(Sequence.ATTACK, Couple.with("G3", 0)); + put(Sequence.ATTACK_2, Couple.with("G3", 16)); + put(Sequence.ATTACK_3, Couple.with("G3", 32)); + }}; + + /** + * A helper method that parses the specified data array and generates a {@link IniMap} instance out of it. + * @param data a String array containing table values for a specific table entry. + * @return a {@code IniMap} instance with the value derived from the specified data array. + * Returns {@code null} if no data could be derived. + */ + public static IniMap processTableData(String[] data) + { + IniMap retVal = null; + if (data == null || data.length < 16) { + return retVal; + } + + String resref = SpriteTables.valueToString(data, SpriteTables.COLUMN_RESREF, ""); + if (resref.isEmpty()) { + return retVal; + } + int falseColor = SpriteTables.valueToInt(data, SpriteTables.COLUMN_CLOWN, 0); + + List lines = SpriteUtils.processTableDataGeneral(data, ANIMATION_TYPE); + lines.add("[monster_large16]"); + lines.add("false_color=" + falseColor); + lines.add("resref=" + resref); + + retVal = IniMap.from(lines); + + return retVal; + } + + public MonsterLarge16Decoder(int animationId, IniMap ini) throws Exception + { + super(ANIMATION_TYPE, animationId, ini); + } + + public MonsterLarge16Decoder(CreResource cre) throws Exception + { + super(ANIMATION_TYPE, cre); + } + + @Override + public List getAnimationFiles(boolean essential) + { + String resref = getAnimationResref(); + ArrayList retVal = new ArrayList() {{ + add(resref + "G1.BAM"); + add(resref + "G1E.BAM"); + add(resref + "G2.BAM"); + add(resref + "G2E.BAM"); + add(resref + "G3.BAM"); + add(resref + "G3E.BAM"); + }}; + return retVal; + } + + @Override + public boolean isSequenceAvailable(Sequence seq) + { + return (getSequenceDefinition(seq) != null); + } + + @Override + protected void init() throws Exception + { + // setting properties + initDefaults(getAnimationInfo()); + IniMapSection section = getSpecificIniSection(); + setFalseColor(section.getAsInteger(KEY_FALSE_COLOR.getName(), 0) != 0); + setDetectedByInfravision(section.getAsInteger(KEY_DETECTED_BY_INFRAVISION.getName(), 0) != 0); + } + + @Override + protected SeqDef getSequenceDefinition(Sequence seq) + { + SeqDef retVal = null; + String resref = getAnimationResref(); + // special case: directions west include NNE + Direction[] dir_w = new Direction[SeqDef.DIR_FULL_W.length + 1]; + System.arraycopy(SeqDef.DIR_FULL_W, 0, dir_w, 0, SeqDef.DIR_FULL_W.length); + dir_w[SeqDef.DIR_FULL_W.length] = Direction.NNE; + Direction[] dir_e = new Direction[SeqDef.DIR_FULL_E.length - 1]; + System.arraycopy(SeqDef.DIR_FULL_E, 1, dir_e, 0, dir_e.length); + Couple data = suffixMap.get(seq); + if (data != null) { + SegmentDef.Behavior behavior = SegmentDef.getBehaviorOf(data.getValue0()); + String suffix = SegmentDef.fixBehaviorSuffix(data.getValue0()); + ResourceEntry entry = ResourceFactory.getResourceEntry(resref + suffix + ".BAM"); + int cycle = data.getValue1().intValue(); + ResourceEntry entryE = ResourceFactory.getResourceEntry(resref + suffix + "E.BAM"); + int cycleE = cycle + dir_w.length; + if (SpriteUtils.bamCyclesExist(entry, cycle, dir_w.length) && + SpriteUtils.bamCyclesExist(entryE, cycleE, dir_e.length)) { + retVal = SeqDef.createSequence(seq, dir_w, false, entry, cycle, null, behavior); + SeqDef tmp = SeqDef.createSequence(seq, dir_e, false, entryE, cycleE, null, behavior); + retVal.addDirections(tmp.getDirections().toArray(new DirDef[tmp.getDirections().size()])); + } + } + + return retVal; + } +} diff --git a/src/org/infinity/resource/cre/decoder/MonsterLargeDecoder.java b/src/org/infinity/resource/cre/decoder/MonsterLargeDecoder.java new file mode 100644 index 000000000..27f6ba399 --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/MonsterLargeDecoder.java @@ -0,0 +1,140 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.decoder; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import org.infinity.resource.ResourceFactory; +import org.infinity.resource.cre.CreResource; +import org.infinity.resource.cre.decoder.tables.SpriteTables; +import org.infinity.resource.cre.decoder.util.AnimationInfo; +import org.infinity.resource.cre.decoder.util.DirDef; +import org.infinity.resource.cre.decoder.util.SegmentDef; +import org.infinity.resource.cre.decoder.util.SeqDef; +import org.infinity.resource.cre.decoder.util.Sequence; +import org.infinity.resource.cre.decoder.util.SpriteUtils; +import org.infinity.resource.key.ResourceEntry; +import org.infinity.util.IniMap; +import org.infinity.util.IniMapSection; +import org.infinity.util.tuples.Couple; + +/** + * Creature animation decoder for processing type 9000 (monster_large) animations. + * Available ranges: [9000,9fff] + */ +public class MonsterLargeDecoder extends SpriteDecoder +{ + /** The animation type associated with this class definition. */ + public static final AnimationInfo.Type ANIMATION_TYPE = AnimationInfo.Type.MONSTER_LARGE; + + private static final HashMap> suffixMap = new HashMap>() {{ + put(Sequence.STAND, Couple.with("G1", 0)); + put(Sequence.STANCE, Couple.with("G1", 8)); + put(Sequence.WALK, Couple.with("G1", 16)); + put(Sequence.ATTACK, Couple.with("G2", 0)); + put(Sequence.ATTACK_2, Couple.with("G2", 8)); + put(Sequence.ATTACK_3, Couple.with("G3", 0)); + put(Sequence.GET_HIT, Couple.with("G3", 8)); + put(Sequence.DIE, Couple.with("G3", 16)); + put(Sequence.SLEEP, get(Sequence.DIE)); + put(Sequence.GET_UP, Couple.with("!G3", 16)); + put(Sequence.TWITCH, Couple.with("G3", 24)); + }}; + + /** + * A helper method that parses the specified data array and generates a {@link IniMap} instance out of it. + * @param data a String array containing table values for a specific table entry. + * @return a {@code IniMap} instance with the value derived from the specified data array. + * Returns {@code null} if no data could be derived. + */ + public static IniMap processTableData(String[] data) + { + IniMap retVal = null; + if (data == null || data.length < 16) { + return retVal; + } + + String resref = SpriteTables.valueToString(data, SpriteTables.COLUMN_RESREF, ""); + if (resref.isEmpty()) { + return retVal; + } + int falseColor = SpriteTables.valueToInt(data, SpriteTables.COLUMN_CLOWN, 0); + + List lines = SpriteUtils.processTableDataGeneral(data, ANIMATION_TYPE); + lines.add("[monster_large]"); + lines.add("false_color=" + falseColor); + lines.add("resref=" + resref); + + retVal = IniMap.from(lines); + + return retVal; + } + + public MonsterLargeDecoder(int animationId, IniMap ini) throws Exception + { + super(ANIMATION_TYPE, animationId, ini); + } + + public MonsterLargeDecoder(CreResource cre) throws Exception + { + super(ANIMATION_TYPE, cre); + } + + @Override + public List getAnimationFiles(boolean essential) + { + String resref = getAnimationResref(); + ArrayList retVal = new ArrayList() {{ + add(resref + "G1.BAM"); + add(resref + "G1E.BAM"); + add(resref + "G2.BAM"); + add(resref + "G2E.BAM"); + add(resref + "G3.BAM"); + add(resref + "G3E.BAM"); + }}; + return retVal; + } + + @Override + public boolean isSequenceAvailable(Sequence seq) + { + return (getSequenceDefinition(seq) != null); + } + + @Override + protected void init() throws Exception + { + // setting properties + initDefaults(getAnimationInfo()); + IniMapSection section = getSpecificIniSection(); + setFalseColor(section.getAsInteger(KEY_FALSE_COLOR.getName(), 0) != 0); + setDetectedByInfravision(section.getAsInteger(KEY_DETECTED_BY_INFRAVISION.getName(), 0) != 0); + } + + @Override + protected SeqDef getSequenceDefinition(Sequence seq) + { + SeqDef retVal = null; + String resref = getAnimationResref(); + Couple data = suffixMap.get(seq); + if (data != null) { + SegmentDef.Behavior behavior = SegmentDef.getBehaviorOf(data.getValue0()); + String suffix = SegmentDef.fixBehaviorSuffix(data.getValue0()); + ResourceEntry entry = ResourceFactory.getResourceEntry(resref + suffix + ".BAM"); + int cycle = data.getValue1().intValue(); + ResourceEntry entryE = ResourceFactory.getResourceEntry(resref + suffix + "E.BAM"); + int cycleE = cycle + SeqDef.DIR_REDUCED_W.length; + if (SpriteUtils.bamCyclesExist(entry, cycle, SeqDef.DIR_REDUCED_W.length) && + SpriteUtils.bamCyclesExist(entryE, cycleE, SeqDef.DIR_REDUCED_E.length)) { + retVal = SeqDef.createSequence(seq, SeqDef.DIR_REDUCED_W, false, entry, cycle, null, behavior); + SeqDef tmp = SeqDef.createSequence(seq, SeqDef.DIR_REDUCED_E, false, entryE, cycleE, null, behavior); + retVal.addDirections(tmp.getDirections().toArray(new DirDef[tmp.getDirections().size()])); + } + } + return retVal; + } +} diff --git a/src/org/infinity/resource/cre/decoder/MonsterLayeredDecoder.java b/src/org/infinity/resource/cre/decoder/MonsterLayeredDecoder.java new file mode 100644 index 000000000..421d668fd --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/MonsterLayeredDecoder.java @@ -0,0 +1,216 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.decoder; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import org.infinity.resource.ResourceFactory; +import org.infinity.resource.cre.CreResource; +import org.infinity.resource.cre.decoder.tables.SpriteTables; +import org.infinity.resource.cre.decoder.util.AnimationInfo; +import org.infinity.resource.cre.decoder.util.DecoderAttribute; +import org.infinity.resource.cre.decoder.util.DirDef; +import org.infinity.resource.cre.decoder.util.ItemInfo; +import org.infinity.resource.cre.decoder.util.SegmentDef; +import org.infinity.resource.cre.decoder.util.SeqDef; +import org.infinity.resource.cre.decoder.util.Sequence; +import org.infinity.resource.cre.decoder.util.SpriteUtils; +import org.infinity.resource.key.ResourceEntry; +import org.infinity.util.IniMap; +import org.infinity.util.IniMapSection; +import org.infinity.util.tuples.Couple; + +/** + * Creature animation decoder for processing type 8000 (monster_layered) animations. + * Available ranges: [8000,8fff] + */ +public class MonsterLayeredDecoder extends SpriteDecoder +{ + /** The animation type associated with this class definition. */ + public static final AnimationInfo.Type ANIMATION_TYPE = AnimationInfo.Type.MONSTER_LAYERED; + + public static final DecoderAttribute KEY_RESREF_WEAPON1 = DecoderAttribute.with("resref_weapon1", DecoderAttribute.DataType.STRING); + public static final DecoderAttribute KEY_RESREF_WEAPON2 = DecoderAttribute.with("resref_weapon2", DecoderAttribute.DataType.STRING); + + private static final HashMap> suffixMap = new HashMap>() {{ + put(Sequence.WALK, Couple.with("G1", 0)); + put(Sequence.STANCE, Couple.with("G1", 8)); + put(Sequence.STAND, Couple.with("G1", 16)); + put(Sequence.GET_HIT, Couple.with("G1", 24)); + put(Sequence.DIE, Couple.with("G1", 32)); + put(Sequence.SLEEP, get(Sequence.DIE)); + put(Sequence.GET_UP, Couple.with("!G1", 32)); + put(Sequence.TWITCH, Couple.with("G1", 40)); + put(Sequence.ATTACK, Couple.with("G2", 0)); + put(Sequence.ATTACK_2, Couple.with("G2", 8)); + put(Sequence.ATTACK_2H, Couple.with("G2", 16)); + }}; + + /** + * A helper method that parses the specified data array and generates a {@link IniMap} instance out of it. + * @param data a String array containing table values for a specific table entry. + * @return a {@code IniMap} instance with the value derived from the specified data array. + * Returns {@code null} if no data could be derived. + */ + public static IniMap processTableData(String[] data) + { + IniMap retVal = null; + if (data == null || data.length < 16) { + return retVal; + } + + String resref = SpriteTables.valueToString(data, SpriteTables.COLUMN_RESREF, ""); + if (resref.isEmpty()) { + return retVal; + } + String resrefWeapon1 = SpriteTables.valueToString(data, SpriteTables.COLUMN_HEIGHT, ""); + String resrefWeapon2 = SpriteTables.valueToString(data, SpriteTables.COLUMN_HEIGHT_SHIELD, ""); + + List lines = SpriteUtils.processTableDataGeneral(data, ANIMATION_TYPE); + lines.add("[monster_layered]"); + lines.add("resref=" + resref); + lines.add("resref_weapon1=" + resrefWeapon1); + lines.add("resref_weapon2=" + resrefWeapon2); + + retVal = IniMap.from(lines); + + return retVal; + } + + public MonsterLayeredDecoder(int animationId, IniMap ini) throws Exception + { + super(ANIMATION_TYPE, animationId, ini); + } + + public MonsterLayeredDecoder(CreResource cre) throws Exception + { + super(ANIMATION_TYPE, cre); + } + + /** Returns the two-letter weapon animation prefix for 1-handed weapons. */ + public String getWeapon1Overlay() { return getAttribute(KEY_RESREF_WEAPON1); } + protected void setWeapon1Overlay(String s) { setAttribute(KEY_RESREF_WEAPON1, s); } + + /** Returns the two-letter weapon animation prefix for 2-handed weapons. */ + public String getWeapon2Overlay() { return getAttribute(KEY_RESREF_WEAPON2); } + protected void setWeapon2Overlay(String s) { setAttribute(KEY_RESREF_WEAPON2, s); } + + @Override + public List getAnimationFiles(boolean essential) + { + String resref = getAnimationResref(); + final String w1 = !getWeapon1Overlay().isEmpty() ? getWeapon1Overlay().substring(0, 1) : ""; + final String w2 = !getWeapon2Overlay().isEmpty() ? getWeapon2Overlay().substring(0, 1) : ""; + final String[] suffix = { "G1", "G1E", "G2", "G2E" }; + ArrayList retVal = new ArrayList() {{ + for (final String s : suffix) { + add(resref + s + ".BAM"); + } + if (!w1.isEmpty()) { + for (final String s : suffix) { + add(resref + w1 + s + ".BAM"); + } + } + if (!w2.isEmpty()) { + for (final String s : suffix) { + add(resref + w2 + s + ".BAM"); + } + } + }}; + return retVal; + } + + @Override + public boolean isSequenceAvailable(Sequence seq) + { + return (getSequenceDefinition(seq) != null); + } + + @Override + protected void init() throws Exception + { + // setting properties + initDefaults(getAnimationInfo()); + IniMapSection section = getSpecificIniSection(); + setFalseColor(true); + setDetectedByInfravision(true); + setWeapon1Overlay(section.getAsString(KEY_RESREF_WEAPON1.getName(), "")); + setWeapon2Overlay(section.getAsString(KEY_RESREF_WEAPON2.getName(), "")); + } + + @Override + protected SeqDef getSequenceDefinition(Sequence seq) + { + SeqDef retVal = null; + + Couple data = suffixMap.get(seq); + if (data == null) { + return retVal; + } + + switch (seq) { + case ATTACK: + case ATTACK_2: + if (ItemInfo.testAll(getCreatureInfo().getEquippedWeapon(), ItemInfo.FILTER_WEAPON_2H)) { + return retVal; + } + break; + case ATTACK_2H: + if (!ItemInfo.testAll(getCreatureInfo().getEquippedWeapon(), ItemInfo.FILTER_WEAPON_2H)) { + return retVal; + } + break; + default: + } + + ArrayList> creResList = new ArrayList<>(); + + // defining creature resref prefix + String resref = getAnimationResref(); + SegmentDef.Behavior behavior = SegmentDef.getBehaviorOf(data.getValue0()); + String suffix = SegmentDef.fixBehaviorSuffix(data.getValue0()); + creResList.add(Couple.with(resref + suffix, SegmentDef.SpriteType.AVATAR)); + + // defining weapon overlay for current creature + ItemInfo itmWeapon = getCreatureInfo().getEquippedWeapon(); + if (itmWeapon != null) { + String weapon = itmWeapon.getAppearance().trim(); + if (!weapon.isEmpty()) { + weapon = weapon.substring(0, 1); + } + if (!getWeapon1Overlay().startsWith(weapon) && !getWeapon2Overlay().startsWith(weapon)) { + weapon = ""; + } + + if (!weapon.isEmpty()) { + creResList.add(Couple.with(resref + weapon + suffix, SegmentDef.SpriteType.WEAPON)); + } + } + + int cycle = data.getValue1().intValue(); + int cycleE = cycle + SeqDef.DIR_REDUCED_W.length; + + retVal = new SeqDef(seq); + for (Couple resEntry : creResList) { + ResourceEntry entry = ResourceFactory.getResourceEntry(resEntry.getValue0() + ".BAM"); + ResourceEntry entryE = ResourceFactory.getResourceEntry(resEntry.getValue0() + "E.BAM"); + if (SpriteUtils.bamCyclesExist(entry, cycle, SeqDef.DIR_REDUCED_W.length) && + SpriteUtils.bamCyclesExist(entryE, cycleE, SeqDef.DIR_REDUCED_E.length)) { + SeqDef tmp = SeqDef.createSequence(seq, SeqDef.DIR_REDUCED_W, false, entry, cycle, resEntry.getValue1(), behavior); + retVal.addDirections(tmp.getDirections().toArray(new DirDef[tmp.getDirections().size()])); + tmp = SeqDef.createSequence(seq, SeqDef.DIR_REDUCED_E, false, entryE, cycleE, resEntry.getValue1(), behavior); + retVal.addDirections(tmp.getDirections().toArray(new DirDef[tmp.getDirections().size()])); + } + } + + if (retVal.isEmpty()) { + retVal = null; + } + + return retVal; + } +} diff --git a/src/org/infinity/resource/cre/decoder/MonsterLayeredSpellDecoder.java b/src/org/infinity/resource/cre/decoder/MonsterLayeredSpellDecoder.java new file mode 100644 index 000000000..38dd83d0a --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/MonsterLayeredSpellDecoder.java @@ -0,0 +1,248 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.decoder; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import org.infinity.resource.ResourceFactory; +import org.infinity.resource.cre.CreResource; +import org.infinity.resource.cre.decoder.tables.SpriteTables; +import org.infinity.resource.cre.decoder.util.AnimationInfo; +import org.infinity.resource.cre.decoder.util.DecoderAttribute; +import org.infinity.resource.cre.decoder.util.DirDef; +import org.infinity.resource.cre.decoder.util.ItemInfo; +import org.infinity.resource.cre.decoder.util.SegmentDef; +import org.infinity.resource.cre.decoder.util.SeqDef; +import org.infinity.resource.cre.decoder.util.Sequence; +import org.infinity.resource.cre.decoder.util.SpriteUtils; +import org.infinity.resource.key.ResourceEntry; +import org.infinity.util.IniMap; +import org.infinity.util.IniMapSection; +import org.infinity.util.tuples.Couple; + +/** + * Creature animation decoder for processing type 2000 (monster_layered_spell) animations. + * Note: This type can be incorrectly labeled as "monster_layered" in INI files + * Available ranges: [2000,2fff] + */ +public class MonsterLayeredSpellDecoder extends SpriteDecoder +{ + /** The animation type associated with this class definition. */ + public static final AnimationInfo.Type ANIMATION_TYPE = AnimationInfo.Type.MONSTER_LAYERED_SPELL; + + public static final DecoderAttribute KEY_DUAL_ATTACK = DecoderAttribute.with("dual_attack", DecoderAttribute.DataType.BOOLEAN); + public static final DecoderAttribute KEY_INVULNERABLE = DecoderAttribute.with("invulnerable", DecoderAttribute.DataType.BOOLEAN); + public static final DecoderAttribute KEY_RESREF_WEAPON1 = DecoderAttribute.with("resref_weapon1", DecoderAttribute.DataType.STRING); + public static final DecoderAttribute KEY_RESREF_WEAPON2 = DecoderAttribute.with("resref_weapon2", DecoderAttribute.DataType.STRING); + + private static final HashMap> suffixMap = new HashMap>() {{ + put(Sequence.WALK, Couple.with("G1", 0)); + put(Sequence.STANCE, Couple.with("G1", 8)); + put(Sequence.STAND, Couple.with("G1", 16)); + put(Sequence.GET_HIT, Couple.with("G1", 24)); + put(Sequence.DIE, Couple.with("G1", 32)); + put(Sequence.SLEEP, get(Sequence.DIE)); + put(Sequence.GET_UP, Couple.with("!G1", 32)); + put(Sequence.TWITCH, Couple.with("G1", 40)); + put(Sequence.ATTACK, Couple.with("G2", 0)); + put(Sequence.SPELL, Couple.with("G2", 8)); + put(Sequence.ATTACK_2H, Couple.with("G2", 16)); + }}; + + /** + * A helper method that parses the specified data array and generates a {@link IniMap} instance out of it. + * @param data a String array containing table values for a specific table entry. + * @return a {@code IniMap} instance with the value derived from the specified data array. + * Returns {@code null} if no data could be derived. + */ + public static IniMap processTableData(String[] data) + { + IniMap retVal = null; + if (data == null || data.length < 16) { + return retVal; + } + + String resref = SpriteTables.valueToString(data, SpriteTables.COLUMN_RESREF, ""); + if (resref.isEmpty()) { + return retVal; + } + int falseColor = SpriteTables.valueToInt(data, SpriteTables.COLUMN_CLOWN, 0); + int dualAttack = SpriteTables.valueToInt(data, SpriteTables.COLUMN_WEAPON, 0); + String resrefWeapon1 = SpriteTables.valueToString(data, SpriteTables.COLUMN_HEIGHT, ""); + String resrefWeapon2 = SpriteTables.valueToString(data, SpriteTables.COLUMN_HEIGHT_SHIELD, ""); + + List lines = SpriteUtils.processTableDataGeneral(data, ANIMATION_TYPE); + lines.add("[monster_layered_spell]"); + lines.add("dual_attack=" + dualAttack); + lines.add("false_color=" + falseColor); + lines.add("resref=" + resref); + lines.add("resref_weapon1=" + resrefWeapon1); + lines.add("resref_weapon2=" + resrefWeapon2); + + retVal = IniMap.from(lines); + + return retVal; + } + + public MonsterLayeredSpellDecoder(int animationId, IniMap ini) throws Exception + { + super(ANIMATION_TYPE, animationId, ini); + } + + public MonsterLayeredSpellDecoder(CreResource cre) + throws Exception + { + super(ANIMATION_TYPE, cre); + } + + /** ??? */ + public boolean isDualAttack() { return getAttribute(KEY_DUAL_ATTACK); } + protected void setDualAttack(boolean b) { setAttribute(KEY_DUAL_ATTACK, b); } + + /** Returns the two-letter weapon animation prefix for 1-handed weapons. */ + public String getWeapon1Overlay() { return getAttribute(KEY_RESREF_WEAPON1); } + protected void setWeapon1Overlay(String s) { setAttribute(KEY_RESREF_WEAPON1, s); } + + /** Returns the two-letter weapon animation prefix for 2-handed weapons. */ + public String getWeapon2Overlay() { return getAttribute(KEY_RESREF_WEAPON2); } + protected void setWeapon2Overlay(String s) { setAttribute(KEY_RESREF_WEAPON2, s); } + + @Override + public List getAnimationFiles(boolean essential) + { + String resref = getAnimationResref(); + final String w1 = !getWeapon1Overlay().isEmpty() ? getWeapon1Overlay().substring(0, 1) : ""; + final String w2 = !getWeapon2Overlay().isEmpty() ? getWeapon2Overlay().substring(0, 1) : ""; + final String[] suffix = { "G1", "G1E", "G2", "G2E" }; + ArrayList retVal = new ArrayList() {{ + for (final String s : suffix) { + add(resref + s + ".BAM"); + } + if (!w1.isEmpty()) { + for (final String s : suffix) { + add(resref + w1 + s + ".BAM"); + } + } + if (!w2.isEmpty()) { + for (final String s : suffix) { + add(resref + w2 + s + ".BAM"); + } + } + }}; + return retVal; + } + + @Override + public boolean isSequenceAvailable(Sequence seq) + { + return (getSequenceDefinition(seq) != null); + } + + @Override + protected void init() throws Exception + { + // setting properties + initDefaults(getAnimationInfo()); + + // Hardcoded defaults + int defFalseColor = 0; + int defDualAttack = 0; + String defWeapon1 = ""; + String defWeapon2 = ""; +// switch (getAnimationId() & 0xff00) { +// case 0x2000: // Sirine +// defFalseColor = 1; +// defWeapon2 = "BW"; +// break; +// case 0x2100: // Volo +// defWeapon1 = "MS"; +// break; +// case 0x2200: // Ogre Mage +// defFalseColor = 1; +// defDualAttack = 1; +// defWeapon1 = "S1"; +// break; +// case 0x2300: // Death Knight +// defDualAttack = 1; +// break; +// } + + IniMapSection section = getSpecificIniSection(); + if (section.getEntryCount() == 0) { + // EE: defined as "monster_layered" type + section = getAnimationInfo().getSection(AnimationInfo.Type.MONSTER_LAYERED.getSectionName()); + } + + setDetectedByInfravision(true); + setFalseColor(section.getAsInteger(KEY_FALSE_COLOR.getName(), defFalseColor) != 0); + setDualAttack(section.getAsInteger(KEY_DUAL_ATTACK.getName(), defDualAttack) != 0); + setWeapon1Overlay(section.getAsString(KEY_RESREF_WEAPON1.getName(), defWeapon1)); + setWeapon2Overlay(section.getAsString(KEY_RESREF_WEAPON2.getName(), defWeapon2)); + } + + @Override + protected SeqDef getSequenceDefinition(Sequence seq) + { + SeqDef retVal = null; + + Couple data = suffixMap.get(seq); + if (data == null) { + return retVal; + } + + ItemInfo itmWeapon = getCreatureInfo().getEquippedWeapon(); + if (seq == Sequence.ATTACK_2H && ItemInfo.testAll(itmWeapon, ItemInfo.FILTER_WEAPON_2H)) { + return retVal; + } + + ArrayList> creResList = new ArrayList<>(); + + // defining creature resref prefix + String resref = getAnimationResref(); + SegmentDef.Behavior behavior = SegmentDef.getBehaviorOf(data.getValue0()); + String suffix = SegmentDef.fixBehaviorSuffix(data.getValue0()); + creResList.add(Couple.with(resref + suffix, SegmentDef.SpriteType.AVATAR)); + + // defining weapon overlay for current creature + if (itmWeapon != null) { + String weapon = itmWeapon.getAppearance().trim(); + if (!weapon.isEmpty()) { + weapon = weapon.substring(0, 1); + } + if (!getWeapon1Overlay().startsWith(weapon) && !getWeapon2Overlay().startsWith(weapon)) { + weapon = ""; + } + + if (!weapon.isEmpty()) { + creResList.add(Couple.with(resref + weapon + suffix, SegmentDef.SpriteType.WEAPON)); + } + } + + int cycle = data.getValue1().intValue(); + int cycleE = cycle + SeqDef.DIR_REDUCED_W.length; + + retVal = new SeqDef(seq); + for (Couple resEntry : creResList) { + ResourceEntry entry = ResourceFactory.getResourceEntry(resEntry.getValue0() + ".BAM"); + ResourceEntry entryE = ResourceFactory.getResourceEntry(resEntry.getValue0() + "E.BAM"); + if (SpriteUtils.bamCyclesExist(entry, cycle, SeqDef.DIR_REDUCED_W.length) && + SpriteUtils.bamCyclesExist(entryE, cycleE, SeqDef.DIR_REDUCED_E.length)) { + SeqDef tmp = SeqDef.createSequence(seq, SeqDef.DIR_REDUCED_W, false, entry, cycle, resEntry.getValue1(), behavior); + retVal.addDirections(tmp.getDirections().toArray(new DirDef[tmp.getDirections().size()])); + tmp = SeqDef.createSequence(seq, SeqDef.DIR_REDUCED_E, false, entryE, cycleE, resEntry.getValue1(), behavior); + retVal.addDirections(tmp.getDirections().toArray(new DirDef[tmp.getDirections().size()])); + } + } + + if (retVal.isEmpty()) { + retVal = null; + } + + return retVal; + } + +} diff --git a/src/org/infinity/resource/cre/decoder/MonsterMultiDecoder.java b/src/org/infinity/resource/cre/decoder/MonsterMultiDecoder.java new file mode 100644 index 000000000..c5ebfd597 --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/MonsterMultiDecoder.java @@ -0,0 +1,327 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.decoder; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; + +import org.infinity.resource.ResourceFactory; +import org.infinity.resource.cre.CreResource; +import org.infinity.resource.cre.decoder.tables.SpriteTables; +import org.infinity.resource.cre.decoder.util.AnimationInfo; +import org.infinity.resource.cre.decoder.util.DecoderAttribute; +import org.infinity.resource.cre.decoder.util.DirDef; +import org.infinity.resource.cre.decoder.util.Direction; +import org.infinity.resource.cre.decoder.util.SegmentDef; +import org.infinity.resource.cre.decoder.util.SeqDef; +import org.infinity.resource.cre.decoder.util.Sequence; +import org.infinity.resource.cre.decoder.util.SpriteUtils; +import org.infinity.resource.key.ResourceEntry; +import org.infinity.util.IniMap; +import org.infinity.util.IniMapSection; +import org.infinity.util.Misc; +import org.infinity.util.tuples.Couple; + +/** + * Creature animation decoder for processing type 1000 (multi_monster) animations consisting of 9 quadrants. + * Note: This type can be incorrectly labeled as "multi_new" in INI files. + * Available ranges: [1200,12ff], [1400,1fff] + */ +public class MonsterMultiDecoder extends QuadrantsBaseDecoder +{ + /** The animation type associated with this class definition. */ + public static final AnimationInfo.Type ANIMATION_TYPE = AnimationInfo.Type.MONSTER_MULTI; + + public static final DecoderAttribute KEY_SPLIT_BAMS = DecoderAttribute.with("split_bams", DecoderAttribute.DataType.BOOLEAN); + public static final DecoderAttribute KEY_DOUBLE_BLIT = DecoderAttribute.with("double_blit", DecoderAttribute.DataType.BOOLEAN); + public static final DecoderAttribute KEY_PALETTE_1 = DecoderAttribute.with("palette1", DecoderAttribute.DataType.STRING); + public static final DecoderAttribute KEY_PALETTE_2 = DecoderAttribute.with("palette2", DecoderAttribute.DataType.STRING); + public static final DecoderAttribute KEY_PALETTE_3 = DecoderAttribute.with("palette3", DecoderAttribute.DataType.STRING); + public static final DecoderAttribute KEY_PALETTE_4 = DecoderAttribute.with("palette4", DecoderAttribute.DataType.STRING); + public static final DecoderAttribute KEY_PALETTE_5 = DecoderAttribute.with("palette5", DecoderAttribute.DataType.STRING); + + private static final HashMap> suffixMapUnsplit = new HashMap>() {{ + // Note: Replace 'underscore' in suffix by one-based quadrant index + // Note 2: 'exclamation mark' in suffix indicates reversed playback of frames (e.g. get up = reversed sleep); remove + put(Sequence.WALK, Couple.with("G1_", 0)); + put(Sequence.STANCE, Couple.with("G2_", 0)); + put(Sequence.SPELL, get(Sequence.STANCE)); + put(Sequence.STAND, Couple.with("G2_", 9)); // engine appears to use STANCE sequence instead +// put(Sequence.STAND, get(Sequence.STANCE)); + put(Sequence.ATTACK, Couple.with("G3_", 0)); +// put(Sequence.ATTACK_2, get(Sequence.ATTACK)); // apparently unused by the engine +// put(Sequence.ATTACK_3, get(Sequence.ATTACK)); // apparently unused by the engine + put(Sequence.GET_HIT, Couple.with("G4_", 0)); + put(Sequence.DIE, Couple.with("G4_", 9)); + put(Sequence.SLEEP, get(Sequence.DIE)); + put(Sequence.GET_UP, Couple.with("!G4_", 9)); + put(Sequence.TWITCH, Couple.with("G4_", 18)); + put(Sequence.CAST, Couple.with("G5_", 9)); + put(Sequence.SHOOT, get(Sequence.CAST)); + }}; + + private static final HashMap> suffixMapSplit = new HashMap>() {{ + // Note: Replace 'underscore' in suffix by one-based quadrant index + // Note 2: Replace 'plus' in suffix by zero-based direction index (0=south, 4=west, ...) + // Note 3: 'exclamation mark' in suffix indicates reversed playback of frames (e.g. get up = reversed sleep); remove + put(Sequence.WALK, Couple.with("1_0+", 0)); + put(Sequence.STANCE, Couple.with("2_0+", 0)); + put(Sequence.SPELL, get(Sequence.STANCE)); + put(Sequence.STAND, Couple.with("2_0+", 9)); // engine appears to use STANCE sequence instead +// put(Sequence.STAND, get(Sequence.STANCE)); + put(Sequence.ATTACK, Couple.with("3_0+", 0)); +// put(Sequence.ATTACK_2, get(Sequence.ATTACK)); // apparently unused by the engine +// put(Sequence.ATTACK_3, get(Sequence.ATTACK)); // apparently unused by the engine + put(Sequence.GET_HIT, Couple.with("4_0+", 0)); + put(Sequence.DIE, Couple.with("4_1+", 9)); + put(Sequence.SLEEP, get(Sequence.DIE)); + put(Sequence.GET_UP, Couple.with("!4_1+", 9)); + put(Sequence.TWITCH, Couple.with("4_2+", 18)); + put(Sequence.CAST, Couple.with("5_1+", 9)); + put(Sequence.SHOOT, get(Sequence.CAST)); + }}; + + /** + * A helper method that parses the specified data array and generates a {@link IniMap} instance out of it. + * @param data a String array containing table values for a specific table entry. + * @return a {@code IniMap} instance with the value derived from the specified data array. + * Returns {@code null} if no data could be derived. + */ + public static IniMap processTableData(String[] data) + { + IniMap retVal = null; + if (data == null || data.length < 16) { + return retVal; + } + + String resref = SpriteTables.valueToString(data, SpriteTables.COLUMN_RESREF, ""); + if (resref.isEmpty()) { + return retVal; + } + int splitBams = SpriteTables.valueToInt(data, SpriteTables.COLUMN_SPLIT, 0); + String paletteBase = SpriteTables.valueToString(data, SpriteTables.COLUMN_PALETTE, ""); + + List lines = SpriteUtils.processTableDataGeneral(data, ANIMATION_TYPE); + lines.add("[monster_multi]"); + lines.add("quadrants=9"); + lines.add("split_bams=" + splitBams); + lines.add("resref=" + resref); + if (!paletteBase.isEmpty()) { + lines.add("palette1=" + paletteBase + "1"); + lines.add("palette2=" + paletteBase + "2"); + lines.add("palette3=" + paletteBase + "3"); + lines.add("palette4=" + paletteBase + "4"); + lines.add("palette5=" + paletteBase + "5"); + } + + retVal = IniMap.from(lines); + + return retVal; + } + + public MonsterMultiDecoder(int animationId, IniMap ini) throws Exception + { + super(ANIMATION_TYPE, animationId, ini); + } + + public MonsterMultiDecoder(CreResource cre) throws Exception + { + super(ANIMATION_TYPE, cre); + } + + /** Returns the correct sequence map for the current settings. */ + private HashMap> getSuffixMap() + { + return isSplittedBams() ? suffixMapSplit : suffixMapUnsplit; + } + + /** Returns whether animations are spread over various subfiles. */ + public boolean isSplittedBams() { return getAttribute(KEY_SPLIT_BAMS); } + protected void setSplittedBams(boolean b) { setAttribute(KEY_SPLIT_BAMS, b); } + + /** unused */ + public boolean isDoubleBlit() { return getAttribute(KEY_DOUBLE_BLIT); } + protected void setDoubleBlit(boolean b) { setAttribute(KEY_DOUBLE_BLIT, b); } + + /** Returns the palette resref for the specified BAM prefix. Falls back to "new_palette" if needed. */ + public String getPalette(int idx) + { + String retVal = null; + switch (idx) { + case 1: + retVal = getAttribute(KEY_PALETTE_1); + break; + case 2: + retVal = getAttribute(KEY_PALETTE_2); + break; + case 3: + retVal = getAttribute(KEY_PALETTE_3); + break; + case 4: + retVal = getAttribute(KEY_PALETTE_4); + break; + case 5: + retVal = getAttribute(KEY_PALETTE_5); + break; + default: + return getNewPalette(); + } + + if (retVal == null || retVal.isEmpty()) { + String s = getNewPalette(); + if (!s.isEmpty()) { + retVal = s + idx; + } + } + + return retVal; + } + + protected void setPalette(int idx, String s) + { + switch (idx) { + case 1: + setAttribute(KEY_PALETTE_1, s); + break; + case 2: + setAttribute(KEY_PALETTE_2, s); + break; + case 3: + setAttribute(KEY_PALETTE_3, s); + break; + case 4: + setAttribute(KEY_PALETTE_4, s); + break; + case 5: + setAttribute(KEY_PALETTE_5, s); + break; + } + } + + @Override + public List getAnimationFiles(boolean essential) + { + String resref = getAnimationResref(); + final HashSet fileSet = new HashSet<>(); + for (final HashMap.Entry> entry : getSuffixMap().entrySet()) { + for (int i = 0; i < getQuadrants(); i++) { + for (int j = 0; j < SeqDef.DIR_FULL_W.length; j++) { + String suffix = entry.getValue().getValue0() + .replace("_", Integer.toString(i+1)) + .replace("+", Integer.toString(j)); + suffix = SegmentDef.fixBehaviorSuffix(suffix); + fileSet.add(resref + suffix + ".BAM"); + } + } + } + ArrayList retVal = new ArrayList() {{ + for (final String s : fileSet) { + add(s); + } + }}; + return retVal; + } + + @Override + protected void init() throws Exception + { + super.init(); + IniMapSection section = getSpecificIniSection(); + if (section.getEntryCount() == 0) { + // EE: defined as "multi_new" type + section = getAnimationInfo().getSection(AnimationInfo.Type.MONSTER_MULTI_NEW.getSectionName()); + } + setSplittedBams(section.getAsInteger(KEY_SPLIT_BAMS.getName(), 0) != 0); + setQuadrants(section.getAsInteger(KEY_QUADRANTS.getName(), 9)); + setPalette(1, section.getAsString(KEY_PALETTE_1.getName())); + setPalette(2, section.getAsString(KEY_PALETTE_2.getName())); + setPalette(3, section.getAsString(KEY_PALETTE_3.getName())); + setPalette(4, section.getAsString(KEY_PALETTE_4.getName())); + setPalette(5, section.getAsString(KEY_PALETTE_5.getName())); + Misc.requireCondition(getQuadrants() < 10, "Too many quadrants defined: " + getQuadrants(), IllegalArgumentException.class); + } + + @Override + public boolean isSequenceAvailable(Sequence seq) + { + return (getSequenceDefinition(seq) != null); + } + + @Override + protected int[] getNewPaletteData(ResourceEntry bamRes) + { + int[] retVal = null; + + String bamResref = bamRes.getResourceRef(); + String resref = getAnimationResref(); + + int idx = Misc.toNumber(Character.toString(bamResref.charAt(resref.length())), -1); + retVal = SpriteUtils.loadReplacementPalette(getPalette(idx)); + + return retVal; + } + + @Override + protected SeqDef getSequenceDefinition(Sequence seq) + { + SeqDef retVal = null; + String resref = getAnimationResref(); + + List cycleList = new ArrayList<>(); + if (getSuffixMap().containsKey(seq)) { + String suffixBase = getSuffixMap().get(seq).getValue0(); + SegmentDef.Behavior behavior = SegmentDef.getBehaviorOf(suffixBase); + suffixBase = SegmentDef.fixBehaviorSuffix(suffixBase); + int cycleBase = getSuffixMap().get(seq).getValue1().intValue(); + retVal = new SeqDef(seq); + + // defining western directions + for (final Direction dir : SeqDef.DIR_FULL_W) { + cycleList.clear(); + for (int seg = 0; seg < getQuadrants(); seg++) { + String suffix = suffixBase.replace("_", Integer.toString(seg+1)).replace("+", Integer.toString(dir.getValue())); + ResourceEntry entry = ResourceFactory.getResourceEntry(resref + suffix + ".BAM"); + int cycleIdx = cycleBase + dir.getValue(); + if (!SpriteUtils.bamCyclesExist(entry, cycleIdx, 1)) { + return null; + } + cycleList.add(new SegmentDef(entry, cycleIdx, SegmentDef.SpriteType.AVATAR, behavior)); + } + SeqDef tmp = SeqDef.createSequence(seq, new Direction[] {dir}, false, cycleList); + retVal.addDirections(tmp.getDirections().toArray(new DirDef[tmp.getDirections().size()])); + } + + // calculating eastern directions + for (final Direction dir : SeqDef.DIR_FULL_E) { + cycleList.clear(); + int dir2 = SeqDef.DIR_FULL_W.length - (dir.getValue() - Direction.N.getValue() + 1); // direction to mirror + for (int seg = 0; seg < getQuadrants(); seg++) { + String suffix = suffixBase.replace("_", Integer.toString(seg+1)).replace("+", Integer.toString(dir2)); + ResourceEntry entry = ResourceFactory.getResourceEntry(resref + suffix + ".BAM"); + int cycleIdx = cycleBase + dir2; + cycleList.add(new SegmentDef(entry, cycleIdx, SegmentDef.SpriteType.AVATAR, behavior)); + } + SeqDef tmp = SeqDef.createSequence(seq, new Direction[] {dir}, true, cycleList); + retVal.addDirections(tmp.getDirections().toArray(new DirDef[tmp.getDirections().size()])); + } + +// Structure: +// xxxx1[1-9]0[0-8]: [0-8] +// xxxx1100: 0, xxxx1200: 0, xxxx1300: 0, xxxx1400: 0, xxxx1500: 0, xxxx1600: 0, xxxx1700: 0, xxxx1800: 0, xxxx1900: 0 +// xxxx1101: 1, xxxx1201: 1, xxxx1301: 1, xxxx1401: 1, xxxx1501: 1, xxxx1601: 1, xxxx1701: 1, xxxx1801: 1, xxxx1901: 1 +// xxxx1102: 2, xxxx1202: 2, xxxx1302: 2, xxxx1402: 2, xxxx1502: 2, xxxx1602: 2, xxxx1702: 2, xxxx1802: 2, xxxx1902: 2 +// xxxx1103: 3, xxxx1203: 3, xxxx1303: 3, xxxx1403: 3, xxxx1503: 3, xxxx1603: 3, xxxx1703: 3, xxxx1803: 3, xxxx1903: 3 +// xxxx1104: 4, xxxx1204: 4, xxxx1304: 4, xxxx1404: 4, xxxx1504: 4, xxxx1604: 4, xxxx1704: 4, xxxx1804: 4, xxxx1904: 4 +// xxxx1105: 5, xxxx1205: 5, xxxx1305: 5, xxxx1405: 5, xxxx1505: 5, xxxx1605: 5, xxxx1705: 5, xxxx1805: 5, xxxx1905: 5 +// xxxx1106: 6, xxxx1206: 6, xxxx1306: 6, xxxx1406: 6, xxxx1506: 6, xxxx1606: 6, xxxx1706: 6, xxxx1806: 6, xxxx1906: 6 +// xxxx1107: 7, xxxx1207: 7, xxxx1307: 7, xxxx1407: 7, xxxx1507: 7, xxxx1607: 7, xxxx1707: 7, xxxx1807: 7, xxxx1907: 7 +// xxxx1108: 8, xxxx1208: 8, xxxx1308: 8, xxxx1408: 8, xxxx1508: 8, xxxx1608: 8, xxxx1708: 8, xxxx1808: 8, xxxx1908: 8 + } + + return retVal; + } +} diff --git a/src/org/infinity/resource/cre/decoder/MonsterMultiNewDecoder.java b/src/org/infinity/resource/cre/decoder/MonsterMultiNewDecoder.java new file mode 100644 index 000000000..a13607d59 --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/MonsterMultiNewDecoder.java @@ -0,0 +1,208 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.decoder; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import org.infinity.resource.ResourceFactory; +import org.infinity.resource.cre.CreResource; +import org.infinity.resource.cre.decoder.tables.SpriteTables; +import org.infinity.resource.cre.decoder.util.AnimationInfo; +import org.infinity.resource.cre.decoder.util.DecoderAttribute; +import org.infinity.resource.cre.decoder.util.DirDef; +import org.infinity.resource.cre.decoder.util.SegmentDef; +import org.infinity.resource.cre.decoder.util.SeqDef; +import org.infinity.resource.cre.decoder.util.Sequence; +import org.infinity.resource.cre.decoder.util.SpriteUtils; +import org.infinity.resource.key.ResourceEntry; +import org.infinity.util.IniMap; +import org.infinity.util.IniMapSection; +import org.infinity.util.Misc; +import org.infinity.util.tuples.Couple; + +/** + * Creature animation decoder for processing type 1000 (multi_new) animations consisting of 4 segments. + * Available ranges: [1300,13ff] + */ +public class MonsterMultiNewDecoder extends QuadrantsBaseDecoder +{ + /** The animation type associated with this class definition. */ + public static final AnimationInfo.Type ANIMATION_TYPE = AnimationInfo.Type.MONSTER_MULTI_NEW; + + public static final DecoderAttribute KEY_CAN_LIE_DOWN = DecoderAttribute.with("can_lie_down", DecoderAttribute.DataType.BOOLEAN); + public static final DecoderAttribute KEY_PATH_SMOOTH = DecoderAttribute.with("path_smooth", DecoderAttribute.DataType.BOOLEAN); + public static final DecoderAttribute KEY_SPLIT_BAMS = DecoderAttribute.with("split_bams", DecoderAttribute.DataType.BOOLEAN); + + private static final HashMap> suffixMapSplit = new HashMap>() {{ + // Note: Replace underscore in suffix by one-based quadrant index + put(Sequence.WALK, Couple.with("G1_1", 0)); + put(Sequence.STANCE, Couple.with("G1_", 9)); + put(Sequence.STAND, Couple.with("G1_2", 18)); + put(Sequence.GET_HIT, Couple.with("G1_3", 27)); + put(Sequence.DIE, Couple.with("G1_4", 36)); + put(Sequence.SLEEP, get(Sequence.DIE)); + put(Sequence.GET_UP, Couple.with("!G1_4", 36)); + put(Sequence.TWITCH, Couple.with("G1_5", 45)); + put(Sequence.ATTACK, Couple.with("G2_", 0)); + put(Sequence.ATTACK_2, Couple.with("G2_1", 9)); + put(Sequence.ATTACK_3, Couple.with("G2_2", 18)); + put(Sequence.ATTACK_4, Couple.with("G2_3", 27)); + put(Sequence.ATTACK_5, Couple.with("G2_4", 36)); + put(Sequence.SPELL, Couple.with("G2_5", 45)); + put(Sequence.CAST, Couple.with("G2_6", 54)); + }}; + private static final HashMap> suffixMapUnsplit = new HashMap>() {{ + // Note: Replace underscore in suffix by one-based quadrant index + put(Sequence.WALK, Couple.with("G1_", 0)); + put(Sequence.STANCE, Couple.with("G1_", 9)); + put(Sequence.STAND, Couple.with("G1_", 18)); + put(Sequence.GET_HIT, Couple.with("G1_", 27)); + put(Sequence.DIE, Couple.with("G1_", 36)); + put(Sequence.SLEEP, get(Sequence.DIE)); + put(Sequence.GET_UP, Couple.with("!G1_", 36)); + put(Sequence.TWITCH, Couple.with("G1_", 45)); + put(Sequence.ATTACK, Couple.with("G2_", 0)); + put(Sequence.ATTACK_2, Couple.with("G2_", 9)); + put(Sequence.ATTACK_3, Couple.with("G2_", 18)); + put(Sequence.ATTACK_4, Couple.with("G2_", 27)); + put(Sequence.ATTACK_5, Couple.with("G2_", 36)); + put(Sequence.SPELL, Couple.with("G2_", 45)); + put(Sequence.CAST, Couple.with("G2_", 54)); + }}; + + /** + * A helper method that parses the specified data array and generates a {@link IniMap} instance out of it. + * @param data a String array containing table values for a specific table entry. + * @return a {@code IniMap} instance with the value derived from the specified data array. + * Returns {@code null} if no data could be derived. + */ + public static IniMap processTableData(String[] data) + { + IniMap retVal = null; + if (data == null || data.length < 16) { + return retVal; + } + + String resref = SpriteTables.valueToString(data, SpriteTables.COLUMN_RESREF, ""); + if (resref.isEmpty()) { + return retVal; + } + int falseColor = SpriteTables.valueToInt(data, SpriteTables.COLUMN_CLOWN, 0); + int splitBams = SpriteTables.valueToInt(data, SpriteTables.COLUMN_SPLIT, 0); + int translucent = SpriteTables.valueToInt(data, SpriteTables.COLUMN_TRANSLUCENT, 0); + + List lines = SpriteUtils.processTableDataGeneral(data, ANIMATION_TYPE); + lines.add("[multi_new]"); + lines.add("false_color=" + falseColor); + lines.add("quadrants=4"); + lines.add("split_bams=" + splitBams); + lines.add("translucent=" + translucent); + lines.add("resref=" + resref); + + retVal = IniMap.from(lines); + + return retVal; + } + + public MonsterMultiNewDecoder(int animationId, IniMap ini) throws Exception + { + super(ANIMATION_TYPE, animationId, ini); + } + + public MonsterMultiNewDecoder(CreResource cre) throws Exception + { + super(ANIMATION_TYPE, cre); + } + + /** Returns the correct sequence map for the current settings. */ + private HashMap> getSuffixMap() + { + return isSplittedBams() ? suffixMapSplit : suffixMapUnsplit; + } + + /** Returns whether the creature falls down when dead/unconscious. */ + public boolean canLieDown() { return getAttribute(KEY_CAN_LIE_DOWN); } + protected void setCanLieDown(boolean b) { setAttribute(KEY_CAN_LIE_DOWN, b); } + + /** ??? */ + public boolean isSmoothPath() { return getAttribute(KEY_PATH_SMOOTH); } + protected void setSmoothPath(boolean b) { setAttribute(KEY_PATH_SMOOTH, b); } + + /** Returns whether animations are spread over various subfiles. */ + public boolean isSplittedBams() { return getAttribute(KEY_SPLIT_BAMS); } + protected void setSplittedBams(boolean b) { setAttribute(KEY_SPLIT_BAMS, b); } + + @Override + public List getAnimationFiles(boolean essential) + { + String resref = getAnimationResref(); + ArrayList retVal = new ArrayList() {{ + for (final HashMap.Entry> entry : getSuffixMap().entrySet()) { + String suffixBase = SegmentDef.fixBehaviorSuffix(entry.getValue().getValue0()); + for (int i = 0; i < getQuadrants(); i++) { + String suffix = suffixBase.replace("_", Integer.toString(i+1)); + add(resref + suffix + ".BAM"); + } + } + }}; + return retVal; + } + + @Override + protected void init() throws Exception + { + super.init(); + IniMapSection section = getSpecificIniSection(); + setCanLieDown(section.getAsInteger(KEY_CAN_LIE_DOWN.getName(), 0) != 0); + setDetectedByInfravision(section.getAsInteger(KEY_DETECTED_BY_INFRAVISION.getName(), 0) != 0); + setFalseColor(section.getAsInteger(KEY_FALSE_COLOR.getName(), 0) != 0); + setSmoothPath(section.getAsInteger(KEY_PATH_SMOOTH.getName(), 0) != 0); + setSplittedBams(section.getAsInteger(KEY_SPLIT_BAMS.getName(), 0) != 0); + setTranslucent(section.getAsInteger(KEY_TRANSLUCENT.getName(), 0) != 0); + setQuadrants(section.getAsInteger(KEY_QUADRANTS.getName(), 4)); + Misc.requireCondition(getQuadrants() > 0 && getQuadrants() < 10, + "Invalid number of quadrants: " + getQuadrants(), IllegalArgumentException.class); + } + + @Override + public boolean isSequenceAvailable(Sequence seq) + { + return (getSequenceDefinition(seq) != null); + } + + @Override + protected SeqDef getSequenceDefinition(Sequence seq) + { + SeqDef retVal = null; + String resref = getAnimationResref(); + + List cycleList = new ArrayList<>(); + List cycleListE = new ArrayList<>(); + boolean valid = true; + if (getSuffixMap().containsKey(seq)) { + String suffixBase = getSuffixMap().get(seq).getValue0(); + SegmentDef.Behavior behavior = SegmentDef.getBehaviorOf(suffixBase); + suffixBase = SegmentDef.fixBehaviorSuffix(suffixBase); + int cycleOfs = getSuffixMap().get(seq).getValue1().intValue(); + for (int i = 0; valid && i < getQuadrants(); i++) { + String suffix = suffixBase.replace("_", Integer.toString(i+1)); + ResourceEntry entry = ResourceFactory.getResourceEntry(resref + suffix + ".BAM"); + cycleList.add(new SegmentDef(entry, cycleOfs, null, behavior)); + cycleListE.add(new SegmentDef(entry, cycleOfs + 1, null, behavior)); + valid &= SpriteUtils.bamCyclesExist(entry, cycleOfs, SeqDef.DIR_FULL_W.length); + } + } + + if (!cycleList.isEmpty() && valid) { + retVal = SeqDef.createSequence(seq, SeqDef.DIR_FULL_W, false, cycleList); + SeqDef tmp = SeqDef.createSequence(seq, SeqDef.DIR_FULL_E, true, cycleListE); + retVal.addDirections(tmp.getDirections().toArray(new DirDef[tmp.getDirections().size()])); + } + + return retVal; + } +} diff --git a/src/org/infinity/resource/cre/decoder/MonsterOldDecoder.java b/src/org/infinity/resource/cre/decoder/MonsterOldDecoder.java new file mode 100644 index 000000000..0a02d3733 --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/MonsterOldDecoder.java @@ -0,0 +1,153 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.decoder; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import org.infinity.resource.ResourceFactory; +import org.infinity.resource.cre.CreResource; +import org.infinity.resource.cre.decoder.tables.SpriteTables; +import org.infinity.resource.cre.decoder.util.AnimationInfo; +import org.infinity.resource.cre.decoder.util.DirDef; +import org.infinity.resource.cre.decoder.util.SegmentDef; +import org.infinity.resource.cre.decoder.util.SeqDef; +import org.infinity.resource.cre.decoder.util.Sequence; +import org.infinity.resource.cre.decoder.util.SpriteUtils; +import org.infinity.resource.key.ResourceEntry; +import org.infinity.util.IniMap; +import org.infinity.util.IniMapSection; +import org.infinity.util.tuples.Couple; + +/** + * Creature animation decoder for processing type 7000 (monster_old) animations. + * Available ranges: (using notation slot/range where slot can be a formula with range definitions as [x,y]) + * (0x7000 | ([0x00,0x1f] << 4))/0x1 + * (0x7000 | ([0x20,0x2f] << 4))/0x3 + * (0x7000 | ([0x40,0x4f] << 4))/0x2 + * (0x7000 | ([0x50,0x5f] << 4))/0x1 + * (0x7000 | ([0x60,0x6f] << 4))/0xf + * (0x7000 | ([0x70,0x7f] << 4))/0x2 + * (0x7000 | ([0x80,0x8f] << 4))/0xf + * (0x7000 | ([0x90,0xaf] << 4))/0x4 + * (0x7000 | ([0xb0,0xbf] << 4))/0x6 + * (0x7000 | ([0xc0,0xcf] << 4))/0x1 + * (0x7000 | ([0xd0,0xdf] << 4))/0xf + * (0x7000 | ([0xe0,0xef] << 4))/0x1 + */ +public class MonsterOldDecoder extends SpriteDecoder +{ + /** The animation type associated with this class definition. */ + public static final AnimationInfo.Type ANIMATION_TYPE = AnimationInfo.Type.MONSTER_OLD; + + private static final HashMap> suffixMap = new HashMap>() {{ + put(Sequence.WALK, Couple.with("G1", 0)); + put(Sequence.STANCE, Couple.with("G1", 8)); + put(Sequence.STAND, Couple.with("G1", 16)); + put(Sequence.GET_HIT, Couple.with("G1", 24)); + put(Sequence.DIE, Couple.with("G1", 32)); + put(Sequence.SLEEP, get(Sequence.DIE)); + put(Sequence.GET_UP, Couple.with("!G1", 32)); + put(Sequence.TWITCH, Couple.with("G1", 40)); + put(Sequence.ATTACK, Couple.with("G2", 0)); + put(Sequence.ATTACK_2, Couple.with("G2", 8)); + put(Sequence.ATTACK_3, Couple.with("G2", 16)); + }}; + + /** + * A helper method that parses the specified data array and generates a {@link IniMap} instance out of it. + * @param data a String array containing table values for a specific table entry. + * @return a {@code IniMap} instance with the value derived from the specified data array. + * Returns {@code null} if no data could be derived. + */ + public static IniMap processTableData(String[] data) + { + IniMap retVal = null; + if (data == null || data.length < 16) { + return retVal; + } + + String resref = SpriteTables.valueToString(data, SpriteTables.COLUMN_RESREF, ""); + if (resref.isEmpty()) { + return retVal; + } + int falseColor = SpriteTables.valueToInt(data, SpriteTables.COLUMN_CLOWN, 0); + int translucent = SpriteTables.valueToInt(data, SpriteTables.COLUMN_TRANSLUCENT, 0); + + List lines = SpriteUtils.processTableDataGeneral(data, ANIMATION_TYPE); + lines.add("[monster_old]"); + lines.add("false_color=" + falseColor); + lines.add("translucent=" + translucent); + lines.add("resref=" + resref); + + retVal = IniMap.from(lines); + + return retVal; + } + + public MonsterOldDecoder(int animationId, IniMap ini) throws Exception + { + super(ANIMATION_TYPE, animationId, ini); + } + + public MonsterOldDecoder(CreResource cre) throws Exception + { + super(ANIMATION_TYPE, cre); + } + + @Override + public List getAnimationFiles(boolean essential) + { + String resref = getAnimationResref(); + ArrayList retVal = new ArrayList() {{ + add(resref + "G1.BAM"); + add(resref + "G1E.BAM"); + add(resref + "G2.BAM"); + add(resref + "G2E.BAM"); + }}; + return retVal; + } + + @Override + public boolean isSequenceAvailable(Sequence seq) + { + return (getSequenceDefinition(seq) != null); + } + + @Override + protected void init() throws Exception + { + // setting properties + initDefaults(getAnimationInfo()); + IniMapSection section = getSpecificIniSection(); + setFalseColor(section.getAsInteger(KEY_FALSE_COLOR.getName(), 0) != 0); + setDetectedByInfravision(section.getAsInteger(KEY_DETECTED_BY_INFRAVISION.getName(), 0) != 0); + setTranslucent(section.getAsInteger(KEY_TRANSLUCENT.getName(), 0) != 0); + } + + @Override + protected SeqDef getSequenceDefinition(Sequence seq) + { + SeqDef retVal = null; + String resref = getAnimationResref(); + Couple data = suffixMap.get(seq); + if (data != null) { + SegmentDef.Behavior behavior = SegmentDef.getBehaviorOf(data.getValue0()); + String suffix = SegmentDef.fixBehaviorSuffix(data.getValue0()); + ResourceEntry entry = ResourceFactory.getResourceEntry(resref + suffix + ".BAM"); + int cycle = data.getValue1().intValue(); + ResourceEntry entryE = ResourceFactory.getResourceEntry(resref + suffix + "E.BAM"); + int cycleE = cycle + SeqDef.DIR_REDUCED_W.length; + if (SpriteUtils.bamCyclesExist(entry, cycle, SeqDef.DIR_REDUCED_W.length) && + SpriteUtils.bamCyclesExist(entryE, cycleE, SeqDef.DIR_REDUCED_E.length)) { + retVal = SeqDef.createSequence(seq, SeqDef.DIR_REDUCED_W, false, entry, cycle, null, behavior); + SeqDef tmp = SeqDef.createSequence(seq, SeqDef.DIR_REDUCED_E, false, entryE, cycleE, null, behavior); + retVal.addDirections(tmp.getDirections().toArray(new DirDef[tmp.getDirections().size()])); + } + } + return retVal; + } +} diff --git a/src/org/infinity/resource/cre/decoder/MonsterPlanescapeDecoder.java b/src/org/infinity/resource/cre/decoder/MonsterPlanescapeDecoder.java new file mode 100644 index 000000000..84659de68 --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/MonsterPlanescapeDecoder.java @@ -0,0 +1,552 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.decoder; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Objects; +import java.util.TreeMap; + +import org.infinity.datatype.IsNumeric; +import org.infinity.resource.Profile; +import org.infinity.resource.ResourceFactory; +import org.infinity.resource.cre.CreResource; +import org.infinity.resource.cre.decoder.tables.SpriteTables; +import org.infinity.resource.cre.decoder.util.AnimationInfo; +import org.infinity.resource.cre.decoder.util.DecoderAttribute; +import org.infinity.resource.cre.decoder.util.DirDef; +import org.infinity.resource.cre.decoder.util.Direction; +import org.infinity.resource.cre.decoder.util.SegmentDef; +import org.infinity.resource.cre.decoder.util.SeqDef; +import org.infinity.resource.cre.decoder.util.Sequence; +import org.infinity.resource.cre.decoder.util.SpriteUtils; +import org.infinity.resource.graphics.BamV1Decoder; +import org.infinity.resource.graphics.BamV1Decoder.BamV1Control; +import org.infinity.resource.graphics.BamV1Decoder.BamV1FrameEntry; +import org.infinity.resource.key.ResourceEntry; +import org.infinity.util.IniMap; +import org.infinity.util.IniMapSection; +import org.infinity.util.Misc; + +/** + * Creature animation decoder for processing type F000 (monster_planescape) animations. + * Available ranges: [f000,ffff] + */ +public class MonsterPlanescapeDecoder extends SpriteDecoder +{ + /** The animation type associated with this class definition. */ + public static final AnimationInfo.Type ANIMATION_TYPE = AnimationInfo.Type.MONSTER_PLANESCAPE; + + public static final DecoderAttribute KEY_BESTIARY = DecoderAttribute.with("bestiary", DecoderAttribute.DataType.INT); + public static final DecoderAttribute KEY_CLOWN = DecoderAttribute.with("clown", DecoderAttribute.DataType.BOOLEAN); + public static final DecoderAttribute KEY_ARMOR = DecoderAttribute.with("armor", DecoderAttribute.DataType.INT); + public static final DecoderAttribute KEY_WALKSCALE = DecoderAttribute.with("walkscale", DecoderAttribute.DataType.DECIMAL); + public static final DecoderAttribute KEY_RUNSCALE = DecoderAttribute.with("runscale", DecoderAttribute.DataType.DECIMAL); + public static final DecoderAttribute KEY_COLOR_LOCATION = DecoderAttribute.with("color", DecoderAttribute.DataType.USERDEFINED, new int[0]); + public static final DecoderAttribute KEY_SHADOW = DecoderAttribute.with("shadow", DecoderAttribute.DataType.STRING); + public static final DecoderAttribute KEY_RESREF2 = DecoderAttribute.with("resref2", DecoderAttribute.DataType.STRING); + + protected final BeforeSourceBam FN_BEFORE_SRC_BAM = new BeforeSourceBam() { + @Override + public void accept(BamV1Control control, SegmentDef sd) + { + String resref = sd.getEntry().getResourceRef(); + + // fix hardcoded "Pillar of Skulls" shadow center: shift by: [193.59] + if (resref.equalsIgnoreCase("POSSHAD")) { + BamV1Decoder decoder = control.getDecoder(); + for (int idx = 0, count = decoder.frameCount(); idx < count; idx++) { + BamV1FrameEntry entry = decoder.getFrameInfo(idx); + entry.resetCenter(); + entry.setCenterX(entry.getCenterX() + 193); + entry.setCenterY(entry.getCenterY() + 59); + } + } + + // "Pillar of Skulls" uses separate shadow animation + if (!resref.equalsIgnoreCase("POSMAIN")) { + SpriteUtils.fixShadowColor(control, isTransparentShadow()); + } + + if (isPaletteReplacementEnabled() && isFalseColor()) { + applyFalseColors(control, sd); + } + + if ((isTranslucencyEnabled() && isTranslucent()) || + (isBlurEnabled() && isBlurred())) { + int minVal = (isBlurEnabled() && isBlurred()) ? 64 : 255; + applyTranslucency(control, minVal); + } + } + }; + + // available animation slot names + private static final HashMap Slots = new HashMap() {{ + put(Sequence.PST_ATTACK1, "attack1"); + put(Sequence.PST_ATTACK2, "attack2"); + put(Sequence.PST_ATTACK3, "attack3"); + put(Sequence.PST_GET_HIT, "gethit"); + put(Sequence.PST_RUN, "run"); + put(Sequence.PST_WALK, "walk"); + put(Sequence.PST_SPELL1, "spell1"); + put(Sequence.PST_SPELL2, "spell2"); + put(Sequence.PST_SPELL3, "spell3"); + put(Sequence.PST_GET_UP, "getup"); + put(Sequence.PST_DIE_FORWARD, "dieforward"); + put(Sequence.PST_DIE_BACKWARD, "diebackward"); + put(Sequence.PST_DIE_COLLAPSE, "diecollapse"); + put(Sequence.PST_TALK1, "talk1"); + put(Sequence.PST_TALK2, "talk2"); + put(Sequence.PST_TALK3, "talk3"); + put(Sequence.PST_STAND_FIDGET1, "standfidget1"); + put(Sequence.PST_STAND_FIDGET2, "standfidget2"); + put(Sequence.PST_STANCE_FIDGET1, "stancefidget1"); + put(Sequence.PST_STANCE_FIDGET2, "stancefidget2"); + put(Sequence.PST_STAND, "stand"); + put(Sequence.PST_STANCE, "stance"); + put(Sequence.PST_STANCE_TO_STAND, "stance2stand"); + put(Sequence.PST_STAND_TO_STANCE, "stand2stance"); + put(Sequence.PST_MISC1, "misc1"); + put(Sequence.PST_MISC2, "misc2"); + put(Sequence.PST_MISC3, "misc3"); + put(Sequence.PST_MISC4, "misc4"); + put(Sequence.PST_MISC5, "misc5"); + put(Sequence.PST_MISC6, "misc6"); + put(Sequence.PST_MISC7, "misc7"); + put(Sequence.PST_MISC8, "misc8"); + put(Sequence.PST_MISC9, "misc9"); + put(Sequence.PST_MISC10, "misc10"); + put(Sequence.PST_MISC11, "misc11"); + put(Sequence.PST_MISC12, "misc12"); + put(Sequence.PST_MISC13, "misc13"); + put(Sequence.PST_MISC14, "misc14"); + put(Sequence.PST_MISC15, "misc15"); + put(Sequence.PST_MISC16, "misc16"); + put(Sequence.PST_MISC17, "misc17"); + put(Sequence.PST_MISC18, "misc18"); + put(Sequence.PST_MISC19, "misc19"); + put(Sequence.PST_MISC20, "misc20"); + }}; + + // action prefixes used to determine BAM resref for animation sequences + private static final HashMap ActionPrefixes = new HashMap() {{ + put(Sequence.PST_ATTACK1, "at1"); + put(Sequence.PST_ATTACK2, "at2"); + put(Sequence.PST_ATTACK3, "at2"); + put(Sequence.PST_GET_HIT, "hit"); + put(Sequence.PST_RUN, "run"); + put(Sequence.PST_WALK, "wlk"); + put(Sequence.PST_SPELL1, "sp1"); + put(Sequence.PST_SPELL2, "sp2"); + put(Sequence.PST_SPELL3, "sp3"); + put(Sequence.PST_GET_UP, "gup"); + put(Sequence.PST_DIE_FORWARD, "dff"); + put(Sequence.PST_DIE_BACKWARD, "dfb"); + put(Sequence.PST_DIE_COLLAPSE, "dcl"); + put(Sequence.PST_TALK1, "tk1"); + put(Sequence.PST_TALK2, "tk2"); + put(Sequence.PST_TALK3, "tk3"); + put(Sequence.PST_STAND_FIDGET1, "sf1"); + put(Sequence.PST_STAND_FIDGET2, "sf2"); + put(Sequence.PST_STANCE_FIDGET1, "cf1"); + put(Sequence.PST_STANCE_FIDGET2, "cf2"); + put(Sequence.PST_STAND, "std"); + put(Sequence.PST_STANCE, "stc"); + put(Sequence.PST_STANCE_TO_STAND, "c2s"); + put(Sequence.PST_STAND_TO_STANCE, "s2c"); + put(Sequence.PST_MISC1, "ms1"); + put(Sequence.PST_MISC2, "ms2"); + put(Sequence.PST_MISC3, "ms3"); + put(Sequence.PST_MISC4, "ms4"); + put(Sequence.PST_MISC5, "ms5"); + put(Sequence.PST_MISC6, "ms6"); + put(Sequence.PST_MISC7, "ms7"); + put(Sequence.PST_MISC8, "ms8"); + put(Sequence.PST_MISC9, "ms9"); + // guessed action prefixes + put(Sequence.PST_MISC10, "msa"); + put(Sequence.PST_MISC11, "msb"); + put(Sequence.PST_MISC12, "msc"); + put(Sequence.PST_MISC13, "msd"); + put(Sequence.PST_MISC14, "mse"); + put(Sequence.PST_MISC15, "msf"); + put(Sequence.PST_MISC16, "msg"); + put(Sequence.PST_MISC17, "msh"); + put(Sequence.PST_MISC18, "msi"); + put(Sequence.PST_MISC19, "msj"); + put(Sequence.PST_MISC20, "msk"); + }}; + + /** + * Returns the action command associated with the specified action sequence. + * @param seq the action sequence. + * @return the action command, {@code null} otherwise. + */ + public static String getActionCommand(Sequence seq) + { + return Slots.get(seq); + } + + /** + * Returns the action prefix associated with the command of the associated action sequence + * according to the default naming scheme. + * @param seq the action sequence. + * @return the action prefix, {@code null} otherwise. + */ + public static String getActionPrefix(Sequence seq) + { + return ActionPrefixes.get(seq); + } + + /** + * A helper method that parses the specified data array and generates a {@link IniMap} instance out of it. + * @param data a String array containing table values for a specific table entry. + * @return a {@code IniMap} instance with the value derived from the specified data array. + * Returns {@code null} if no data could be derived. + */ + public static IniMap processTableData(String[] data) + { + IniMap retVal = null; + if (data == null || data.length < 9) { + return retVal; + } + + int id = SpriteTables.valueToInt(data, SpriteTables.COLUMN_ID, -1); + if (id < 0) { + return retVal; + } + boolean isSpecial = (id >= 0x1000); + + String s = SpriteTables.valueToString(data, SpriteTables.COLUMN_PST_RESREF, ""); + if (s.isEmpty()) { + return retVal; + } + String prefix = isSpecial ? "" : s.substring(0, 1); + String resref = isSpecial ? s : s.substring(1, s.length() - 1); + String suffix = isSpecial ? "" : s.substring(s.length() - 1, s.length()); + TreeMap actions = new TreeMap<>(); + if (isSpecial) { + actions.put(Slots.get(Sequence.PST_STAND), resref); + } else { + for (final Sequence seq : Slots.keySet()) { + String action = Slots.get(seq); + String actionPrefix = ActionPrefixes.get(seq); + if (action != null && actionPrefix != null) { + String bamRes = prefix + actionPrefix + resref; + if (ResourceFactory.resourceExists(bamRes + suffix + ".BAM")) { + actions.put(action, bamRes); + } + } + } + if (actions.isEmpty()) { + if (ResourceFactory.resourceExists(prefix + resref + suffix + ".BAM")) { + actions.put(Slots.get(Sequence.PST_STAND), prefix + resref); + } + } + } + int clown = SpriteTables.valueToInt(data, SpriteTables.COLUMN_PST_CLOWN, 0); + int bestiary = SpriteTables.valueToInt(data, SpriteTables.COLUMN_PST_BESTIARY, 0); + int armor = SpriteTables.valueToInt(data, SpriteTables.COLUMN_PST_ARMOR, 0); + + List lines = SpriteUtils.processTableDataGeneralPst(data); + lines.add("[monster_planescape]"); + for (final String action : actions.keySet()) { + lines.add(action + "=" + actions.get(action)); + } + if (clown != 0) { + lines.add("clown=" + clown); + for (int i = 0; i < clown; i++) { + lines.add("color" + (i + 1) + "=" + (128 + i * 16)); + } + } + if (bestiary != 0) { + lines.add("bestiary=" + bestiary); + } + if (armor != 0) { + lines.add("armor=" + armor); + } + + retVal = IniMap.from(lines); + + return retVal; + } + + public MonsterPlanescapeDecoder(int animationId, IniMap ini) throws Exception + { + super(ANIMATION_TYPE, animationId, ini); + } + + public MonsterPlanescapeDecoder(CreResource cre) throws Exception + { + super(ANIMATION_TYPE, cre); + setTransparentShadow(false); + } + + /** Returns the bestiary entry index. */ + public int getBestiaryIndex() { return getAttribute(KEY_BESTIARY); } + protected void setBestiaryIndex(int v) { setAttribute(KEY_BESTIARY, v); } + + /** ??? */ + public int getArmor() { return getAttribute(KEY_ARMOR); } + protected void setArmor(int v) { setAttribute(KEY_ARMOR, v); } + + /** Returns the walking speed of the creature animation. */ + public double getWalkScale() { return getAttribute(KEY_WALKSCALE); } + protected void setWalkScale(double d) { setAttribute(KEY_WALKSCALE, d); } + + /** Returns the running speed of the creature animation. */ + public double getRunScale() { return getAttribute(KEY_RUNSCALE); } + protected void setRunScale(double d) { setAttribute(KEY_RUNSCALE, d); } + + @Override + protected int getColorOffset(int locationIndex) + { + int retVal = -1; + int numLocations = getColorLocationCount(); + if (locationIndex >= 0 && locationIndex < numLocations) { + retVal = 256 - (numLocations - locationIndex) * 32; + } + return retVal; + } + + /** Returns the number of available false color ranges. */ + public int getColorLocationCount() + { + List color = getAttribute(KEY_COLOR_LOCATION); + return color.size(); + } + + /** Returns the specified color location. */ + public int getColorLocation(int index) throws IndexOutOfBoundsException + { + List color = getAttribute(KEY_COLOR_LOCATION); + if (index < 0 || index >= color.size()) { + throw new IndexOutOfBoundsException(); + } + return color.get(index); + } + + /** Returns a list of all valid color locations. */ + private List setColorLocations(IniMapSection section) + { + List retVal = null; + if (Profile.getGame() == Profile.Game.PST) { + retVal = setColorLocationsCre(); + if (retVal.isEmpty()) { + retVal = setColorLocationsIni(section); + } + } else { + retVal = setColorLocationsIni(section); + } + return retVal; + } + + /** Returns a list of all valid color locations defined in the associated INI file. (PSTEE) */ + private List setColorLocationsIni(IniMapSection section) + { + final HashSet usedColors = new HashSet<>(); + List retVal = new ArrayList<>(7); + for (int i = 0; i < 7; i++) { + int v = section.getAsInteger(KEY_COLOR_LOCATION.getName() + (i + 1), 0); + if (v >= 128 && v < 240 && !usedColors.contains(v & 0xf0)) { + usedColors.add(v & 0xf0); + retVal.add(Integer.valueOf(v)); + } + } + return retVal; + } + + /** Returns a list of all valid color locations from the CRE resource. (original PST) */ + private List setColorLocationsCre() + { + final HashSet usedColors = new HashSet<>(); + CreResource cre = getCreResource(); + int numColors = Math.max(0, Math.min(7, ((IsNumeric)cre.getAttribute(CreResource.CRE_NUM_COLORS)).getValue())); + List retVal = new ArrayList<>(numColors); + for (int i = 0; i < numColors; i++) { + int l = ((IsNumeric)cre.getAttribute(String.format(CreResource.CRE_COLOR_PLACEMENT_FMT, i + 1))).getValue(); + if (l > 0 && !usedColors.contains(l & 0xf0)) { + usedColors.add(l & 0xf0); + retVal.add(Integer.valueOf(l)); + } + } + return retVal; + } + + @Override + public List getAnimationFiles(boolean essential) + { + ArrayList retVal = new ArrayList<>(); + IniMapSection section = getAnimationInfo().getSection(getAnimationSectionName()); + for (final HashMap.Entry e : Slots.entrySet()) { + String resref = section.getAsString(e.getValue(), ""); + if (!resref.isEmpty()) { + resref = resref.toUpperCase(Locale.ENGLISH); + if (ResourceFactory.resourceExists(resref + "B.BAM")) { + retVal.add(resref + "B.BAM"); + } else if (ResourceFactory.resourceExists(resref + ".BAM")) { + retVal.add(resref + ".BAM"); + } + } + } + return retVal; + } + + @Override + public boolean isSequenceAvailable(Sequence seq) + { + return (getSequenceDefinition(seq) != null); + } + + @Override + protected void initDefaults(IniMap ini) throws Exception + { + IniMapSection section = getGeneralIniSection(Objects.requireNonNull(ini, "INI object cannot be null")); + + Objects.requireNonNull(section.getAsString(KEY_ANIMATION_TYPE.getName()), "animation_type required"); + Misc.requireCondition(getAnimationType().contains(getAnimationId()), + String.format("Animation slot (%04X) is not compatible with animation type (%s)", + getAnimationId(), getAnimationType().toString())); + + setMoveScale(section.getAsDouble(KEY_MOVE_SCALE.getName(), 0.0)); + setEllipse(section.getAsInteger(KEY_ELLIPSE.getName(), 16)); + setPersonalSpace(section.getAsInteger(KEY_PERSONAL_SPACE.getName(), 3)); + } + + @Override + protected void init() throws Exception + { + // setting properties + initDefaults(getAnimationInfo()); + IniMapSection section = getSpecificIniSection(); + setFalseColor(section.getAsInteger(KEY_CLOWN.getName(), 0) != 0); + setBestiaryIndex(section.getAsInteger(KEY_BESTIARY.getName(), 0)); + setArmor(section.getAsInteger(KEY_ARMOR.getName(), 0)); + setWalkScale(section.getAsDouble(KEY_WALKSCALE.getName(), getMoveScale())); + setRunScale(section.getAsDouble(KEY_RUNSCALE.getName(), getWalkScale())); + setAttribute(KEY_COLOR_LOCATION, setColorLocations(section)); + + // hardcoded stuff + String resref = section.getAsString(Slots.get(Sequence.PST_STAND), ""); + if ("POSMAIN".equalsIgnoreCase(resref)) { + // Pillar of Skulls: relocate shadow center + setAttribute(KEY_SHADOW, "POSSHAD"); + } +// if ("IGHEAD".equalsIgnoreCase(resref)) { +// // "Coaxmetal" (iron golem): relocate center of arm segments +// setAttribute(KEY_RESREF2, "IGARM"); +// } + if ((getAnimationId() & 0x0fff) == 0x000e) { + // Deionarra + setBrightest(true); + setLightSource(true); // TODO: confirm + } + } + + @Override + protected void createSequence(Sequence seq, Direction[] directions) throws Exception + { + SeqDef sd = Objects.requireNonNull(getSequenceDefinition(seq), "Sequence not available: " + (seq != null ? seq : "(null)")); + if (directions == null) { + directions = Direction.values(); + } + createAnimation(sd, Arrays.asList(directions), FN_BEFORE_SRC_BAM, FN_BEFORE_SRC_FRAME, FN_AFTER_SRC_FRAME, FN_AFTER_DST_FRAME); + } + + @Override + protected SeqDef getSequenceDefinition(Sequence seq) + { + SeqDef retVal = null; + IniMapSection section = getAnimationInfo().getSection(getAnimationSectionName()); + if (Slots.containsKey(seq)) { + ArrayList resrefList = new ArrayList<>(); + + // Shadow resref? + String resref = getAttribute(KEY_SHADOW); + if (!resref.isEmpty()) { + ResourceEntry entry = ResourceFactory.getResourceEntry(resref + ".BAM"); + if (entry != null) { + resrefList.add(entry); + } + } + + // Sprite resref + String name = Slots.get(seq); + resref = section.getAsString(name, ""); + if (resref.isEmpty()) { + return retVal; + } else { + ResourceEntry entry = ResourceFactory.getResourceEntry(resref.toUpperCase(Locale.ENGLISH) + "B.BAM"); + if (entry == null) { + entry = ResourceFactory.getResourceEntry(resref.toUpperCase(Locale.ENGLISH) + ".BAM"); + } + if (entry != null) { + resrefList.add(entry); + } + } + + // Secondary sprite? + resref = getAttribute(KEY_RESREF2); + if (!resref.isEmpty()) { + ResourceEntry entry = ResourceFactory.getResourceEntry(resref + ".BAM"); + if (entry != null) { + resrefList.add(entry); + } + } + + retVal = new SeqDef(seq); + for (final ResourceEntry entry : resrefList) { + // determining number of directions + BamV1Control ctrl = SpriteUtils.loadBamController(entry); + int dirCount = ctrl.cycleCount(); + Direction[] directions = null; + Direction[] directionsE = null; + boolean mirror = true; + if (dirCount < SeqDef.DIR_REDUCED_W.length) { + // special: no individual directions + mirror = false; + directions = Arrays.copyOfRange(SeqDef.DIR_REDUCED_W, 0, dirCount); + } else if (dirCount == SeqDef.DIR_REDUCED_W.length) { + // reduced directions, mirrored east + directions = SeqDef.DIR_REDUCED_W; + directionsE = SeqDef.DIR_REDUCED_E; + } else if (dirCount == SeqDef.DIR_REDUCED.length) { + // special case: reduced directions, not mirrored + mirror = false; + directions = new Direction[SeqDef.DIR_REDUCED.length]; + System.arraycopy(SeqDef.DIR_REDUCED_W, 0, directions, 0, SeqDef.DIR_REDUCED_W.length); + System.arraycopy(SeqDef.DIR_REDUCED_E, 0, directions, SeqDef.DIR_REDUCED_W.length, SeqDef.DIR_REDUCED_E.length); + } else if (dirCount == SeqDef.DIR_FULL_W.length) { + // full directions, mirrored east + directions = SeqDef.DIR_FULL_W; + directionsE = SeqDef.DIR_FULL_E; + } else if (dirCount == SeqDef.DIR_FULL.length) { + // full directions, not mirrored + mirror = false; + directions = SeqDef.DIR_FULL; + } + + if (directions != null && SpriteUtils.bamCyclesExist(entry, 0, directions.length)) { + SeqDef tmp = SeqDef.createSequence(seq, directions, false, entry, 0, null); + retVal.addDirections(tmp.getDirections().toArray(new DirDef[tmp.getDirections().size()])); + if (mirror) { + tmp = SeqDef.createSequence(seq, directionsE, true, entry, 1, null); + retVal.addDirections(tmp.getDirections().toArray(new DirDef[tmp.getDirections().size()])); + } + } + } + + if (retVal.isEmpty()) { + retVal = null; + } + } + + return retVal; + } +} diff --git a/src/org/infinity/resource/cre/decoder/MonsterQuadrantDecoder.java b/src/org/infinity/resource/cre/decoder/MonsterQuadrantDecoder.java new file mode 100644 index 000000000..893c9dc87 --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/MonsterQuadrantDecoder.java @@ -0,0 +1,206 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.decoder; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import org.infinity.resource.ResourceFactory; +import org.infinity.resource.cre.CreResource; +import org.infinity.resource.cre.decoder.tables.SpriteTables; +import org.infinity.resource.cre.decoder.util.AnimationInfo; +import org.infinity.resource.cre.decoder.util.DecoderAttribute; +import org.infinity.resource.cre.decoder.util.DirDef; +import org.infinity.resource.cre.decoder.util.SegmentDef; +import org.infinity.resource.cre.decoder.util.SeqDef; +import org.infinity.resource.cre.decoder.util.Sequence; +import org.infinity.resource.cre.decoder.util.SpriteUtils; +import org.infinity.resource.key.ResourceEntry; +import org.infinity.util.IniMap; +import org.infinity.util.IniMapSection; +import org.infinity.util.Misc; +import org.infinity.util.tuples.Couple; + +/** + * Creature animation decoder for processing type 1000 (monster_quadrant) animations. + * Available ranges: [1000,1fff] + */ +public class MonsterQuadrantDecoder extends QuadrantsBaseDecoder +{ + /** The animation type associated with this class definition. */ + public static final AnimationInfo.Type ANIMATION_TYPE = AnimationInfo.Type.MONSTER_QUADRANT; + + public static final DecoderAttribute KEY_CASTER = DecoderAttribute.with("caster", DecoderAttribute.DataType.BOOLEAN); + public static final DecoderAttribute KEY_PATH_SMOOTH = DecoderAttribute.with("path_smooth", DecoderAttribute.DataType.BOOLEAN); + public static final DecoderAttribute KEY_EXTEND_DIRECTION = DecoderAttribute.with("extend_direction", DecoderAttribute.DataType.BOOLEAN); + public static final DecoderAttribute KEY_EXTEND_DIRECTION_TEST = DecoderAttribute.with("extend_direction_test", DecoderAttribute.DataType.INT); + + private static final HashMap> suffixMap = new HashMap>() {{ + put(Sequence.WALK, Couple.with("G1", 0)); + put(Sequence.STAND, Couple.with("G2", 0)); + put(Sequence.STANCE, Couple.with("G2", 16)); + put(Sequence.GET_HIT, Couple.with("G2", 32)); + put(Sequence.DIE, Couple.with("G2", 48)); + put(Sequence.SLEEP, get(Sequence.DIE)); + put(Sequence.GET_UP, Couple.with("!G2", 48)); + put(Sequence.TWITCH, Couple.with("G2", 64)); + put(Sequence.ATTACK, Couple.with("G3", 0)); + put(Sequence.ATTACK_2, Couple.with("G3", 16)); + put(Sequence.ATTACK_3, Couple.with("G3", 32)); + put(Sequence.CAST, get(Sequence.ATTACK_3)); + }}; + + /** + * A helper method that parses the specified data array and generates a {@link IniMap} instance out of it. + * @param data a String array containing table values for a specific table entry. + * @return a {@code IniMap} instance with the value derived from the specified data array. + * Returns {@code null} if no data could be derived. + */ + public static IniMap processTableData(String[] data) + { + IniMap retVal = null; + if (data == null || data.length < 16) { + return retVal; + } + + String resref = SpriteTables.valueToString(data, SpriteTables.COLUMN_RESREF, ""); + if (resref.isEmpty()) { + return retVal; + } + int falseColor = SpriteTables.valueToInt(data, SpriteTables.COLUMN_CLOWN, 0); + int caster = SpriteTables.valueToInt(data, SpriteTables.COLUMN_HELMET, 0); + int extendDirection = ResourceFactory.resourceExists(resref + "G11E.BAM") ? 1 : 0; + int extendDirectionTest = 9; + ResourceEntry bamEntry = ResourceFactory.getResourceEntry(resref + "G11.BAM"); + int numCycles = SpriteUtils.getBamCycles(bamEntry); + if (numCycles == 8) { + extendDirectionTest = 5; // TBC + } + + List lines = SpriteUtils.processTableDataGeneral(data, ANIMATION_TYPE); + lines.add("[monster_quadrant]"); + lines.add("false_color=" + falseColor); + lines.add("caster=" + caster); + lines.add("extend_direction=" + extendDirection); + lines.add("extend_direction_test=" + extendDirectionTest); + lines.add("quadrants=4"); + lines.add("resref=" + resref); + + retVal = IniMap.from(lines); + + return retVal; + } + + public MonsterQuadrantDecoder(int animationId, IniMap ini) throws Exception + { + super(ANIMATION_TYPE, animationId, ini); + } + + public MonsterQuadrantDecoder(CreResource cre) throws Exception + { + super(ANIMATION_TYPE, cre); + } + + /** Returns whether attack animations {@code Attack2} and {@code Attack3} are used as casting animations. */ + public boolean isCaster() { return getAttribute(KEY_CASTER); } + protected void setCaster(boolean b) { setAttribute(KEY_CASTER, b); } + + /** ??? */ + public boolean isSmoothPath() { return getAttribute(KEY_PATH_SMOOTH); } + protected void setSmoothPath(boolean b) { setAttribute(KEY_PATH_SMOOTH, b); } + + /** Returns whether eastern directions are available. */ + public boolean isExtendedDirection() { return getAttribute(KEY_EXTEND_DIRECTION); } + protected void setExtendedDirection(boolean b) { setAttribute(KEY_EXTEND_DIRECTION, b); } + + /** ??? */ + public int getExtendedDirectionSize() { return getAttribute(KEY_EXTEND_DIRECTION_TEST); } + protected void setExtendedDirectionSize(int v) { setAttribute(KEY_EXTEND_DIRECTION_TEST, v); } + + @Override + public List getAnimationFiles(boolean essential) + { + ArrayList retVal = new ArrayList<>(); + String resref = getAnimationResref(); + for (final String suffix : new String[] {"G1", "G2", "G3"}) { + for (int i = 0; i < getQuadrants(); i++) { + retVal.add(resref + suffix + (i+1) + ".BAM"); + if (isExtendedDirection()) { + retVal.add(resref + suffix + (i+1) + "E.BAM"); + } + } + } + return retVal; + } + + @Override + public boolean isSequenceAvailable(Sequence seq) + { + return (getSequenceDefinition(seq) != null); + } + + @Override + protected void init() throws Exception + { + // setting properties + super.init(); + IniMapSection section = getSpecificIniSection(); + setCaster(section.getAsInteger(KEY_CASTER.getName(), 0) != 0); + setFalseColor(section.getAsInteger(KEY_FALSE_COLOR.getName(), 0) != 0); + setSmoothPath(section.getAsInteger(KEY_PATH_SMOOTH.getName(), 0) != 0); + setExtendedDirection(section.getAsInteger(KEY_EXTEND_DIRECTION.getName(), 0) != 0); + setExtendedDirectionSize(section.getAsInteger(KEY_EXTEND_DIRECTION_TEST.getName(), 9)); + setQuadrants(section.getAsInteger(KEY_QUADRANTS.getName(), 4)); + Misc.requireCondition(getQuadrants() < 10, "Too many quadrants defined: " + getQuadrants(), IllegalArgumentException.class); + } + + @Override + protected SeqDef getSequenceDefinition(Sequence seq) + { + SeqDef retVal = null; + String resref = getAnimationResref(); + + if (isCaster() && (seq == Sequence.ATTACK_3)) { + return retVal; + } + + if (!isCaster() && (seq == Sequence.CAST)) { + return retVal; + } + + List cycleList = new ArrayList<>(); + List cycleListE = new ArrayList<>(); + boolean valid = true; + if (suffixMap.containsKey(seq) && (isCaster() || seq != Sequence.SPELL && seq != Sequence.CAST)) { + String suffix = suffixMap.get(seq).getValue0(); + SegmentDef.Behavior behavior = SegmentDef.getBehaviorOf(suffix); + suffix = SegmentDef.fixBehaviorSuffix(suffix); + int cycleOfs = suffixMap.get(seq).getValue1().intValue(); + for (int i = 1; valid && i <= getQuadrants(); i++) { + ResourceEntry entry = ResourceFactory.getResourceEntry(resref + suffix + i + ".BAM"); + ResourceEntry entryE = entry; + int cycle = cycleOfs; + int cycleE = cycleOfs + 1; + valid &= SpriteUtils.bamCyclesExist(entry, cycle, SeqDef.DIR_FULL_W.length); + if (isExtendedDirection()) { + entryE = ResourceFactory.getResourceEntry(resref + suffix + i + "E.BAM"); + cycleE = cycle + SeqDef.DIR_FULL_W.length; + valid &= SpriteUtils.bamCyclesExist(entryE, cycleE, SeqDef.DIR_FULL_E.length); + } + cycleList.add(new SegmentDef(entry, cycle, null, behavior)); + cycleListE.add(new SegmentDef(entryE, cycleE, null, behavior)); + } + } + + if (!cycleList.isEmpty() && valid) { + retVal = SeqDef.createSequence(seq, SeqDef.DIR_FULL_W, false, cycleList); + SeqDef tmp = SeqDef.createSequence(seq, SeqDef.DIR_FULL_E, !isExtendedDirection(), cycleListE); + retVal.addDirections(tmp.getDirections().toArray(new DirDef[tmp.getDirections().size()])); + } + + return retVal; + } +} diff --git a/src/org/infinity/resource/cre/decoder/PlaceholderDecoder.java b/src/org/infinity/resource/cre/decoder/PlaceholderDecoder.java new file mode 100644 index 000000000..02a844950 --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/PlaceholderDecoder.java @@ -0,0 +1,161 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.decoder; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.infinity.resource.Profile; +import org.infinity.resource.cre.CreResource; +import org.infinity.resource.cre.decoder.util.AnimationInfo; +import org.infinity.resource.cre.decoder.util.DecoderAttribute; +import org.infinity.resource.cre.decoder.util.Direction; +import org.infinity.resource.cre.decoder.util.SegmentDef; +import org.infinity.resource.cre.decoder.util.SeqDef; +import org.infinity.resource.cre.decoder.util.Sequence; +import org.infinity.resource.key.BufferedResourceEntry; +import org.infinity.resource.key.ResourceEntry; +import org.infinity.util.IniMap; +import org.infinity.util.IniMapEntry; +import org.infinity.util.IniMapSection; +import org.infinity.util.io.StreamUtils; + +/** + * General-purpose creature animation decoder for handling non-existing or unknown animation types. + * Available ranges: [0000,ffff] + */ +public class PlaceholderDecoder extends SpriteDecoder +{ + /** The animation type associated with this class definition. */ + public static final AnimationInfo.Type ANIMATION_TYPE = AnimationInfo.Type.PLACEHOLDER; + + /** ResourceEntry for the placeholder animation BAM file. */ + private static final ResourceEntry BAM_PLACEHOLDER = loadPlaceholderBam(); + + /** Loads the placeholder animation into memory as a virtual resource entry object. */ + private static ResourceEntry loadPlaceholderBam() + { + ResourceEntry retVal = null; + // REMEMBER: BAM file path is relative to PlaceholderDecoder class path + try (InputStream is = PlaceholderDecoder.class.getResourceAsStream("placeholder.bam")) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + int nRead; + byte[] buf = new byte[1024]; + while ((nRead = is.read(buf, 0, buf.length)) != -1) { + baos.write(buf, 0, nRead); + } + baos.flush(); + retVal = new BufferedResourceEntry(StreamUtils.getByteBuffer(baos.toByteArray()), "placeholder.bam"); + } catch (IOException e) { + e.printStackTrace(); + } + return retVal; + } + + public PlaceholderDecoder(int animationId, IniMap ini) throws Exception + { + super(ANIMATION_TYPE, animationId, ini); + } + + public PlaceholderDecoder(CreResource cre) throws Exception + { + super(ANIMATION_TYPE, cre); + } + + @Override + public List getAnimationFiles(boolean essential) + { + String resref = getAnimationResref(); + ArrayList retVal = new ArrayList<>(); + retVal.add(resref + ".BAM"); + return retVal; + } + + @Override + public boolean isSequenceAvailable(Sequence seq) + { + return (getSequenceDefinition(seq) != null); + } + + @Override + protected IniMapSection getSpecificIniSection() + { + IniMapSection retVal = null; + IniMap ini = getAnimationInfo(); + if (ini != null) { + for (final Iterator iter = ini.iterator(); iter.hasNext(); ) { + IniMapSection section = iter.next(); + switch (section.getName().toLowerCase()) { + case "general": + case "sounds": + break; + default: + retVal = section; + } + if (retVal != null) { + break; + } + } + } + + if (retVal == null) { + retVal = new IniMapSection(getAnimationSectionName(), 0, null); + } + + return retVal; + } + + @Override + protected void init() throws Exception + { + // setting properties + initDefaults(getAnimationInfo()); + + // autodetecting animation attributes + IniMapSection section = getSpecificIniSection(); + setAttribute(KEY_ANIMATION_SECTION, section.getName()); // override with animation-specific name + for (final Iterator iter = section.iterator(); iter.hasNext(); ) { + IniMapEntry entry = iter.next(); + String key = entry.getKey(); + String value = entry.getValue(); + try { + int n = Integer.parseInt(value); + if (n == 0 || n == 1) { + setAttribute(new DecoderAttribute(key, DecoderAttribute.DataType.BOOLEAN), Boolean.valueOf(n != 0)); + } else { + setAttribute(new DecoderAttribute(key, DecoderAttribute.DataType.DECIMAL), Integer.valueOf(n)); + } + } catch (NumberFormatException e) { + setAttribute(new DecoderAttribute(key, DecoderAttribute.DataType.STRING), value); + } + } + } + + @Override + protected SeqDef getSequenceDefinition(Sequence seq) + { + SeqDef retVal = null; + + switch (Profile.getGame()) { + case PST: + case PSTEE: + if (seq == Sequence.PST_STAND) { + retVal = SeqDef.createSequence(seq, new Direction[] {Direction.S}, false, + BAM_PLACEHOLDER, 0, SegmentDef.SpriteType.AVATAR); + } + break; + default: + if (seq == Sequence.STAND) { + retVal = SeqDef.createSequence(seq, new Direction[] {Direction.S}, false, + BAM_PLACEHOLDER, 0, SegmentDef.SpriteType.AVATAR); + } + } + return retVal; + } +} diff --git a/src/org/infinity/resource/cre/decoder/QuadrantsBaseDecoder.java b/src/org/infinity/resource/cre/decoder/QuadrantsBaseDecoder.java new file mode 100644 index 000000000..160258aa3 --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/QuadrantsBaseDecoder.java @@ -0,0 +1,40 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.decoder; + +import org.infinity.resource.cre.CreResource; +import org.infinity.resource.cre.decoder.util.AnimationInfo; +import org.infinity.resource.cre.decoder.util.DecoderAttribute; +import org.infinity.util.IniMap; + +/** + * Common base for processing segmented creature animations. + */ +public abstract class QuadrantsBaseDecoder extends SpriteDecoder +{ + public static final DecoderAttribute KEY_QUADRANTS = DecoderAttribute.with("quadrants", DecoderAttribute.DataType.INT); + + /** Returns the number of segments this creature animation has been split into. */ + public int getQuadrants() { return getAttribute(KEY_QUADRANTS); } + protected void setQuadrants(int v) { setAttribute(KEY_QUADRANTS, v); } + + public QuadrantsBaseDecoder(AnimationInfo.Type type, int animationId, IniMap ini) throws Exception + { + super(type, animationId, ini); + } + + protected QuadrantsBaseDecoder(AnimationInfo.Type type, CreResource cre) throws Exception + { + super(type, cre); + } + + @Override + protected void init() throws Exception + { + // setting properties + initDefaults(getAnimationInfo()); + setDetectedByInfravision(true); + } +} diff --git a/src/org/infinity/resource/cre/decoder/SpriteDecoder.java b/src/org/infinity/resource/cre/decoder/SpriteDecoder.java new file mode 100644 index 000000000..3c13aa64e --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/SpriteDecoder.java @@ -0,0 +1,2107 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.decoder; + +import java.awt.AlphaComposite; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Composite; +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.RenderingHints; +import java.awt.Stroke; +import java.awt.Transparency; +import java.awt.geom.AffineTransform; +import java.awt.image.AffineTransformOp; +import java.awt.image.BufferedImage; +import java.awt.image.DataBufferInt; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.TreeMap; + +import org.infinity.datatype.IsNumeric; +import org.infinity.resource.Profile; +import org.infinity.resource.ResourceFactory; +import org.infinity.resource.cre.CreResource; +import org.infinity.resource.cre.decoder.util.AnimationInfo; +import org.infinity.resource.cre.decoder.util.CreatureInfo; +import org.infinity.resource.cre.decoder.util.CycleDef; +import org.infinity.resource.cre.decoder.util.DecoderAttribute; +import org.infinity.resource.cre.decoder.util.DirDef; +import org.infinity.resource.cre.decoder.util.Direction; +import org.infinity.resource.cre.decoder.util.EffectInfo; +import org.infinity.resource.cre.decoder.util.FrameInfo; +import org.infinity.resource.cre.decoder.util.ItemInfo; +import org.infinity.resource.cre.decoder.util.SegmentDef; +import org.infinity.resource.cre.decoder.util.SeqDef; +import org.infinity.resource.cre.decoder.util.Sequence; +import org.infinity.resource.cre.decoder.util.SpriteUtils; +import org.infinity.resource.graphics.BamDecoder; +import org.infinity.resource.graphics.BamV1Decoder.BamV1Control; +import org.infinity.resource.graphics.BlendingComposite; +import org.infinity.resource.graphics.ColorConvert; +import org.infinity.resource.graphics.PseudoBamDecoder; +import org.infinity.resource.key.ResourceEntry; +import org.infinity.util.IniMap; +import org.infinity.util.IniMapSection; +import org.infinity.util.Misc; +import org.infinity.util.tuples.Couple; + +/** + * Specialized BAM decoder for creature animation sprites. + */ +public abstract class SpriteDecoder extends PseudoBamDecoder +{ + // List of general creature animation attributes + public static final DecoderAttribute KEY_ANIMATION_TYPE = DecoderAttribute.with("animation_type", DecoderAttribute.DataType.USERDEFINED); + public static final DecoderAttribute KEY_ANIMATION_SECTION = DecoderAttribute.with("animation_section", DecoderAttribute.DataType.STRING); + public static final DecoderAttribute KEY_MOVE_SCALE = DecoderAttribute.with("move_scale", DecoderAttribute.DataType.DECIMAL); + public static final DecoderAttribute KEY_ELLIPSE = DecoderAttribute.with("ellipse", DecoderAttribute.DataType.INT); + public static final DecoderAttribute KEY_COLOR_BLOOD = DecoderAttribute.with("color_blood", DecoderAttribute.DataType.INT); + public static final DecoderAttribute KEY_COLOR_CHUNKS = DecoderAttribute.with("color_chunks", DecoderAttribute.DataType.INT); + public static final DecoderAttribute KEY_SOUND_FREQ = DecoderAttribute.with("sound_freq", DecoderAttribute.DataType.INT); + public static final DecoderAttribute KEY_SOUND_DEATH = DecoderAttribute.with("sound_death", DecoderAttribute.DataType.STRING); + public static final DecoderAttribute KEY_PERSONAL_SPACE = DecoderAttribute.with("personal_space", DecoderAttribute.DataType.INT); + public static final DecoderAttribute KEY_CAST_FRAME = DecoderAttribute.with("cast_frame", DecoderAttribute.DataType.INT); + public static final DecoderAttribute KEY_HEIGHT_OFFSET = DecoderAttribute.with("height_offset", DecoderAttribute.DataType.INT); + public static final DecoderAttribute KEY_BRIGHTEST = DecoderAttribute.with("brightest", DecoderAttribute.DataType.BOOLEAN); + public static final DecoderAttribute KEY_MULTIPLY_BLEND = DecoderAttribute.with("multiply_blend", DecoderAttribute.DataType.BOOLEAN); + public static final DecoderAttribute KEY_LIGHT_SOURCE = DecoderAttribute.with("light_source", DecoderAttribute.DataType.BOOLEAN); + public static final DecoderAttribute KEY_NEW_PALETTE = DecoderAttribute.with("new_palette", DecoderAttribute.DataType.STRING); + public static final DecoderAttribute KEY_SOUND_REF = DecoderAttribute.with("sound_ref", DecoderAttribute.DataType.STRING); + public static final DecoderAttribute KEY_COMBAT_ROUND_0 = DecoderAttribute.with("combat_round_0", DecoderAttribute.DataType.STRING); + public static final DecoderAttribute KEY_COMBAT_ROUND_1 = DecoderAttribute.with("combat_round_1", DecoderAttribute.DataType.STRING); + public static final DecoderAttribute KEY_COMBAT_ROUND_2 = DecoderAttribute.with("combat_round_2", DecoderAttribute.DataType.STRING); + public static final DecoderAttribute KEY_COMBAT_ROUND_3 = DecoderAttribute.with("combat_round_3", DecoderAttribute.DataType.STRING); + public static final DecoderAttribute KEY_COMBAT_ROUND_4 = DecoderAttribute.with("combat_round_4", DecoderAttribute.DataType.STRING); + public static final DecoderAttribute KEY_WALK_SOUND = DecoderAttribute.with("walk_sound", DecoderAttribute.DataType.STRING); + // List of commonly used attributes specific to creature animation types + public static final DecoderAttribute KEY_RESREF = DecoderAttribute.with("resref", DecoderAttribute.DataType.STRING); + public static final DecoderAttribute KEY_DETECTED_BY_INFRAVISION = DecoderAttribute.with("detected_by_infravision", DecoderAttribute.DataType.BOOLEAN); + public static final DecoderAttribute KEY_FALSE_COLOR = DecoderAttribute.with("false_color", DecoderAttribute.DataType.BOOLEAN); + public static final DecoderAttribute KEY_TRANSLUCENT = DecoderAttribute.with("translucent", DecoderAttribute.DataType.BOOLEAN); + + /** + * A default operation that can be passed to the + * {@link #createAnimation(SeqDef, List, BeforeSourceBam, BeforeSourceFrame, AfterSourceFrame, AfterDestFrame)} + * method. It is called once per source BAM resource. + * Performed actions: palette replacement, shadow color fix, false color replacement, translucency + */ + protected final BeforeSourceBam FN_BEFORE_SRC_BAM = new BeforeSourceBam() { + @Override + public void accept(BamV1Control control, SegmentDef sd) + { + if (isPaletteReplacementEnabled() && sd.getSpriteType() == SegmentDef.SpriteType.AVATAR) { + int[] palette = getNewPaletteData(sd.getEntry()); + if (palette != null) { + SpriteUtils.applyNewPalette(control, palette); + } + } + + SpriteUtils.fixShadowColor(control, isTransparentShadow()); + + if (isPaletteReplacementEnabled() && isFalseColor()) { + applyFalseColors(control, sd); + } + + if (isTintEnabled()) { + applyColorTint(control, sd); + } + + if (isPaletteReplacementEnabled() && isFalseColor() && sd.getSpriteType() == SegmentDef.SpriteType.AVATAR) { + applyColorEffects(control, sd); + } + + if ((isTranslucencyEnabled() && isTranslucent()) || + (isBlurEnabled() && isBlurred())) { + int minVal = (isBlurEnabled() && isBlurred()) ? 64 : 255; + applyTranslucency(control, minVal); + } + } + }; + + /** + * A default operation that can be passed to the + * {@link #createAnimation(SeqDef, List, BeforeSourceBam, BeforeSourceFrame, AfterSourceFrame, AfterDestFrame)} + * method. It is called for each source frame (segment) before being applied to the target frame. + */ + protected final BeforeSourceFrame FN_BEFORE_SRC_FRAME = new BeforeSourceFrame() { + @Override + public BufferedImage apply(SegmentDef sd, BufferedImage image, Graphics2D g) + { + // nothing to do... + return image; + } + }; + + /** + * A default operation that can be passed to the + * {@link #createAnimation(SeqDef, List, BeforeSourceBam, BeforeSourceFrame, AfterSourceFrame, AfterDestFrame)} + * method. It is called for each source frame (segment) after being applied to the target frame. + */ + protected final AfterSourceFrame FN_AFTER_SRC_FRAME = new AfterSourceFrame() { + @Override + public void accept(SegmentDef sd, Graphics2D g) + { + // nothing to do... + } + }; + + /** + * A default action that can be passed to the + * {@link #createAnimation(SeqDef, List, BeforeSourceBam, BeforeSourceFrame, AfterSourceFrame, AfterDestFrame)} + * method. It calculates an eastern direction frame by mirroring it horizontally if needed. + */ + protected final AfterDestFrame FN_AFTER_DST_FRAME = new AfterDestFrame() { + @Override + public void accept(DirDef dd, int frameIdx) + { + if (dd.isMirrored()) { + flipImageHorizontal(frameIdx); + } + } + }; + + private final CreatureInfo creInfo; + private final IniMap ini; + /** Storage for associations between directions and cycle indices. */ + private final EnumMap directionMap; + /** Cache for creature animation attributes. */ + private final TreeMap attributesMap; + + private BufferedImage imageCircle; + private Sequence currentSequence; + private boolean showCircle; + private boolean selectionCircleBitmap; + private boolean showPersonalSpace; + private boolean showBoundingBox; + private boolean transparentShadow; + private boolean translucencyEnabled; + private boolean tintEnabled; + private boolean blurEnabled; + private boolean paletteReplacementEnabled; + private boolean renderSpriteAvatar; + private boolean renderSpriteWeapon; + private boolean renderSpriteHelmet; + private boolean renderSpriteShield; + private boolean animationChanged; + private boolean autoApplyChanges; + + /** + * Creates a new {@code SpriteDecoder} instance based on the specified animation id. + * @param animationId the creature animation id in the range [0, 0xffff]. + * @return A {@code SpriteDecoder} instance with processed animation data. + * @throws Exception if the creature animation could not be loaded. + */ + public static SpriteDecoder importSprite(int animationId) throws Exception + { + if (animationId < 0 || animationId > 0xffff) { + throw new IllegalArgumentException(String.format("Animation id is out of range: 0x%04x", animationId)); + } + CreResource cre = SpriteUtils.getPseudoCre(animationId, null, null); + return importSprite(cre); + } + + /** + * Creates a new {@code SpriteDecoder} instance based on the specified CRE resource. + * @param cre The CRE resource instance. + * @return A {@code SpriteDecoder} instance with processed animation data. + * @throws Exception if the specified resource could not be processed. + */ + public static SpriteDecoder importSprite(CreResource cre) throws Exception + { + Objects.requireNonNull(cre, "CRE resource cannot be null"); + int animationId = ((IsNumeric)cre.getAttribute(CreResource.CRE_ANIMATION)).getValue(); + Class spriteClass = + Objects.requireNonNull(SpriteUtils.detectAnimationType(animationId), String.format("Creature animation is not available: 0x%04x", animationId)); + try { + Constructor ctor = + Objects.requireNonNull(spriteClass.getConstructor(CreResource.class), "No matching constructor found"); + return ctor.newInstance(cre); + } catch (InvocationTargetException ite) { + throw (ite.getCause() instanceof Exception) ? (Exception)ite.getCause() : ite; + } + } + + /** + * Instances creates with this constructor are only suited for identification purposes. + * @param type the animation type + * @param animationId specific animation id + * @param sectionName INI section name for animation-specific data + * @param ini the INI file with creature animation attributes + * @throws Exception + */ + protected SpriteDecoder(AnimationInfo.Type type, int animationId, IniMap ini) throws Exception + { + Objects.requireNonNull(type, "Animation type cannot be null"); + Objects.requireNonNull(ini, "No INI data available for animation id: " + animationId); + this.attributesMap = new TreeMap<>(); + this.directionMap = new EnumMap<>(Direction.class); + setAttribute(KEY_ANIMATION_TYPE, type); + setAttribute(KEY_ANIMATION_SECTION, type.getSectionName()); + this.creInfo = new CreatureInfo(this, SpriteUtils.getPseudoCre(animationId, null, null)); + this.ini = ini; + this.currentSequence = Sequence.NONE; + init(); + if (!isMatchingAnimationType()) { + throw new IllegalArgumentException("Animation id is incompatible with animation type: " + type.toString()); + } + } + + /** + * This constructor creates an instance that can be used to render animation sequences. + * @param type the animation type + * @param cre the CRE resource instance. + * @throws Exception + */ + protected SpriteDecoder(AnimationInfo.Type type, CreResource cre) throws Exception + { + Objects.requireNonNull(type, "Animation type cannot be null"); + this.attributesMap = new TreeMap<>(); + this.directionMap = new EnumMap<>(Direction.class); + setAttribute(KEY_ANIMATION_TYPE, type); + setAttribute(KEY_ANIMATION_SECTION, type.getSectionName()); + this.creInfo = new CreatureInfo(this, cre); + this.ini = Objects.requireNonNull(SpriteUtils.getAnimationInfo(getAnimationId()), + String.format("No INI data available for animation id: 0x%04x", getAnimationId())); + this.currentSequence = Sequence.NONE; + this.showCircle = false; + this.selectionCircleBitmap = (Profile.getGame() == Profile.Game.PST) || (Profile.getGame() == Profile.Game.PSTEE); + this.showPersonalSpace = false; + this.showBoundingBox = false; + this.transparentShadow = true; + this.translucencyEnabled = true; + this.tintEnabled = true; + this.blurEnabled = true; + this.paletteReplacementEnabled = true; + this.renderSpriteAvatar = true; + this.renderSpriteWeapon = true; + this.renderSpriteShield = true; + this.renderSpriteHelmet = true; + this.autoApplyChanges = true; + SpriteUtils.updateRandomPool(); + init(); + } + + /** + * Returns the data associated with the specified attribute name. + * @param key the attribute name. + * @return attribute data in the type inferred from the method call. + * Returns {@code null} if data is not available for the inferred type. + */ + @SuppressWarnings("unchecked") + public T getAttribute(DecoderAttribute att) + { + T retVal = null; + if (att == null) { + return retVal; + } + + Object data = attributesMap.getOrDefault(att, att.getDefaultValue()); + if (data != null) { + try { + retVal = (T)data; + } catch (ClassCastException e) { + // e.printStackTrace(); + } + } + return retVal; + } + + /** + * Stores the attribute key and value along with the autodetected data type. + * @param key the attribute name. + * @param value the value in one of the data types covered by {@link DecoderAttribute.DataType}. + */ + protected void setAttribute(DecoderAttribute att, Object value) + { + if (att == null) { + return; + } + attributesMap.put(att, value); + } + + /** Returns an iterator over the attribute keys. */ + public Iterator getAttributeIterator() + { + return attributesMap.keySet().iterator(); + } + + /** Returns the type of the current creature animation. */ + public AnimationInfo.Type getAnimationType() + { + return getAttribute(KEY_ANIMATION_TYPE); + } + + /** + * Returns the INI section name for the current animation type. + * Returns {@code null} if the name could not be determined. + */ + public String getAnimationSectionName() + { + return getAttribute(KEY_ANIMATION_SECTION); + } + + /** + * Returns a list of BAM filenames associated with the current animation type. + * @param essential if set returns only essential files required for the animation. + * @return list of BAM filenames associated with the current animation type. + * Returns {@code null} if files could not be determined. + */ + public abstract List getAnimationFiles(boolean essential); + + /** Recreates the creature animation based on the current creature resource. */ + public void reset() throws Exception + { + Direction[] directions = getDirectionMap().keySet().toArray(new Direction[getDirectionMap().keySet().size()]); + discard(); + // recreating current sequence + if (getCurrentSequence() != Sequence.NONE) { + createSequence(getCurrentSequence(), directions); + } + } + + /** Removes the currently loaded animation sequence. */ + protected void discard() + { + frameClear(); + directionMap.clear(); + SpriteUtils.clearBamCache(); + } + + /** + * Loads the specified sequence if available. Discards the currently active sequence. + * Call {@code reset()} instead to enforce reloading the same sequence with different + * creature attributes. + * @param seq the animation sequence to load. Specifying {@code Sequence.None} only discards the current sequence. + * @return whether the sequence was successfully loaded. + */ + public boolean loadSequence(Sequence seq) throws Exception + { + return loadSequence(seq, null); + } + + /** + * Loads selected directions of the specified sequence if available. Discards the currently active sequence. + * Call {@code reset()} instead to enforce reloading the same sequence with different + * creature attributes. + * @param seq the animation sequence to load. Specifying {@code Sequence.None} only discards the current sequence. + * @param directions array with directions allowed to be created. Specify {@code null} to create animations + * for all directions. + * @return whether the sequence was successfully loaded. + */ + public boolean loadSequence(Sequence seq, Direction[] directions) throws Exception + { + boolean retVal = true; + + if (getCurrentSequence() != Objects.requireNonNull(seq, "Animation sequence cannot be null")) { + // discarding current sequence + discard(); + + try { + createSequence(seq, directions); + currentSequence = seq; + } catch (NullPointerException e) { + retVal = (seq != Sequence.NONE); + } catch (Exception e) { + e.printStackTrace(); + retVal = (seq != Sequence.NONE); + } + } + + return retVal; + } + + /** Returns the currently active sequence. */ + public Sequence getCurrentSequence() + { + return currentSequence; + } + + /** Returns whether the specified animation sequence is available for the current creature animation. */ + public abstract boolean isSequenceAvailable(Sequence seq); + + /** + * Returns the closest available direction to the specified direction. + * @param dir the requested direction + * @return an available {@code Direction} that is closest to the specified direction. + * Returns {@code null} if no direction is available. + */ + public Direction getExistingDirection(Direction dir) + { + Direction retVal = null; + + if (dir == null) { + return retVal; + } + if (getDirectionMap().containsKey(dir)) { + return dir; + } + SeqDef sd = getSequenceDefinition(getCurrentSequence()); + if (sd == null || sd.isEmpty()) { + return retVal; + } + + int dirIdx = dir.getValue(); + int dirLen = Direction.values().length; + int maxRange = dirLen / 2; + for (int range = 1; range <= maxRange; range++) { + int dist = (dirIdx + range + dirLen) % dirLen; + Direction distDir = Direction.from(dist); + if (getDirectionMap().containsKey(distDir)) { + retVal = distDir; + break; + } + dist = (dirIdx - range + dirLen) % dirLen; + distDir = Direction.from(dist); + if (getDirectionMap().containsKey(distDir)) { + retVal = distDir; + break; + } + } + + return retVal; + } + + /** Provides access to the {@link CreatureInfo} instance associated with the sprite decoder. */ + public CreatureInfo getCreatureInfo() + { + return creInfo; + } + + /** Returns the {@code CreResource} instance of the current CRE resource. */ + public CreResource getCreResource() + { + return creInfo.getCreResource(); + } + + /** Returns the numeric animation id of the current CRE resource. */ + public int getAnimationId() + { + return creInfo.getAnimationId(); + } + + /** Returns a INI structure with creature animation info. */ + public IniMap getAnimationInfo() + { + return ini; + } + + /** Returns whether the selection circle for the creature is drawn. */ + public boolean isSelectionCircleEnabled() + { + return showCircle; + } + + /** Sets whether the selection circle for the creature is drawn. */ + public void setSelectionCircleEnabled(boolean b) + { + if (showCircle != b) { + showCircle = b; + selectionCircleChanged(); + } + } + + /** Returns whether the space occupied by the creature is visualized. */ + public boolean isPersonalSpaceVisible() + { + return showPersonalSpace; + } + + /** Sets whether the space occupied by the creature is visualized. */ + public void setPersonalSpaceVisible(boolean b) + { + if (showPersonalSpace != b) { + showPersonalSpace = b; + personalSpaceChanged(); + } + } + + /** Returns whether a bounding box is drawn around sprites (or quadrants) and secondary overlays. */ + public boolean isBoundingBoxVisible() + { + return showBoundingBox; + } + + /** Sets whether a bounding box is drawn around sprites (or quadrants) and secondary overlays. */ + public void setBoundingBoxVisible(boolean b) + { + if (showBoundingBox != b) { + showBoundingBox = b; + spriteChanged(); + } + } + + /** Returns whether the avatar sprite should be rendered. */ + public boolean getRenderAvatar() + { + return renderSpriteAvatar; + } + + /** Sets whether the avatar sprite should be rendered. */ + public void setRenderAvatar(boolean b) + { + if (renderSpriteAvatar != b) { + renderSpriteAvatar = b; + spriteChanged(); + } + } + + /** Returns whether the weapon overlay should be rendered. This option affects only specific animation types. */ + public boolean getRenderWeapon() + { + return renderSpriteWeapon; + } + + /** Sets whether the weapon overlay should be rendered. This option affects only specific animation types. */ + public void setRenderWeapon(boolean b) + { + if (renderSpriteWeapon != b) { + renderSpriteWeapon = b; + spriteChanged(); + } + } + + /** + * Returns whether the shield (or left-handed weapon) overlay should be rendered. + * This option affects only specific animation types. + */ + public boolean getRenderShield() + { + return renderSpriteShield; + } + + /** + * Sets whether the shield (or left-handed weapon) overlay should be rendered. + * This option affects only specific animation types. + */ + public void setRenderShield(boolean b) + { + if (renderSpriteShield != b) { + renderSpriteShield = b; + spriteChanged(); + } + } + + /** Returns whether the helmet overlay should be rendered. This option affects only specific animation types. */ + public boolean getRenderHelmet() + { + return renderSpriteHelmet; + } + + /** Sets whether the helmet overlay should be rendered. This option affects only specific animation types. */ + public void setRenderHelmet(boolean b) + { + if (renderSpriteHelmet != b) { + renderSpriteHelmet = b; + spriteChanged(); + } + } + + /** Returns whether translucency effect is applied to the creature animation. */ + public boolean isTranslucencyEnabled() + { + return translucencyEnabled; + } + + /** Sets whether translucency effect is applied to the creature animation. */ + public void setTranslucencyEnabled(boolean b) + { + if (translucencyEnabled != b) { + translucencyEnabled = b; + if (isTranslucent()) { + SpriteUtils.clearBamCache(); + spriteChanged(); + } + } + } + + /** Returns whether tint effects are applied to the creature animation. */ + public boolean isTintEnabled() + { + return tintEnabled; + } + + /** Sets whether tint effects are applied to the creature animation. */ + public void setTintEnabled(boolean b) + { + if (tintEnabled != b) { + tintEnabled = b; + SpriteUtils.clearBamCache(); + spriteChanged(); + } + } + + /** Returns whether blur effect is applied to the creature animation. */ + public boolean isBlurEnabled() + { + return blurEnabled; + } + + /** Sets whether blur effect is applied to the creature animation. */ + public void setBlurEnabled(boolean b) + { + if (blurEnabled != b) { + blurEnabled = b; + SpriteUtils.clearBamCache(); + spriteChanged(); + } + } + + /** Returns whether any kind of palette replacement (full palette or false colors) is enabled. */ + public boolean isPaletteReplacementEnabled() + { + return paletteReplacementEnabled; + } + + /** Sets whether palette replacement (full palette or false colors) is enabled. */ + public void setPaletteReplacementEnabled(boolean b) + { + if (paletteReplacementEnabled != b) { + paletteReplacementEnabled = b; + SpriteUtils.clearBamCache(); + spriteChanged(); + } + } + + /** + * Returns {@code true} if a bitmap is used to render the selection circle. + * Returns {@code false} if a colored circle is drawn instead. + */ + public boolean isSelectionCircleBitmap() + { + return selectionCircleBitmap; + } + + /** + * Specify {@code true} if a bitmap should be used to render the selection circle. + * Specify {@code false} if a colored circle should be drawn instead. + */ + public void setSelectionCircleBitmap(boolean b) + { + if (selectionCircleBitmap != b) { + selectionCircleBitmap = b; + selectionCircleChanged(); + } + } + + /** Returns the moving speed of the creature animation. */ + public double getMoveScale() + { + return getAttribute(KEY_MOVE_SCALE); + } + + /** Sets the moving speed of the creature animation. */ + protected void setMoveScale(double value) + { + setAttribute(KEY_MOVE_SCALE, value); + } + + /** Returns the selection circle size of the creature animation. */ + public int getEllipse() + { + return getAttribute(KEY_ELLIPSE); + } + + /** Sets the selection circle size of the creature animation. */ + public void setEllipse(int value) + { + if (getEllipse() != value) { + setAttribute(KEY_ELLIPSE, value); + selectionCircleChanged(); + } + } + + /** Returns the map space (in search map units) reserved exclusively for the creature animation*/ + public int getPersonalSpace() + { + return getAttribute(KEY_PERSONAL_SPACE); + } + + /** Sets the map space (in search map units) reserved exclusively for the creature animation*/ + public void setPersonalSpace(int value) + { + if (getPersonalSpace() != value) { + setAttribute(KEY_PERSONAL_SPACE, value); + personalSpaceChanged(); + } + } + + /** Returns the resref (prefix) for the associated animation files. */ + public String getAnimationResref() + { + return getAttribute(KEY_RESREF); + } + + /** Sets the resref (prefix) for the associated animation files. */ + protected void setAnimationResref(String resref) + { + setAttribute(KEY_RESREF, resref); + } + + /** Returns the replacement palette for the creature animation. Returns empty string if no replacement palette exists. */ + public String getNewPalette() + { + return getAttribute(KEY_NEW_PALETTE); + } + + /** Sets the replacement palette for the creature animation. */ + public void setNewPalette(String resref) + { + resref = (resref != null) ? resref.trim() : ""; + if (!getNewPalette().equalsIgnoreCase(resref)) { + setAttribute(KEY_NEW_PALETTE, resref); + paletteChanged(); + } + } + + /** Loads the replacement palette associated with the specified BAM resource. */ + protected int[] getNewPaletteData(ResourceEntry bamRes) + { + // Note: method argument is irrelevant for base implementation + return SpriteUtils.loadReplacementPalette(getNewPalette()); + } + + /** + * Returns whether blending mode "brightest" is enabled. + *

    Blending modes and their effects: + *

  • Brightest only: Use {@code GL_ONE_MINUS_DST_COLOR} + *
  • MultiplyBlend only: Use {@code GL_DST_COLOR} + *
  • Brightest and MultiplyBlend: Use {@code GL_SRC_COLOR} + */ + public boolean isBrightest() + { + return getAttribute(KEY_BRIGHTEST); + } + + /** Sets blending mode "brightest". */ + protected void setBrightest(boolean b) + { + setAttribute(KEY_BRIGHTEST, b); + } + + /** + * Returns whether blending mode "multiply_blend" is enabled. + *

    Blending modes and their effects: + *

  • Brightest only: Use {@code GL_ONE_MINUS_DST_COLOR} + *
  • MultiplyBlend only: Use {@code GL_DST_COLOR} + *
  • Brightest and MultiplyBlend: Use {@code GL_SRC_COLOR} + */ + public boolean isMultiplyBlend() + { + return getAttribute(KEY_MULTIPLY_BLEND); + } + + /** Sets blending mode "multiply_blend". */ + protected void setMultiplyBlend(boolean b) + { + setAttribute(KEY_MULTIPLY_BLEND, b); + } + + /** Returns whether sprite is affected by environmental lighting. */ + public boolean isLightSource() + { + return getAttribute(KEY_LIGHT_SOURCE); + } + + /** Sets whether sprite is affected by environmental lighting. */ + protected void setLightSource(boolean b) + { + setAttribute(KEY_LIGHT_SOURCE, b); + } + + /** Returns whether a red tint is applied to the creature if detected by infravision. */ + public boolean isDetectedByInfravision() + { + return getAttribute(KEY_DETECTED_BY_INFRAVISION); + } + + /** Sets whether a red tint is applied to the creature if detected by infravision. */ + protected void setDetectedByInfravision(boolean b) + { + setAttribute(KEY_DETECTED_BY_INFRAVISION, b); + } + + /** Returns whether palette range replacement is enabled. */ + public boolean isFalseColor() + { + return getAttribute(KEY_FALSE_COLOR); + } + + /** Sets whether palette range replacement is enabled. */ + protected void setFalseColor(boolean b) + { + setAttribute(KEY_FALSE_COLOR, b); + } + + /** Returns whether creature animation is translucent. */ + public boolean isTranslucent() + { + return getCreatureInfo().getEffectiveTranslucency() > 0; + } + + /** Sets whether creature animation is translucent. */ + protected void setTranslucent(boolean b) + { + setAttribute(KEY_TRANSLUCENT, b); + } + + /** Returns whether the blur effect is active for the creature animation. */ + public boolean isBlurred() + { + return getCreatureInfo().isBlurEffect(); + } + + /** Call this method whenever the visibility of the selection circle has been changed. */ + public void selectionCircleChanged() + { + // force recaching + imageCircle = null; + } + + /** Call this method whenever the visibility of personal space has been changed. */ + public void personalSpaceChanged() + { + // nothing to do + } + + /** Call this method whenever the visibility of any sprite types has been changed. */ + public void spriteChanged() + { + setAnimationChanged(); + } + + /** Call this method whenever the allegiance value has been changed. */ + public void allegianceChanged() + { + // force recaching + imageCircle = null; + } + + /** + * Call this method whenever the creature palette has changed. + * False color is processed by {@link #falseColorChanged()}. + */ + public void paletteChanged() + { + // Note: PST false color palette may also contain true color regions. + if (!isFalseColor() || + Profile.getGame() == Profile.Game.PSTEE || + Profile.getEngine() == Profile.Engine.PST) { + setAnimationChanged(); + } + } + + /** + * Call this method whenever the false color palette of the creature has changed. + * Conventional palette changes are processed by {@link #paletteChanged()}. */ + public void falseColorChanged() + { + if (isFalseColor()) { + setAnimationChanged(); + } + } + + /** This method reloads the creature animation if any relevant changes have been made. */ + public void applyAnimationChanges() + { + if (hasAnimationChanged()) { + resetAnimationChanged(); + try { + reset(); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + /** Returns whether changes to the creature animation should be applied immediately. */ + public boolean isAutoApplyChanges() + { + return autoApplyChanges; + } + + /** Sets whether changes to the creature animation should be applied immediately. */ + public void setAutoApplyChanges(boolean b) + { + if (autoApplyChanges != b) { + autoApplyChanges = b; + if (autoApplyChanges) { + applyAnimationChanges(); + } + } + } + + /** + * Returns whether a creature animation reload has been requested. + * Call {@link #applyAnimationChanges()} to apply the changes. + */ + public boolean hasAnimationChanged() + { + return animationChanged; + } + + /** Call to request a creature animation reload by the method {@link #applyAnimationChanges()}. */ + public void setAnimationChanged() + { + animationChanged = true; + if (isAutoApplyChanges()) { + applyAnimationChanges(); + } + } + + /** Call to cancel the request of a creature animation reload by the method {@link #applyAnimationChanges()}. */ + public void resetAnimationChanged() + { + animationChanged = false; + } + + @Override + public SpriteBamControl createControl() + { + return new SpriteBamControl(this); + } + + /** + * Returns the preferred compositor for rendering the sprite on the target surface. + */ + @Override + public Composite getComposite() + { + int blending = ((isBrightest() ? 1 : 0) << 0) | ((isMultiplyBlend() ? 1 : 0) << 1); + switch (blending) { + case 1: // brightest + return BlendingComposite.Brightest; + case 2: // multiply + return BlendingComposite.Multiply; + case 3: // brightest + multiply + return BlendingComposite.BrightestMultiply; + default: + return AlphaComposite.SrcOver; + } + } + + /** + * Returns the BAM cycle associated with the specified direction. + * Returns -1 if entry not found. + */ + public int getCycleIndex(Direction dir) + { + int retVal = -1; + Integer value = directionMap.get(dir); + if (value != null) { + retVal = value.intValue(); + } + + return retVal; + } + + /** + * Returns a copy of the map containing associations between animation directions and bam sequence numbers. + */ + public EnumMap getDirectionMap() + { + return directionMap.clone(); + } + + /** Creates the BAM structure for the creature animation. */ + protected abstract void init() throws Exception; + + /** + * Initializes general data for the creature animation. + * @param ini The INI map containing creature animation data. + */ + protected void initDefaults(IniMap ini) throws Exception + { + IniMapSection section = getGeneralIniSection(Objects.requireNonNull(ini, "INI object cannot be null")); + Objects.requireNonNull(section.getAsString("animation_type"), "animation_type required"); + Misc.requireCondition(getAnimationType().contains(getAnimationId()), + String.format("Animation slot (%04X) is not compatible with animation type (%s)", + getAnimationId(), getAnimationType().toString())); + + setMoveScale(section.getAsDouble("move_scale", 0.0)); + setEllipse(section.getAsInteger("ellipse", 16)); + setPersonalSpace(section.getAsInteger("personal_space", 3)); + setBrightest(section.getAsInteger("brightest", 0) != 0); + setMultiplyBlend(section.getAsInteger("multiply_blend", 0) != 0); + setLightSource(section.getAsInteger("light_source", 0) != 0); + + String s = section.getAsString("new_palette", ""); + setNewPalette(s); + + // getting first available "resref" definition + for (Iterator iter = getAnimationInfo().iterator(); iter.hasNext(); ) { + section = iter.next(); + s = section.getAsString("resref", ""); + if (!s.isEmpty()) { + setAnimationResref(s); + break; + } + } + Misc.requireCondition(!getAnimationResref().isEmpty(), "Animation resource prefix required"); + } + + /** + * Returns the general INI map section defined for all supported creature animation types from the specified + * {@code IniMap} instance. Returns an empty {@code IniMapSection} instance if section could not be determined. + */ + protected IniMapSection getGeneralIniSection(IniMap ini) + { + final String sectionName = "general"; + IniMapSection retVal = null; + if (ini != null) { + retVal = ini.getSection(sectionName); + } + + if (retVal == null) { + retVal = new IniMapSection(sectionName, 0, null); + } + + return retVal; + } + + /** + * Returns the INI map section responsible for animation-type-specific attributes. + * Returns an empty {@code IniMapSection} instance if section could not be determined. + */ + protected IniMapSection getSpecificIniSection() + { + IniMapSection retVal = null; + IniMap ini = getAnimationInfo(); + if (ini != null) { + retVal = ini.getSection(getAnimationSectionName()); + } + + if (retVal == null) { + retVal = new IniMapSection(getAnimationSectionName(), 0, null); + } + + return retVal; + } + + /** + * Assigns a cycle index to the specified BAM sequence and direction. + * @param seq the sequence type for identification purposes. + * @param dir the direction type + * @param cycleIndex the cycle index associated with the specified sequence and direction. + * @return The previous BAM cycle index if available. -1 otherwise. + */ + protected int addDirection(Direction dir, int cycleIndex) + { + int retVal = -1; + dir = Objects.requireNonNull(dir, "Creature direction required"); + Integer value = directionMap.get(dir); + if (value != null) { + retVal = value.intValue(); + } + directionMap.put(dir, cycleIndex); + + return retVal; + } + + /** + * Generates definitions for the specified animation sequence. + * @param seq the requested animation sequence. + * @return a fully initialized {@code SeqDef} object if sequence is supported, {@code null} otherwise. + */ + protected abstract SeqDef getSequenceDefinition(Sequence seq); + + /** + * Loads the specified animation sequence into the SpriteDecoder. + * @param seq the sequence to load. + * @throws NullPointerException if specified sequence is not available. + */ + protected void createSequence(Sequence seq) throws Exception + { + createSequence(seq, null); + } + + /** + * Loads the specified animation sequence into the SpriteDecoder. + * Only directions listed in the given {@code Direction} array will be considered. + * @param seq the sequence to load. + * @param directions an array of {@code Direction} values. Only directions listed in the array + * are considered by the creation process. Specify {@code null} to allow all directions. + * @throws NullPointerException if specified sequence is not available. + */ + protected void createSequence(Sequence seq, Direction[] directions) throws Exception + { + SeqDef sd = Objects.requireNonNull(getSequenceDefinition(seq), "Sequence not available: " + (seq != null ? seq : "(null)")); + if (directions == null) { + directions = Direction.values(); + } + createAnimation(sd, Arrays.asList(directions), FN_BEFORE_SRC_BAM, FN_BEFORE_SRC_FRAME, FN_AFTER_SRC_FRAME, FN_AFTER_DST_FRAME); + } + + /** Returns the number of sprite instances to render by the current blur state of the creature animation. */ + protected int getBlurInstanceCount() + { + return isBlurred() ? 4 : 1; + } + + protected Point getBlurInstanceShift(Point pt, Direction dir, int idx) + { + Point retVal = (pt != null) ? pt : new Point(); + if (isBlurred()) { + int dist = Math.max(0, idx) * 9; // distance between images: 9 pixels + // shift position depends on sprite direction and specified index + int dirValue = ((dir != null) ? dir.getValue() : 0) & ~1; // truncate to nearest semi-cardinal direction + switch (dirValue) { + case 2: // SW + retVal.x = dist / 2; + retVal.y = -dist / 2; + break; + case 4: // W + retVal.x = dist; + retVal.y = 0; + break; + case 6: // NW + retVal.x = dist / 2; + retVal.y = dist / 2; + break; + case 8: // N + retVal.x = 0; + retVal.y = dist; + break; + case 10: // NE + retVal.x = -dist / 2; + retVal.y = dist / 2; + break; + case 12: // E + retVal.x = -dist; + retVal.y = 0; + break; + case 14: // SE + retVal.x = -dist / 2; + retVal.y = -dist / 2; + break; + default: // S + retVal.x = 0; + retVal.y = -dist; + } + } else { + retVal.x = retVal.y = 0; + } + return retVal; + } + + protected void createAnimation(SeqDef definition, List directions, + BeforeSourceBam beforeSrcBam, + BeforeSourceFrame beforeSrcFrame, + AfterSourceFrame afterSrcFrame, + AfterDestFrame afterDstFrame) + { + PseudoBamControl dstCtrl = createControl(); + BamV1Control srcCtrl = null; + ResourceEntry entry = null; + definition = Objects.requireNonNull(definition, "Sequence definition cannot be null"); + + if (directions == null) { + directions = Arrays.asList(Direction.values()); + } + if (directions.isEmpty()) { + return; + } + + // Ensure that BeforeSourceBam function is applied only once per source BAM + HashSet bamControlSet = new HashSet<>(); + + for (final DirDef dd : definition.getDirections()) { + if (!directions.contains(dd.getDirection())) { + continue; + } + CycleDef cd = dd.getCycle(); + int cycleIndex = dstCtrl.cycleAdd(); + addDirection(dd.getDirection(), cycleIndex); + + cd.reset(); + int frameCount = cd.getMaximumFrames(); + final ArrayList frameInfo = new ArrayList<>(); + for (int frame = 0; frame < frameCount; frame++) { + frameInfo.clear(); + int copyCount = isBlurEnabled() ? getBlurInstanceCount() : 1; + Point centerShift = new Point(); + for (int copyIdx = copyCount - 1; copyIdx >= 0; copyIdx--) { + if (isBlurEnabled()) { + centerShift = getBlurInstanceShift(centerShift, dd.getDirection(), copyIdx); + } + for (final SegmentDef sd : cd.getCycles()) { + // checking visibility of sprite types + boolean skip = (sd.getSpriteType() == SegmentDef.SpriteType.AVATAR) && !getRenderAvatar(); + skip |= (sd.getSpriteType() == SegmentDef.SpriteType.WEAPON) && !getRenderWeapon(); + skip |= (sd.getSpriteType() == SegmentDef.SpriteType.SHIELD) && !getRenderShield(); + skip |= (sd.getSpriteType() == SegmentDef.SpriteType.HELMET) && !getRenderHelmet(); + if (skip) { + continue; + } + + entry = sd.getEntry(); + srcCtrl = Objects.requireNonNull(SpriteUtils.loadBamController(entry)); + srcCtrl.cycleSet(sd.getCycleIndex()); + + if (sd.getCurrentFrame() >= 0) { + if (beforeSrcBam != null && !bamControlSet.contains(srcCtrl)) { + bamControlSet.add(srcCtrl); + beforeSrcBam.accept(srcCtrl, sd); + } + frameInfo.add(new FrameInfo(srcCtrl, sd, centerShift)); + } + } + } + + for (final SegmentDef sd : cd.getCycles()) { + sd.advance(); + } + + int frameIndex = createFrame(frameInfo.toArray(new FrameInfo[frameInfo.size()]), beforeSrcFrame, afterSrcFrame); + if (afterDstFrame != null) { + afterDstFrame.accept(dd, frameIndex); + } + dstCtrl.cycleAddFrames(cycleIndex, new int[] {frameIndex}); + } + } + } + + /** + * Creates a single creature animation frame from the given array of source frame segments + * and adds it to the BAM frame list. Each source frame segment can be processed by the specified lambda function + * before it is drawn onto to the target frame. + * @param sourceFrames array of source frame segments to compose. + * @param beforeSrcFrame optional function that is executed before a source frame segment is drawn onto the + * target frame. + * @param afterSrcFrame optional method that is executed right after a source frame segment has been drawn onto the + * target frame. + * @return the absolute target BAM frame index. + */ + protected int createFrame(FrameInfo[] sourceFrames, BeforeSourceFrame beforeSrcFrame, AfterSourceFrame afterSrcFrame) + { + Rectangle rect; + if (Objects.requireNonNull(sourceFrames, "Source frame info objects required").length > 0) { + rect = SpriteUtils.getTotalFrameDimension(sourceFrames); + } else { + rect = new Rectangle(0, 0, 1, 1); + } + + // include personal space region in image size + rect = SpriteUtils.updateFrameDimension(rect, getPersonalSpaceSize(true)); + + // include selection circle in image size + float circleStrokeSize = getSelectionCircleStrokeSize(); + Dimension dim = getSelectionCircleSize(); + rect = SpriteUtils.updateFrameDimension(rect, new Dimension(2 * (dim.width + (int)circleStrokeSize), + 2 * (dim.height + (int)circleStrokeSize))); + + // creating target image + BufferedImage image; + if (rect.width > 0 && rect.height > 0) { + image = ColorConvert.createCompatibleImage(rect.width, rect.height, Transparency.TRANSLUCENT); + Graphics2D g = image.createGraphics(); + try { + g.setComposite(AlphaComposite.SrcOver); + g.setColor(new Color(0, true)); + g.fillRect(0, 0, image.getWidth(), image.getHeight()); + + // drawing source frames to target image + for (final FrameInfo fi : sourceFrames) { + BamV1Control ctrl = fi.getController(); + ctrl.cycleSet(fi.getCycle()); + int frameIdx = fi.getFrame(); + ctrl.cycleSetFrameIndex(frameIdx); + BufferedImage srcImage = (BufferedImage)ctrl.cycleGetFrame(); + if (beforeSrcFrame != null) { + srcImage = beforeSrcFrame.apply(fi.getSegmentDefinition(), srcImage, g); + } + FrameEntry entry = ctrl.getDecoder().getFrameInfo(ctrl.cycleGetFrameIndexAbsolute()); + int x = -rect.x - entry.getCenterX() + fi.getCenterShift().x; + int y = -rect.y - entry.getCenterY() + fi.getCenterShift().y; + + if (isBoundingBoxVisible() && entry.getWidth() > 2 && entry.getHeight() > 2) { + // drawing bounding box around sprite elements + Stroke oldStroke = g.getStroke(); + Color oldColor = g.getColor(); + Object oldHints = g.getRenderingHint(RenderingHints.KEY_ANTIALIASING); + try { + g.setStroke(FrameInfo.STROKE_BOUNDING_BOX); + g.setColor(FrameInfo.SPRITE_COLOR.getOrDefault(fi.getSegmentDefinition().getSpriteType(), FrameInfo.SPRITE_COLOR_DEFAULT)); + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF); + g.drawRect(x, y, entry.getWidth() - 1, entry.getHeight() - 1); + } finally { + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, (oldHints != null) ? oldHints : RenderingHints.VALUE_ANTIALIAS_DEFAULT); + if (oldColor != null) { + g.setColor(oldColor); + } + if (oldStroke != null) { + g.setStroke(oldStroke); + } + } + } + + g.drawImage(srcImage, x, y, entry.getWidth(), entry.getHeight(), null); + + if (afterSrcFrame != null) { + afterSrcFrame.accept(fi.getSegmentDefinition(), g); + } + ctrl = null; + } + } finally { + g.dispose(); + g = null; + } + } else { + // dummy graphics + image = ColorConvert.createCompatibleImage(1, 1, Transparency.TRANSLUCENT); + } + + // setting center point + int cx = -rect.x; + int cy = -rect.y; + + return frameAdd(image, new Point(cx, cy)); + } + + /** + * Calculates the total size of the personal space region. + * @param scaled whether dimension should be scaled according to search map unit size. + */ + protected Dimension getPersonalSpaceSize(boolean scaled) + { + int size = Math.max(0, (getPersonalSpace() - 1) | 1); + if (scaled) { + return new Dimension(size * 16, size * 12); + } else { + return new Dimension(size, size); + } + } + + /** + * Draws the personal space region onto the specified graphics object. + * @param g the {@code Graphics2D} instance of the image. + * @param center center position of the personal space. + * @param color the fill color of the drawn region. Specify {@code null} to use a default color. + * @param alpha alpha transparency in range [0.0, 1.0] where 0.0 is fully transparent (invisible) and 1.0 is fully opaque. + */ + protected void drawPersonalSpace(Graphics2D g, Point center, Color color, float alpha) + { + if (g != null) { + BufferedImage image = createPersonalSpace(color, alpha); + g.drawImage(image, center.x - (image.getWidth() / 2), center.y - (image.getHeight() / 2), null); + } + } + + /** Creates a bitmap with the personal space tiles. */ + protected BufferedImage createPersonalSpace(Color color, float alpha) + { + // preparations + if (color == null) { + color = new Color(224, 0, 224); + } + alpha = Math.max(0.0f, Math.min(1.0f, alpha)); // clamping alpha to [0.0, 1.0] + color = new Color(color.getRed(), color.getGreen(), color.getBlue(), (int)(255 * alpha)); + + // creating personal space pattern (unscaled) + Dimension dim = getPersonalSpaceSize(false); + if (dim.width == 0 || dim.height == 0) { + // personal space is not defined + return new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB); + } + BufferedImage image = new BufferedImage(dim.width, dim.height, BufferedImage.TYPE_INT_ARGB); + int[] bitmap = ((DataBufferInt)image.getRaster().getDataBuffer()).getData(); + int cx = dim.width / 2; + int cy = dim.height / 2; + int c = color.getRGB(); + float maxDist = (dim.width / 2.0f) * (dim.height / 2.0f); + for (int y = 0; y < dim.height; y++) { + for (int x = 0; x < dim.width; x++) { + int ofs = y * dim.width + x; + int dx = (cx - x) * (cx - x); + int dy = (cy - y) * (cy - y); + if (dx + dy < maxDist) { + bitmap[ofs] = c; + } + } + } + + // scaling up to search map unit size + dim = getPersonalSpaceSize(true); + BufferedImage retVal = new BufferedImage(dim.width, dim.height, image.getType()); + Graphics2D g = retVal.createGraphics(); + try { + g.setComposite(AlphaComposite.Src); + Object oldHints = g.getRenderingHint(RenderingHints.KEY_INTERPOLATION); + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); + g.drawImage(image, 0, 0, dim.width, dim.height, null); + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, + (oldHints != null) ? oldHints : RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); + } finally { + g.dispose(); + g = null; + } + + return retVal; + } + + /** Calculates the horizontal and vertical radius of the selection circle (ellipse). */ + protected Dimension getSelectionCircleSize() + { + Dimension dim = new Dimension(); + dim.width = Math.max(0, getEllipse()); + if (dim.width == 0) { + return dim; + } + if (isSelectionCircleBitmap()) { + dim.width += 2; // compensation for missing stroke size + } + dim.height = dim.width * 4 / 7; // ratio 1.75 + if (dim.height % 7 > 3) { + // rounding up + dim.height++; + } + return dim; + } + + /** Determines a circle stroke size relative to the circle size. Empty circles or bitmap circles have no stroke size. */ + protected float getSelectionCircleStrokeSize() + { + float circleStrokeSize = 0.0f; + if (!isSelectionCircleBitmap() && getEllipse() > 0) { + // thickness relative to circle size + circleStrokeSize = Math.max(1.0f, (float)(Math.floor(Math.sqrt(getEllipse()) / 2.0))); + } + + return circleStrokeSize; + } + + /** + * Draws a selection circle onto the specified graphics object. + * @param g the {@code Graphics2D} instance of the image. + * @param center center position of the circle. + * @param color the circle color. Specify {@code null} to use global defaults. + * @param strokeSize the thickness of the selection circle. + */ + protected void drawSelectionCircle(Graphics2D g, Point center, float strokeSize) + { + if (g != null) { + Dimension dim = getSelectionCircleSize(); + if (dim.width == 0 || dim.height == 0) { + return; + } + + Image image; + if (isSelectionCircleBitmap()) { + // fetching ornate selection circle image + image = getCreatureInfo().isStatusPanic() ? SpriteUtils.getAllegianceImage(-1) + : SpriteUtils.getAllegianceImage(getCreatureInfo().getAllegiance()); + } else { + if (imageCircle == null) { + // pregenerating circle graphics + int stroke = (int)Math.ceil(strokeSize); + imageCircle = ColorConvert.createCompatibleImage((dim.width + stroke) * 2 + 1, (dim.height + stroke) * 2 + 1, true); + Graphics2D g2 = imageCircle.createGraphics(); + try { + Color color = getCreatureInfo().isStatusPanic() ? SpriteUtils.getAllegianceColor(-1) + : SpriteUtils.getAllegianceColor(getCreatureInfo().getAllegiance()); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g2.setColor(color); + g2.setStroke(new BasicStroke(strokeSize)); + g2.drawOval((imageCircle.getWidth() / 2) - dim.width, + (imageCircle.getHeight() / 2) - dim.height, + 2 * dim.width, + 2 * dim.height); + } finally { + g2.dispose(); + g2 = null; + } + } + image = imageCircle; + // adjusting drawing size + dim.width = image.getWidth(null) / 2; + dim.height = image.getHeight(null) / 2; + } + + // drawing selection circle + Object oldHints = g.getRenderingHint(RenderingHints.KEY_INTERPOLATION); + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g.drawImage(image, center.x - dim.width, center.y - dim.height, 2 * dim.width, 2 * dim.height, null); + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, (oldHints != null) ? oldHints : RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); + } + } + + /** Returns whether creature shadow is semi-transparent. */ + protected boolean isTransparentShadow() + { + return transparentShadow; + } + + /** Sets whether creature shadow is semi-transparent. */ + protected void setTransparentShadow(boolean b) + { + if (transparentShadow != b) { + transparentShadow = b; + spriteChanged(); + } + } + + /** + * Translates the specified color location index into a palette color offset. + * @param locationIndex the location to translate. + * @return the resulting palette color offset. Returns -1 if location is not supported. + */ + protected int getColorOffset(int locationIndex) + { + int retVal = -1; + if (locationIndex >= 0 && locationIndex < 7) { + retVal = 4 + locationIndex * 12; + } + return retVal; + } + + /** + * Returns the palette data for the specified color entry. + * @param colorIndex the color entry. + * @return palette data as int array. Returns {@code null} if palette data could not be determined. + */ + protected int[] getColorData(int colorIndex, boolean allowRandom) + { + int[] retVal = null; + try { + retVal = SpriteUtils.getColorGradient(colorIndex, allowRandom); + } catch (Exception e) { + e.printStackTrace(); + } + return retVal; + } + + /** + * Replaces false colors with color ranges defined in the associated CRE resource. + * @param control the BAM controller. + */ + protected void applyFalseColors(BamV1Control control, SegmentDef sd) + { + if (control == null || sd == null) { + return; + } + + // preparations + final Map colorRanges = new HashMap<>(); + for (int loc = 0; loc < 7; loc++) { + int ofs = getColorOffset(loc); + Couple colorInfo = + getCreatureInfo().getEffectiveColorValue(sd.getSpriteType(), loc); + int colIdx = colorInfo.getValue0().intValue(); + boolean allowRandom = colorInfo.getValue1().booleanValue(); + if (ofs > 0 && colIdx >= 0) { + int[] range = getColorData(colIdx, allowRandom); + if (range != null) { + colorRanges.put(ofs, range); + } + } + } + + // Special: Off-hand weapon uses weapon colors + if (sd.getSpriteType() == SegmentDef.SpriteType.SHIELD) { + ItemInfo itemInfo = getCreatureInfo().getEquippedShield(); + if (itemInfo != null && itemInfo.getSlotType() == ItemInfo.SlotType.WEAPON) { + EffectInfo effectInfo = itemInfo.getEffectInfo(); + List fxList = effectInfo.getEffects(getCreatureInfo(), + SegmentDef.SpriteType.WEAPON, + EffectInfo.OPCODE_SET_COLOR); + for (final EffectInfo.Effect fx : fxList) { + int loc = fx.getParameter2() & 0xf; + int ofs = getColorOffset(loc); + if (ofs > 0) { + int[] range = getColorData(fx.getParameter1(), false); + if (range != null) { + colorRanges.put(ofs, range); + } + } + } + } + } + + // applying colors + int[] palette = control.getCurrentPalette(); + for (final Integer ofs : colorRanges.keySet()) { + // replacing base ranges + final int[] range = colorRanges.get(ofs); + palette = SpriteUtils.replaceColors(palette, range, ofs.intValue(), range.length, false); + } + + if (getAnimationType() != AnimationInfo.Type.MONSTER_PLANESCAPE) { + // preparing offset array + final int srcOfs = 4; + final int dstOfs = 88; + final int srcLen = 12; + final int dstLen = 8; + final int[] offsets = new int[colorRanges.size()]; + for (int i = 0; i < offsets.length; i++) { + offsets[i] = srcOfs + i * srcLen; + } + + // calculating mixed ranges + int k = 0; + for (int i = 0; i < offsets.length - 1; i++) { + int ofs1 = offsets[i]; + for (int j = i + 1; j < offsets.length; j++, k++) { + int ofs2 = offsets[j]; + int ofs3 = dstOfs + k * dstLen; + palette = SpriteUtils.interpolateColors(palette, ofs1, ofs2, srcLen, ofs3, dstLen, false); + } + } + + // fixing special palette entries + palette[2] = 0xFF000000; + palette[3] = 0xFF000000; + } + + control.setExternalPalette(palette); + } + + /** + * Replaces false colors with special color effects applied to the CRE resource. + * Currently supported: stoneskin/petrification, frozen state, burned state. + * @param control the BAM controller. + */ + protected void applyColorEffects(BamV1Control control, SegmentDef sd) + { + if (control == null || sd == null || + getAnimationType() == AnimationInfo.Type.MONSTER_PLANESCAPE) { + return; + } + + boolean isStoneEffect = getCreatureInfo().isStoneEffect(); + boolean isFrozenEffect = getCreatureInfo().isFrozenEffect(); + boolean isBurnedEffect = getCreatureInfo().isBurnedEffect(); + + if (isStoneEffect || isFrozenEffect) { + // isStoneEffect: includes stoneskin effect, petrification/stone death status + // isFrozenEffect: includes frozen death status + int colorIdx = isStoneEffect ? 72 : 71; + int[] range = getColorData(colorIdx, false); + int[] palette = control.getCurrentPalette(); + + // replacing base ranges + for (int i = 0; i < 7; i++) { + int ofs = 4 + (i * range.length); + palette = SpriteUtils.replaceColors(palette, range, ofs, range.length, false); + } + + // calculating mixed ranges + int k = 0; + for (int i = 0; i < 6; i++) { + int ofs1 = 4 + (i * 12); + for (int j = i + 1; j < 7; j++, k++) { + int ofs2 = 4 + (j * 12); + int ofs3 = 88 + (k * 8); + palette = SpriteUtils.interpolateColors(palette, ofs1, ofs2, 12, ofs3, 8, false); + } + } + + control.setExternalPalette(palette); + } else if (isBurnedEffect) { + // isBurnedEffect: includes flame death status + int opcode = 51; + int color = 0x4b4b4b; + int[] palette = control.getCurrentPalette(); + palette = SpriteUtils.tintColors(palette, 2, 254, opcode, color); + control.setExternalPalette(palette); + } + + } + + /** + * Modifies BAM palette with tint colors from selected effect opcodes. + * @param control the BAM controller. + */ + protected void applyColorTint(BamV1Control control, SegmentDef sd) + { + if (control == null || sd == null || + getAnimationType() == AnimationInfo.Type.MONSTER_PLANESCAPE) { + return; + } + + int[] palette = control.getCurrentPalette(); + Couple fullTint = Couple.with(-1, -1); // stores info for later + // color locations >= 0: affects only false color BAMs directly; data is stored for full palette tint though + for (int loc = 0; loc < 7; loc++) { + int ofs = getColorOffset(loc); + Couple colorInfo = getCreatureInfo().getEffectiveTintValue(sd.getSpriteType(), loc); + int opcode = colorInfo.getValue0().intValue(); + int color = colorInfo.getValue1().intValue(); + if (ofs > 0 && opcode >= 0 && color >= 0) { + // applying tint to color range + if (isFalseColor()) { + palette = SpriteUtils.tintColors(palette, ofs, 12, opcode, color); + } else { + fullTint.setValue0(colorInfo.getValue0()); + fullTint.setValue1(colorInfo.getValue1()); + } + } + } + + if (isFalseColor()) { + // preparing offset array + final int srcOfs = 4; + final int dstOfs = 88; + final int srcLen = 12; + final int dstLen = 8; + final int[] offsets = new int[7]; + for (int i = 0; i < offsets.length; i++) { + offsets[i] = srcOfs + i * srcLen; + } + + // calculating mixed ranges + int k = 0; + for (int i = 0; i < offsets.length - 1; i++) { + int ofs1 = offsets[i]; + for (int j = i + 1; j < offsets.length; j++, k++) { + int ofs2 = offsets[j]; + int ofs3 = dstOfs + k * dstLen; + palette = SpriteUtils.interpolateColors(palette, ofs1, ofs2, srcLen, ofs3, dstLen, false); + } + } + } + + // color location -1: affects whole palette (except transparency and shadow color) + Couple colorInfo = getCreatureInfo().getEffectiveTintValue(sd.getSpriteType(), -1); + if (colorInfo.getValue0() >= 0 && colorInfo.getValue1() >= 0) { + fullTint.setValue0(colorInfo.getValue0()); + fullTint.setValue1(colorInfo.getValue1()); + } + int opcode = fullTint.getValue0().intValue(); + int color = fullTint.getValue1().intValue(); + if (opcode >= 0 && color >= 0) { + // applying tint to whole palette + palette = SpriteUtils.tintColors(palette, 2, 254, opcode, color); + } + + control.setExternalPalette(palette); + } + + /** + * The specified frame is mirrored horizontally. Both pixel data and center point are adjusted. + * @param frameIndex absolute frame index in the BAM frame list. + */ + protected void flipImageHorizontal(int frameIndex) + { + PseudoBamFrameEntry frame = getFrameInfo(frameIndex); + // flipping image horizontally + BufferedImage image = frame.getFrame(); + AffineTransform at = AffineTransform.getScaleInstance(-1, 1); + at.translate(-image.getWidth(), 0); + AffineTransformOp op = new AffineTransformOp(at, AffineTransformOp.TYPE_NEAREST_NEIGHBOR); + image = op.filter(image, null); + // updating frame data + frame.setFrame(image); + frame.setCenterX(frame.getWidth() - frame.getCenterX() - 1); + } + + /** + * Applies translucency to the specified paletted image. + * @param control the BAM controller. + * @param minTranslucency the minimum amount of translucency to apply. + */ + protected void applyTranslucency(BamV1Control control, int minTranslucency) + { + if (control != null) { + int alpha = minTranslucency; + if (isTranslucencyEnabled()) { + int value = getCreatureInfo().getEffectiveTranslucency(); + if (value > 0) { + alpha = Math.min(alpha, value); + } + } + int[] palette = control.getCurrentPalette(); + + // shadow color (alpha relative to semi-transparency of shadow) + int alphaShadow = 255 - (palette[1] >>> 24); + alphaShadow = alpha * alphaShadow / 255; + alphaShadow <<= 24; // setting alpha mask + palette[1] = alphaShadow | (palette[1] & 0x00ffffff); + + // creature colors + alpha <<= 24; // setting alpha mask + for (int i = 2; i < palette.length; i++) { + palette[i] = alpha | (palette[i] & 0x00ffffff); + } + + control.setExternalPalette(palette); + } + } + + /** + * Returns whether the current CRE resource provides an animation that is compatible with the + * {@code SpriteDecoder} class. + * @return {@code true} if the animation type of the CRE is compatible with this {@code SpriteDecoder} instance. + * Returns {@code false} otherwise. + */ + protected boolean isMatchingAnimationType() + { + boolean retVal = false; + + List names = getAnimationFiles(true); + if (!names.isEmpty()) { + retVal = names.parallelStream().allMatch(ResourceFactory::resourceExists); + } + + return retVal; + } + + @Override + public int hashCode() + { + int hash = super.hashCode(); + hash = 31 * hash + ((creInfo == null) ? 0 : creInfo.hashCode()); + hash = 31 * hash + ((ini == null) ? 0 : ini.hashCode()); + hash = 31 * hash + ((directionMap == null) ? 0 : directionMap.hashCode()); + hash = 31 * hash + ((attributesMap == null) ? 0 : attributesMap.hashCode()); + hash = 31 * hash + ((currentSequence == null) ? 0 : currentSequence.hashCode()); + hash = 31 * hash + Boolean.valueOf(showCircle).hashCode(); + hash = 31 * hash + Boolean.valueOf(selectionCircleBitmap).hashCode(); + hash = 31 * hash + Boolean.valueOf(showPersonalSpace).hashCode(); + hash = 31 * hash + Boolean.valueOf(showBoundingBox).hashCode(); + hash = 31 * hash + Boolean.valueOf(transparentShadow).hashCode(); + hash = 31 * hash + Boolean.valueOf(translucencyEnabled).hashCode(); + hash = 31 * hash + Boolean.valueOf(tintEnabled).hashCode(); + hash = 31 * hash + Boolean.valueOf(blurEnabled).hashCode(); + hash = 31 * hash + Boolean.valueOf(paletteReplacementEnabled).hashCode(); + hash = 31 * hash + Boolean.valueOf(renderSpriteAvatar).hashCode(); + hash = 31 * hash + Boolean.valueOf(renderSpriteWeapon).hashCode(); + hash = 31 * hash + Boolean.valueOf(renderSpriteHelmet).hashCode(); + hash = 31 * hash + Boolean.valueOf(renderSpriteShield).hashCode(); + hash = 31 * hash + Boolean.valueOf(animationChanged).hashCode(); + hash = 31 * hash + Boolean.valueOf(autoApplyChanges).hashCode(); + return hash; + } + + @Override + public boolean equals(Object o) + { + if (!(o instanceof SpriteDecoder)) { + return false; + } + boolean retVal = super.equals(o); + if (retVal) { + SpriteDecoder other = (SpriteDecoder)o; + retVal &= (this.creInfo == null && other.creInfo == null) || + (this.creInfo != null && this.creInfo.equals(other.creInfo)); + retVal &= (this.ini == null && other.ini == null) || + (this.ini != null && this.ini.equals(other.ini)); + retVal &= (this.directionMap == null && other.directionMap == null) || + (this.directionMap != null && this.directionMap.equals(other.directionMap)); + retVal &= (this.attributesMap == null && other.attributesMap == null) || + (this.attributesMap != null && this.attributesMap.equals(other.attributesMap)); + retVal &= (this.currentSequence == null && other.currentSequence == null) || + (this.currentSequence != null && this.currentSequence.equals(other.currentSequence)); + retVal &= (this.showCircle == other.showCircle); + retVal &= (this.selectionCircleBitmap == other.selectionCircleBitmap); + retVal &= (this.showPersonalSpace == other.showPersonalSpace); + retVal &= (this.showBoundingBox == other.showBoundingBox); + retVal &= (this.transparentShadow == other.transparentShadow); + retVal &= (this.translucencyEnabled == other.translucencyEnabled); + retVal &= (this.tintEnabled == other.tintEnabled); + retVal &= (this.blurEnabled == other.blurEnabled); + retVal &= (this.paletteReplacementEnabled == other.paletteReplacementEnabled); + retVal &= (this.renderSpriteAvatar == other.renderSpriteAvatar); + retVal &= (this.renderSpriteWeapon == other.renderSpriteWeapon); + retVal &= (this.renderSpriteHelmet == other.renderSpriteHelmet); + retVal &= (this.renderSpriteShield == other.renderSpriteShield); + retVal &= (this.animationChanged == other.animationChanged); + retVal &= (this.autoApplyChanges == other.autoApplyChanges); + } + return retVal; + } + +//-------------------------- INNER CLASSES -------------------------- + + /** + * Specialized controller for creature animations. + */ + public static class SpriteBamControl extends PseudoBamControl + { + protected SpriteBamControl(SpriteDecoder decoder) + { + super(decoder); + } + + @Override + public SpriteDecoder getDecoder() + { + return (SpriteDecoder)super.getDecoder(); + } + + /** + * A convenience method that draws personal space marker and/or selection circle onto the canvas specified by the + * {@code Graphics2D} instance based on current decoder settings. + * Frame dimension and center position are taken into account and based on the currently selected cycle frame. + * @param g the {@code Graphics2D} instance used to render the graphics. + * @param offset amount of pixels to move the center point by. Specify {@code null} for no change. + */ + public void getVisualMarkers(Graphics2D g, Point offset) + { + getVisualMarkers(g, offset, cycleGetFrameIndex(), getDecoder().isPersonalSpaceVisible(), + getDecoder().isSelectionCircleEnabled()); + } + + /** + * A convenience method that draws personal space marker and/or selection circle onto the canvas specified by the + * {@code Graphics2D} instance. + * Frame dimension and center position are taken into account and based on the currently selected cycle frame. + * @param g the {@code Graphics2D} instance used to render the graphics. + * @param offset amount of pixels to move the center point by. Specify {@code null} for no change. + * @param drawPersonalSpace whether to draw the personal space marker. + * @param drawSelectionCircle whether to draw the selection circle. + */ + public void getVisualMarkers(Graphics2D g, Point offset, boolean drawPersonalSpace, boolean drawSelectionCircle) + { + getVisualMarkers(g, offset, cycleGetFrameIndex(), drawPersonalSpace, drawSelectionCircle); + } + + /** + * A convenience method that draws personal space marker and/or selection circle onto the canvas specified by the + * {@code Graphics2D} instance based on current decoder settings. + * Frame dimension and center position are taken into account by the specified relative frame index. + * @param g the {@code Graphics2D} instance used to render the graphics. + * @param offset amount of pixels to move the center point by. Specify {@code null} for no change. + * @param frameIdx the frame index relative to current cycle. + */ + public void getVisualMarkers(Graphics2D g, Point offset, int frameIdx) + { + getVisualMarkers(g, offset, frameIdx, getDecoder().isPersonalSpaceVisible(), + getDecoder().isSelectionCircleEnabled()); + } + + /** + * A convenience method that draws personal space marker and/or selection circle onto the canvas specified by the + * {@code Graphics2D} instance. + * Frame dimension and center position are taken into account by the specified relative frame index. + * @param g the {@code Graphics2D} instance used to render the graphics. + * @param offset amount of pixels to move the center point by. Specify {@code null} for no change. + * @param frameIdx the frame index relative to current cycle. + * @param drawPersonalSpace whether to draw the personal space marker. + * @param drawSelectionCircle whether to draw the selection circle. + */ + public void getVisualMarkers(Graphics2D g, Point offset, int frameIdx, + boolean drawPersonalSpace, boolean drawSelectionCircle) + { + if (g == null || (drawPersonalSpace == false && drawSelectionCircle == false)) { + return; + } + + frameIdx = cycleGetFrameIndexAbsolute(frameIdx); + if (frameIdx < 0) { + return; + } + + // getting frame dimension and center + PseudoBamFrameEntry entry = getDecoder().getFrameInfo(frameIdx); + Point center; + int w, h; + if (getMode() == BamDecoder.BamControl.Mode.SHARED) { + updateSharedBamSize(); + Dimension d = getSharedDimension(); + center = getSharedOrigin(); + center.x = -center.x; + center.y = -center.y; + w = d.width; + h = d.height; + } else { + w = entry.getWidth(); + h = entry.getHeight(); + center = new Point(entry.getCenterX(), entry.getCenterY()); + } + + if (w <= 0 || h <= 0) { + return; + } + + if (offset != null) { + center.x += offset.x; + center.y += offset.y; + } + + // drawing markers + Composite comp = g.getComposite(); + g.setComposite(AlphaComposite.SrcOver); + if (drawPersonalSpace) { + getDecoder().drawPersonalSpace(g, center, null, 0.5f); + } + if (drawSelectionCircle) { + getDecoder().drawSelectionCircle(g, center, getDecoder().getSelectionCircleStrokeSize()); + } + if (comp != null) { + g.setComposite(comp); + } + } + } + + + /** + * Represents an operation that is called once per source BAM resource when creating a creature animation. + */ + public interface BeforeSourceBam + { + /** + * Performs this operation on the given arguments. + * @param control the {@code BamV1Control} instance of the source BAM + * @param sd the {@code SegmentDef} instance describing the source BAM. + */ + void accept(BamV1Control control, SegmentDef sd); + } + + /** + * Represents a function that is called for each source frame before it is drawn onto the destination image. + */ + public interface BeforeSourceFrame + { + /** + * Performs this function on the given arguments. + * @param sd the {@code SegmentDef} structure describing the given source frame. + * @param srcImage {@code BufferedImage} object of the the source frame + * @param g the {@code Graphics2D} object of the destination image. + * @return the updated source frame image + */ + BufferedImage apply(SegmentDef sd, BufferedImage srcImage, Graphics2D g); + } + + /** + * Represents an operation that is called for each source frame after it has been drawn onto the destination image. + * It can be used to clean up modifications made to the {@code Graphics2D} instance + * in the {@code BeforeSourceFrame} function. + */ + public interface AfterSourceFrame + { + /** + * Performs this operation on the given arguments. + * @param sd the {@code SegmentDef} structure describing the current source frame. + * @param g the {@code Graphics2D} object of the destination image. + */ + void accept(SegmentDef sd, Graphics2D g); + } + + /** + * Represents an operation that is called for each destination frame after it has been created. + */ + public interface AfterDestFrame + { + /** + * Performs this operation on the given arguments. + * @param dd the {@code DirDef} object defining the current destination cycle + * @param frameIdx the absolute destination BAM frame index. + */ + void accept(DirDef dd, int frameIdx); + } +} diff --git a/src/org/infinity/resource/cre/decoder/TownStaticDecoder.java b/src/org/infinity/resource/cre/decoder/TownStaticDecoder.java new file mode 100644 index 000000000..3cba8d64b --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/TownStaticDecoder.java @@ -0,0 +1,129 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.decoder; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import org.infinity.resource.ResourceFactory; +import org.infinity.resource.cre.CreResource; +import org.infinity.resource.cre.decoder.tables.SpriteTables; +import org.infinity.resource.cre.decoder.util.AnimationInfo; +import org.infinity.resource.cre.decoder.util.DecoderAttribute; +import org.infinity.resource.cre.decoder.util.SegmentDef; +import org.infinity.resource.cre.decoder.util.SeqDef; +import org.infinity.resource.cre.decoder.util.Sequence; +import org.infinity.resource.cre.decoder.util.SpriteUtils; +import org.infinity.resource.key.ResourceEntry; +import org.infinity.util.IniMap; +import org.infinity.util.IniMapSection; + +/** + * Creature animation decoder for processing type 4000 (town_static) animations. + * Available ranges: [4000,4fff] + */ +public class TownStaticDecoder extends SpriteDecoder +{ + /** The animation type associated with this class definition. */ + public static final AnimationInfo.Type ANIMATION_TYPE = AnimationInfo.Type.TOWN_STATIC; + + public static final DecoderAttribute KEY_CAN_LIE_DOWN = DecoderAttribute.with("can_lie_down", DecoderAttribute.DataType.BOOLEAN); + + /** Mapping between sequence and cycle index. */ + private static final HashMap cycleMap = new HashMap() {{ + put(Sequence.STANCE, 0); + put(Sequence.STAND, 16); + put(Sequence.GET_HIT, 32); + put(Sequence.DIE, 48); + put(Sequence.SLEEP, 48); + put(Sequence.GET_UP, 48); + put(Sequence.TWITCH, 64); + }}; + + /** + * A helper method that parses the specified data array and generates a {@link IniMap} instance out of it. + * @param data a String array containing table values for a specific table entry. + * @return a {@code IniMap} instance with the value derived from the specified data array. + * Returns {@code null} if no data could be derived. + */ + public static IniMap processTableData(String[] data) + { + IniMap retVal = null; + if (data == null) { + return retVal; + } + + String resref = SpriteTables.valueToString(data, SpriteTables.COLUMN_RESREF, ""); + if (resref.isEmpty()) { + return retVal; + } + int falseColor = SpriteTables.valueToInt(data, SpriteTables.COLUMN_CLOWN, 0); + + List lines = SpriteUtils.processTableDataGeneral(data, ANIMATION_TYPE); + lines.add("[town_static]"); + lines.add("false_color=" + falseColor); + lines.add("resref=" + resref); + + retVal = IniMap.from(lines); + + return retVal; + } + + public TownStaticDecoder(int animationId, IniMap ini) throws Exception + { + super(ANIMATION_TYPE, animationId, ini); + } + + public TownStaticDecoder(CreResource cre) throws Exception + { + super(ANIMATION_TYPE, cre); + } + + /** Returns whether the creature falls down when dead/unconscious. */ + public boolean canLieDown() { return getAttribute(KEY_CAN_LIE_DOWN); } + protected void setCanLieDown(boolean b) { setAttribute(KEY_CAN_LIE_DOWN, b); } + + @Override + public List getAnimationFiles(boolean essential) + { + ArrayList retVal = new ArrayList<>(); + retVal.add(getAnimationResref() + ".BAM"); + return retVal; + } + + @Override + public boolean isSequenceAvailable(Sequence seq) + { + return (getSequenceDefinition(seq) != null); + } + + @Override + protected void init() throws Exception + { + // setting properties + initDefaults(getAnimationInfo()); + IniMapSection section = getSpecificIniSection(); + setFalseColor(section.getAsInteger(KEY_FALSE_COLOR.getName(), 0) != 0); + setCanLieDown(section.getAsInteger(KEY_CAN_LIE_DOWN.getName(), 0) != 0); + } + + @Override + protected SeqDef getSequenceDefinition(Sequence seq) + { + SeqDef retVal = null; + if (!cycleMap.containsKey(seq)) { + return retVal; + } + + SegmentDef.Behavior behavior = (seq == Sequence.GET_UP) ? SegmentDef.getBehaviorOf("!") : SegmentDef.getBehaviorOf(""); + int cycle = cycleMap.getOrDefault(seq, 0); + ResourceEntry entry = ResourceFactory.getResourceEntry(getAnimationResref() + ".BAM"); + if (SpriteUtils.bamCyclesExist(entry, cycle, SeqDef.DIR_FULL.length)) { + retVal = SeqDef.createSequence(seq, SeqDef.DIR_FULL, false, entry, cycle, null, behavior); + } + return retVal; + } +} diff --git a/src/org/infinity/resource/cre/decoder/placeholder.bam b/src/org/infinity/resource/cre/decoder/placeholder.bam new file mode 100644 index 000000000..b8f107ec8 Binary files /dev/null and b/src/org/infinity/resource/cre/decoder/placeholder.bam differ diff --git a/src/org/infinity/resource/cre/decoder/tables/InfinityTables.java b/src/org/infinity/resource/cre/decoder/tables/InfinityTables.java new file mode 100644 index 000000000..085986050 --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/tables/InfinityTables.java @@ -0,0 +1,257 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.decoder.tables; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Objects; + +import org.infinity.resource.Profile; +import org.infinity.resource.cre.decoder.SpriteDecoder; +import org.infinity.resource.cre.decoder.util.AnimationInfo; +import org.infinity.resource.cre.decoder.util.SpriteUtils; +import org.infinity.resource.key.ResourceEntry; +import org.infinity.util.IdsMap; +import org.infinity.util.IdsMapEntry; +import org.infinity.util.IniMap; +import org.infinity.util.Misc; + +/** + * A static class dedicated to processing Infinity Animation slots. + */ +public class InfinityTables +{ + private static final String[] TABLE_INFINITY_ANIMATIONS = { "", // not installed + "infinityanimations-v5.ids", // IA v5 or earlier + "infinityanimations-v6.ids", // IA v6 or later + }; + + /** + * Creates a creature animation INI definition from the Infinity Animations table. + * @param animationId the creature animation id + * @return a list of {@link IniMap} instances containing animation information. + * Returns an empty {@code IniMap} list if no information could be determined. + */ + public static List createIniMaps(int animationId) + { + List retVal = new ArrayList<>(); + + // determine correct IA version + int ver = Profile.getProperty(Profile.Key.GET_INFINITY_ANIMATIONS).intValue(); + if (ver < 1 || ver >= TABLE_INFINITY_ANIMATIONS.length) { + return retVal; + } + + ResourceEntry entry = SpriteTables.getTableResource(TABLE_INFINITY_ANIMATIONS[ver]); + if (entry == null) { + return retVal; + } + + IdsMap table = new IdsMap(entry); + retVal.addAll(processTable(table, animationId)); + + return retVal; + } + + /** Processes table data. */ + private static List processTable(IdsMap idsMap, int animationId) + { + List retVal = new ArrayList<>(); + if (idsMap == null) { + return retVal; + } + + // finding entry matching the specified animation id + animationId &= 0xffff; + IdsMapEntry idsEntry = idsMap.get(animationId); + if (idsEntry == null) { + return retVal; + } + + try { + // transforming IA table entry into internal table entry + String[] data = parseTableEntry(animationId, idsEntry.getSymbol()); + + // determining SpriteDecoder class instance + int classType = SpriteTables.valueToInt(data, SpriteTables.COLUMN_TYPE, -1); + AnimationInfo.Type animType = AnimationInfo.Type.values()[classType]; + Class cls = SpriteUtils.getSpriteDecoderClass(animType); + if (cls != null) { + // calling method of signature: public static IniMap processTableData(String[] data); + Method method = cls.getMethod("processTableData", String[].class); + Object o = method.invoke(null, new Object[] { data }); + if (o instanceof IniMap) { + retVal.add((IniMap)o); + } + } + } catch (InvocationTargetException ite) { + if (ite.getCause() != null) { + ite.getCause().printStackTrace(); + } else { + ite.printStackTrace(); + } + } catch (Exception e) { + return retVal; + } + + + + return retVal; + } + + private static String[] parseTableEntry(int animationId, String entry) throws Exception + { + final String[] retVal = new String[SpriteTables.NUM_COLUMNS]; + + String[] items = Objects.requireNonNull(entry).split("\\s+"); + Misc.requireCondition(items.length > 5, "Infinity Animations table entry: too few entries"); + + String prefix = items[0].trim(); + Misc.requireCondition(prefix.length() > 0, "Animation prefix not available"); + + int space = Misc.toNumber(SpriteTables.valueToString(items, 3, "3"), 16, 3); + Misc.requireCondition(space > 0, "Invalid personal space: " + space); + int ellipse; + if (space <= 3) { + ellipse = 16; + } else if (space <= 5) { + ellipse = 24; + } else if (space <= 10) { + ellipse = 64; + } else { + ellipse = 72; + } + +// char type = SpriteTables.valueToString(items, 4, " ").charAt(0); + + final HashMap animationTypes = new HashMap() {{ + put("BGI MONSTER LONG 4 PART", AnimationInfo.Type.MONSTER_QUADRANT); + put("DRAGONS", AnimationInfo.Type.MONSTER_MULTI); + put("BGII SPLIT 4 PART", AnimationInfo.Type.MONSTER_MULTI_NEW); + put("BGI SIMPLE CASTER", AnimationInfo.Type.MONSTER_LAYERED_SPELL); + put("BROKEN ANKHEG", AnimationInfo.Type.MONSTER_ANKHEG); + put("CHARACTER BGII", AnimationInfo.Type.CHARACTER); + put("CHARACTER BGI", AnimationInfo.Type.CHARACTER_OLD); + put("BGII UNSPLIT EXT.", AnimationInfo.Type.MONSTER); + put("BGII SPLIT", AnimationInfo.Type.MONSTER); + put("BGI SIMPLE MONSTER", AnimationInfo.Type.MONSTER_OLD); + put("BGI MONSTER LONG", AnimationInfo.Type.MONSTER_LARGE_16); + put("IWD", AnimationInfo.Type.MONSTER_ICEWIND); + }}; + + String typeString = null; + AnimationInfo.Type animType = null; + for (int len = items.length - 5; len > 0 && animType == null; len--) { + typeString = concatItems(items, 5, len); + animType = animationTypes.get(typeString); + } + + int type = -1; + for (int i = AnimationInfo.Type.values().length - 1; i >= 0; i--) { + if (AnimationInfo.Type.values()[i] == animType) { + type = i; + break; + } + } + + if (type < 0) { + throw new Exception("Could not determine animation type"); + } + + int clown = SpriteTables.valueToString(items, items.length - 1, "unpaletted").equalsIgnoreCase("paletted") ? 1 : 0; + + int split = -1; + String height = ""; + String heightShield = ""; + switch (animType) { + case MONSTER_MULTI: + case MONSTER_MULTI_NEW: + split = 1; + break; + case MONSTER_LAYERED_SPELL: + { + String s = SpriteTables.valueToString(items, 8, ""); + if ("(BOW)".equalsIgnoreCase(s)) { + heightShield = "BW"; + } else if ("(S1)".equalsIgnoreCase(s)) { + height = "S1"; + } + break; + } + case CHARACTER: + case CHARACTER_OLD: + { + split = 0; + String s = SpriteTables.valueToString(items, 7, ""); + if (s.length() == 3) { + // height code specified + height = s; + } else { + // height code derived from prefix + height = prefix.substring(1); + } + break; + } + case MONSTER: + { + switch (typeString) { + case "BGII UNSPLIT EXT.": + split = 0; + break; + case "BGII SPLIT": + split = 1; + break; + } + break; + } + default: + } + + Arrays.fill(retVal, ""); + retVal[SpriteTables.COLUMN_ID] = String.format("0x%04x", animationId); + retVal[SpriteTables.COLUMN_RESREF] = prefix; + retVal[SpriteTables.COLUMN_TYPE] = Integer.toString(type); + retVal[SpriteTables.COLUMN_ELLIPSE] = Integer.toString(ellipse); + retVal[SpriteTables.COLUMN_SPACE] = Integer.toString(space); + retVal[SpriteTables.COLUMN_BLENDING] = "0"; + if (clown >= 0) { + retVal[SpriteTables.COLUMN_CLOWN] = Integer.toString(clown); + } + if (split >= 0) { + retVal[SpriteTables.COLUMN_SPLIT] = Integer.toString(split); + } + retVal[SpriteTables.COLUMN_HEIGHT] = height; + retVal[SpriteTables.COLUMN_HEIGHT_SHIELD] = heightShield; + + return retVal; + } + + /** Concatenates the specified strings to a single string, separated by a single space. */ + private static String concatItems(String[] data, int idx, int len) + { + StringBuilder sb = new StringBuilder(); + if (data == null || idx < 0 || idx >= data.length || len <= 0) { + return sb.toString(); + } + + if (data[idx] != null) { + sb.append(data[idx]); + } + + for (int i = 1; i < len; i++) { + if (data[idx + i] != null) { + sb.append(' ').append(data[idx + i]); + } + } + + return sb.toString(); + } + + private InfinityTables() { } +} diff --git a/src/org/infinity/resource/cre/decoder/tables/SpriteTables.java b/src/org/infinity/resource/cre/decoder/tables/SpriteTables.java new file mode 100644 index 000000000..668090836 --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/tables/SpriteTables.java @@ -0,0 +1,410 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.decoder.tables; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.EnumMap; +import java.util.List; + +import org.infinity.resource.Profile; +import org.infinity.resource.cre.decoder.MonsterPlanescapeDecoder; +import org.infinity.resource.cre.decoder.SpriteDecoder; +import org.infinity.resource.cre.decoder.util.SpriteUtils; +import org.infinity.resource.key.BufferedResourceEntry; +import org.infinity.resource.key.ResourceEntry; +import org.infinity.util.IniMap; +import org.infinity.util.Table2da; + +/** + * A static class that provides information and methods for hardcoded creature animations. + */ +public class SpriteTables +{ + // Number of non-PST table columns (id column included) + public static final int NUM_COLUMNS = 16; + // Number of PST table columns (id column included) + public static final int NUM_COLUMNS_PST = 9; + + // Header column index + public static final int COLUMN_ID = 0; // int (hex/composite) + // Column indices for non-PST sprite tables + public static final int COLUMN_RESREF = 1; // string + public static final int COLUMN_TYPE = 2; // int + public static final int COLUMN_ELLIPSE = 3; // int + public static final int COLUMN_SPACE = 4; // int + public static final int COLUMN_BLENDING = 5; // int (bitfield) + public static final int COLUMN_PALETTE = 6; // string + public static final int COLUMN_PALETTE2 = 7; // string + public static final int COLUMN_RESREF2 = 8; // string + public static final int COLUMN_TRANSLUCENT = 9; // bool + public static final int COLUMN_CLOWN = 10; // bool + public static final int COLUMN_SPLIT = 11; // bool + public static final int COLUMN_HELMET = 12; // bool + public static final int COLUMN_WEAPON = 13; // bool + public static final int COLUMN_HEIGHT = 14; // string + public static final int COLUMN_HEIGHT_SHIELD = 15; // string + // Column indices for PST-related sprite tables + public static final int COLUMN_PST_RESREF = 1; // string + public static final int COLUMN_PST_RESREF2 = 2; // string + public static final int COLUMN_PST_TYPE = 3; // int + public static final int COLUMN_PST_ELLIPSE = 4; // int + public static final int COLUMN_PST_SPACE = 5; // int + public static final int COLUMN_PST_CLOWN = 6; // int + public static final int COLUMN_PST_ARMOR = 7; // int + public static final int COLUMN_PST_BESTIARY = 8; // int + + private static final EnumMap> TableMaps = new EnumMap>(Profile.Game.class) {{ + put(Profile.Game.BG1, Arrays.asList("avatars-bg1.2da")); + put(Profile.Game.BG1TotSC, get(Profile.Game.BG1)); + + put(Profile.Game.IWD, Arrays.asList("avatars-iwd.2da")); + put(Profile.Game.IWDHoW, Arrays.asList("avatars-iwdhow.2da", "avatars-iwd.2da")); + put(Profile.Game.IWDHowTotLM, get(Profile.Game.IWDHoW)); + + put(Profile.Game.IWD2, Arrays.asList("avatars-iwd2.2da")); + + put(Profile.Game.PST, Arrays.asList("avatars-pst.2da")); + + put(Profile.Game.BG2SoA, Arrays.asList("avatars-bg2soa.2da")); + put(Profile.Game.BG2ToB, Arrays.asList("avatars-bg2tob.2da", "avatars-bg2soa.2da")); + put(Profile.Game.Tutu, get(Profile.Game.BG2ToB)); + put(Profile.Game.BGT, get(Profile.Game.BG2ToB)); + put(Profile.Game.Unknown, get(Profile.Game.BG2ToB)); + + put(Profile.Game.BG1EE, Arrays.asList("avatars-bgee.2da", "avatars-bg2ee.2da", "avatars-bg2tob.2da")); + put(Profile.Game.BG1SoD, get(Profile.Game.BG1EE)); + put(Profile.Game.BG2EE, Arrays.asList("avatars-bg2ee.2da", "avatars-bgee.2da", "avatars-bg2tob.2da")); + put(Profile.Game.EET, Arrays.asList("avatars-eet.2da", "avatars-bgee.2da", "avatars-bg2ee.2da", "avatars-bg2tob.2da")); + + put(Profile.Game.IWDEE, Arrays.asList("avatars-iwdee.2da", "avatars-bgee.2da", "avatars-bg2ee.2da")); + + put(Profile.Game.PSTEE, Arrays.asList("avatars-pstee.2da")); + }}; + + + /** + * Creates a creature animation INI definition from the table associated with the specified animation id. + * @param animationId the creature animation id + * @return a list of {@link IniMap} instances containing animation information. + * Returns an empty {@code IniMap} list if no information could be determined. + */ + public static List createIniMaps(int animationId) + { + return createIniMaps(Profile.getGame(), animationId); + } + + /** + * Creates a creature animation INI definition from the game-specific table associated with the specified animation id. + * @param game considers only table definitions for the specified game. + * @param animationId the creature animation id + * @return a list of {@link IniMap} instances containing animation information. + * Returns {@code null} if no information could be determined. + */ + public static List createIniMaps(Profile.Game game, int animationId) + { + List retVal = new ArrayList<>(); + if (game == null) { + game = Profile.getGame(); + } + + retVal.addAll(processInfinityAnimations(animationId)); + + if (retVal.isEmpty()) { + List tableNames = findTables(game); + for (final String tableName : tableNames) { + ResourceEntry tableEntry = getTableResource(tableName); + if (tableEntry != null) { + Table2da table = new Table2da(tableEntry); + if (table != null) { + List inis = processTable(game, table, animationId); + if (inis != null && !inis.isEmpty()) { + retVal.addAll(inis); + break; + } + } + } + } + } + + return retVal; + } + + /** + * Attempts to create a list of potential {@link IniMap} instances based on the specified arguments. + * @param game considers only table definitions for the specified game. + * @param table hardcoded table data used as source for generating a list of {@code IniMap} instances. + * @param animationId the creature animation id. + * @return a list of {@link IniMap} instances containing animation information. + * Returns an empty {@code IniMap} list if no information could be determined. + */ + public static List processTable(Profile.Game game, Table2da table, int animationId) + { + List retVal = null; + if (game == null || table == null) { + return retVal; + } + + if (game == Profile.Game.PST || game == Profile.Game.PSTEE) { + retVal = processTablePst(table, animationId); + } else { + retVal = processTable(table, animationId); + } + + return retVal; + } + + /** Processes tables for non-PST games. */ + private static List processTable(Table2da table, int animationId) + { + List retVal = new ArrayList<>(); + if (table == null) { + return retVal; + } + + // finding entry matching the specified animation id + int rowIndex = -1; + for (int row = 0, rowCount = table.getRowCount(); row < rowCount; row++) { + if (valueMatchesAnimationId(table.get(row, 0), animationId)) { + rowIndex = row; + break; + } + } + if (rowIndex < 0) { + return retVal; + } + + // loading all data into a String[] array + String[] data = new String[table.getColCount()]; + String defValue = table.getDefaultValue(); + for (int col = 0, colCount = table.getColCount(); col < colCount; col++) { + String value = table.get(rowIndex, col); + if (value.equalsIgnoreCase(defValue)) { + value = ""; + } + data[col] = value; + } + + // determining SpriteDecoder class instance + Class cls = SpriteUtils.getSpriteDecoderClass(animationId); + if (cls != null) { + try { + // calling method of signature: public static IniMap processTableData(String[] data); + Method method = cls.getMethod("processTableData", String[].class); + Object o = method.invoke(null, new Object[] { data }); + if (o instanceof IniMap) { + retVal.add((IniMap)o); + } + } catch (InvocationTargetException ite) { + if (ite.getCause() != null) { + ite.getCause().printStackTrace(); + } else { + ite.printStackTrace(); + } + } catch (NoSuchMethodException | IllegalAccessException e) { + e.printStackTrace(); + } + } + + return retVal; + } + + /** Processes PST-style tables. */ + private static List processTablePst(Table2da table, int animationId) + { + List retVal = new ArrayList<>(); + if (table == null) { + return retVal; + } + + // finding entry matching the specified animation id + int rowIndex = -1; + // try special animations first + int id = animationId; + for (int row = 0, rowCount = table.getRowCount(); row < rowCount; row++) { + int v = valueToInt(new String[] {table.get(row, 0)}, 0, -1); + if (v == id) { + rowIndex = row; + break; + } + } + + // try regular animations next + if (rowIndex < 0) { + id = animationId & 0x0fff; + for (int row = 0, rowCount = table.getRowCount(); row < rowCount; row++) { + int v = valueToInt(new String[] {table.get(row, 0)}, 0, -1); + if (v == id) { + rowIndex = row; + break; + } + } + } + + if (rowIndex < 0) { + // no luck :( + return retVal; + } + + // loading all data into a String[] array + String[] data = new String[table.getColCount()]; + String defValue = table.getDefaultValue(); + for (int col = 0, colCount = table.getColCount(); col < colCount; col++) { + String value = table.get(rowIndex, col); + if (value.equalsIgnoreCase(defValue)) { + value = ""; + } + data[col] = value; + } + + // delegate parsing to SpriteDecoder class + retVal.add(MonsterPlanescapeDecoder.processTableData(data)); + + return retVal; + } + + /** Helper method for finding matching Infinity Animations entries. */ + private static List processInfinityAnimations(int animationId) + { + if (Profile.getProperty(Profile.Key.GET_INFINITY_ANIMATIONS) > 0) { + return InfinityTables.createIniMaps(animationId); + } + return new ArrayList<>(); + } + + + /** + * Convenience method: Safely retrieves an array item and returns it. + * Returns a default value if the item could not be retrieved or is {@code null}. + */ + public static String valueToString(String[] arr, int arrIdx, String defValue) + { + String retVal = defValue; + try { + retVal = arr[arrIdx]; + if (retVal == null) { + retVal = defValue; + } + } catch (Exception e) { + } + return retVal; + } + + /** Convenience method: Converts an array item into a numeric value. */ + public static int valueToInt(String[] arr, int arrIdx, int defValue) + { + int retVal = defValue; + try { + String s = arr[arrIdx]; + if (s.startsWith("0x") || s.startsWith("0X")) { + retVal = Integer.parseInt(s.substring(2), 16); + } else { + retVal = Integer.parseInt(s); + } + } catch (NullPointerException | ArrayIndexOutOfBoundsException | NumberFormatException e) { + } + return retVal; + } + + /** Convenience method: Returns the animation id from the specified array value. */ + public static int valueToAnimationId(String[] arr, int arrIdx, int defValue) + { + try { + return valueToInt(arr[arrIdx].split("_"), 0, defValue); + } catch (NullPointerException | ArrayIndexOutOfBoundsException e) { + return defValue; + } + } + + /** Convenience method: Checks if the specified animation id is covered by the id value (and mask) from the table. */ + public static boolean valueMatchesAnimationId(String value, int animationId) + { + boolean retVal = false; + if (value == null) { + return retVal; + } + + String[] parts = value.split("_"); + if (parts.length > 0) { + int base = valueToInt(new String[] {parts[0]}, 0, -1); + if (base >= 0) { + int range = 0; + if (parts.length > 1) { + range = valueToInt(new String[] {"0x" + parts[1]}, 0, 0); + base &= ~range; + } + retVal = (animationId >= base && animationId <= (base + range)); + } + } + + return retVal; + } + + /** + * Returns a list of names for tables associated with the specified game. + * @param game The game type. + * @return List of table names ordered by relevance. Returns an empty list if no tables could be determined. + */ + public static List findTables(Profile.Game game) + { + List retVal = TableMaps.get(game); + if (retVal == null) { + retVal = new ArrayList<>(); + } + return retVal; + } + + /** + * Returns a virtual {@code ResourceEntry} instance providing access to the underlying table data. + * @param fileName The filename of the table (without path). + * @return a {@link ResourceEntry} instance linked to the specified table. + * Returns {@code null} if table could not be opened. + */ + public static ResourceEntry getTableResource(String fileName) + { + ResourceEntry entry = null; + try { + try (ByteArrayOutputStream bos = new ByteArrayOutputStream(128 * 1024)) { + try (InputStream is = getResourceAsStream(null, fileName)) { + byte[] buf = new byte[65536]; + int len; + while ((len = is.read(buf)) > 0) { + bos.write(buf, 0, len); + } + } + entry = new BufferedResourceEntry(ByteBuffer.wrap(bos.toByteArray()), fileName); + } + } catch (Exception e) { + } + return entry; + } + + /** Returns an InputStream instance for reading from the specified resource. */ + static InputStream getResourceAsStream(Class c, String fileName) + { + InputStream retVal = null; + if (!fileName.isEmpty()) { + if (c == null) { + retVal = ClassLoader.getSystemResourceAsStream(fileName); + } + if (retVal == null) { + if (c == null) { + c = SpriteTables.class; + } + String basePath = c.getPackage().getName().replace('.', '/'); + String separator = (fileName.charAt(0) == '/') ? "" : "/"; + retVal = ClassLoader.getSystemResourceAsStream(basePath + separator + fileName); + } + } + return retVal; + } + + private SpriteTables() { } +} diff --git a/src/org/infinity/resource/cre/decoder/tables/avatars-bg1.2da b/src/org/infinity/resource/cre/decoder/tables/avatars-bg1.2da new file mode 100644 index 000000000..3330ec8f0 --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/tables/avatars-bg1.2da @@ -0,0 +1,212 @@ +2DA V1.0 +* + RESREF TYPE ELLIPSE SPACE BLENDING PALETTE PALETTE2 RESREF2 TRANSLUCENT CLOWN SPLIT HELMET WEAPON HEIGHT HEIGHT_SHIELD +0x0000 SPRING 0 0 0 0 * * * * 1 0 * * * * +0x0100 SPCHUNKS 0 0 0 0 * * SPSHADOW * 1 1 * * * * +0x0200 SPBLOOD 0 0 0 0 * * * * 0 0 0 * * * +0x0210 SPBLOOD 0 0 0 0 * * * * 0 0 1 * * * +0x0220 SPBLOOD 0 0 0 0 * * * * 0 0 2 * * * +0x0230 SPBLOOD 0 0 0 0 * * * * 0 0 3 * * * +0x0240 SPBLOOD 0 0 0 0 * * * * 0 0 4 * * * +0x0300 SPSMPUFF 0 0 0 0 * * * 1 1 * * * * * +0x0400 SKLH 0 0 0 0 * * SPSHADOW * 0 1 * * * * +0x0410 GLPHWRDH 0 0 0 0 * * SPSHADOW * 0 1 * * * * +0x0500 STNKCLDD 0 0 0 0 * * * * 1 0 1 * * * +0x0510 STNKCLDD 0 0 0 0 * * * * 1 0 0 * * * +0x0600 STNKCLDD 0 0 0 0 * * * * 1 0 1 * * * +0x0700 GREASEH 0 0 0 0 * * * * 0 0 * * * * +0x0710 GREASED 0 0 0 0 * * * * 0 1 * * * * +0x0800 WEBENTH 0 0 0 0 * * * * 1 0 * * * * +0x0810 WEBENTD 0 0 0 0 * * * * 1 0 * * * * +0x0900 STNKCLDD 0 0 0 0 * * * * 1 0 1 * * * +0x1000 MWYV 1 24 4 0 * * * * 0 * * * * * +0x1100 MTAN 1 32 5 0 * * * * 0 * 1 * * * +0x2000 MSIR 5 16 3 0 * * * * 1 * 1 * * BW +0x2100 UVOL 5 16 3 0 * * * * 0 * 1 * MS * +0x2200 MOGM 5 16 3 0 * * * * 1 * 1 1 S1 * +0x2300 MDKN 5 16 3 0 * * * * 0 * 1 1 * * +0x3000 MAKH 6 24 5 0 * * * * 0 * * * * * +0x4000 SNOMC 7 16 3 0 * * * * 1 * * * * * +0x4002 SNOMM 7 16 3 0 * * * * 1 * * * * * +0x4010 SNOWC 7 16 3 0 * * * * 1 * * * * * +0x4012 SNOWM 7 16 3 0 * * * * 1 * * * * * +0x4100 SSIMC 7 16 3 0 * * * * 1 * * * * * +0x4101 SSIMS 7 16 3 0 * * * * 1 * * * * * +0x4102 SSIMM 7 16 3 0 * * * * 1 * * * * * +0x4110 SSIWC 7 16 3 0 * * * * 1 * * * * * +0x4112 SSIWM 7 16 3 0 * * * * 1 * * * * * +0x4200 SHMCM 7 16 3 0 * * * * 1 * * * * * +0x4300 MSPLG1 7 32 5 0 * * * * 0 * * * * * +0x4400 LHMC 7 16 3 0 * * * * 1 * * * * * +0x4410 LHFC 7 16 3 0 * * * * 1 * * * * * +0x4500 LFAM 7 16 3 0 * * * * 1 * * * * * +0x4600 LDMF 7 16 3 0 * * * * 1 * * * * * +0x4700 LEMF 7 16 3 0 * * * * 1 * * * * * +0x4710 LEFF 7 16 3 0 * * * * 1 * * * * * +0x4800 LIMC 7 16 3 0 * * * * 1 * * * * * +0x5000 CHMC 9 16 3 0 * * * * 1 0 1 * WPL * +0x5001 CEMC 9 16 3 0 * * * * 1 0 1 * WPM * +0x5002 CDMC 9 16 3 0 * * * * 1 0 1 * WPS * +0x5003 CIMC 9 16 3 0 * * * * 1 0 1 * WPS * +0x5010 CHFC 9 16 3 0 * * * * 1 0 1 * WPM * +0x5011 CEFC 9 16 3 0 * * * * 1 0 1 * WPM * +0x5012 CDMC 9 16 3 0 * * * * 1 0 1 * WPS * +0x5013 CIFC 9 16 3 0 * * * * 1 0 1 * WPS * +0x5100 CHMF 9 16 3 0 * * * * 1 0 1 * WPL * +0x5101 CEMF 9 16 3 0 * * * * 1 0 1 * WPM * +0x5102 CDMF 9 16 3 0 * * * * 1 0 1 * WPS * +0x5103 CIMF 9 16 3 0 * * * * 1 0 1 * WPS * +0x5110 CHFF 9 16 3 0 * * * * 1 0 1 * WPM * +0x5111 CEFF 9 16 3 0 * * * * 1 0 1 * WPM * +0x5112 CDMF 9 16 3 0 * * * * 1 0 1 * WPS * +0x5113 CIFF 9 16 3 0 * * * * 1 0 1 * WPS * +0x5200 CHMW 9 16 3 0 * * * * 1 0 1 * WPL * +0x5201 CEMW 9 16 3 0 * * * * 1 0 1 * WPM * +0x5202 CDMW 9 16 3 0 * * * * 1 0 1 * WPS * +0x5203 CDMW 9 16 3 0 * * * * 1 0 1 * WPS * +0x5210 CHFW 9 16 3 0 * * * * 1 0 1 * WPM * +0x5211 CEFW 9 16 3 0 * * * * 1 0 1 * WPM * +0x5212 CDMW 9 16 3 0 * * * * 1 0 1 * WPS * +0x5213 CDMW 9 16 3 0 * * * * 1 0 1 * WPS * +0x5300 CHMT 9 16 3 0 * * * * 1 0 0 * WPL * +0x5301 CEMT 9 16 3 0 * * * * 1 0 0 * WPM * +0x5302 CDMT 9 16 3 0 * * * * 1 0 0 * WPS * +0x5303 CIMT 9 16 3 0 * * * * 1 0 0 * WPS * +0x5310 CHFT 9 16 3 0 * * * * 1 0 0 * WPM * +0x5311 CEFT 9 16 3 0 * * * * 1 0 0 * WPM * +0x5312 CDMT 9 16 3 0 * * * * 1 0 0 * WPS * +0x5313 CIFT 9 16 3 0 * * * * 1 0 0 * WPS * +0x6000 CHMC 9 16 3 0 * * * * 1 0 1 * WPL * +0x6001 CEMC 9 16 3 0 * * * * 1 0 1 * WPM * +0x6002 CDMC 9 16 3 0 * * * * 1 0 1 * WPS * +0x6003 CIMC 9 16 3 0 * * * * 1 0 1 * WPS * +0x6004 CDMC 9 16 3 0 * * * * 1 0 1 * WPS * +0x6005 CHMC 9 16 3 0 * * * * 1 0 1 * WPL * +0x6010 CHFC 9 16 3 0 * * * * 1 0 1 * WPM * +0x6011 CEFC 9 16 3 0 * * * * 1 0 1 * WPM * +0x6012 CDMC 9 16 3 0 * * * * 1 0 1 * WPS * +0x6013 CIFC 9 16 3 0 * * * * 1 0 1 * WPS * +0x6014 CIFC 9 16 3 0 * * * * 1 0 1 * WPS * +0x6015 CHFC 9 16 3 0 * * * * 1 0 1 * WPM * +0x6100 CHMF 9 16 3 0 * * * * 1 0 1 * WPL * +0x6101 CEMF 9 16 3 0 * * * * 1 0 1 * WPM * +0x6102 CDMF 9 16 3 0 * * * * 1 0 1 * WPS * +0x6103 CIMF 9 16 3 0 * * * * 1 0 1 * WPS * +0x6104 CDMF 9 16 3 0 * * * * 1 0 1 * WPS * +0x6105 CHMF 9 16 3 0 * * * * 1 0 1 * WPL * +0x6110 CHFF 9 16 3 0 * * * * 1 0 1 * WPM * +0x6111 CEFF 9 16 3 0 * * * * 1 0 1 * WPM * +0x6112 CDMF 9 16 3 0 * * * * 1 0 1 * WPS * +0x6113 CIFF 9 16 3 0 * * * * 1 0 1 * WPS * +0x6114 CIFF 9 16 3 0 * * * * 1 0 1 * WPS * +0x6115 CHFF 9 16 3 0 * * * * 1 0 1 * WPM * +0x6200 CHMW 9 16 3 0 * * * * 1 0 1 * WPL * +0x6201 CEMW 9 16 3 0 * * * * 1 0 1 * WPM * +0x6202 CDMW 9 16 3 0 * * * * 1 0 1 * WPS * +0x6203 CDMW 9 16 3 0 * * * * 1 0 1 * WPS * +0x6204 CDMW 9 16 3 0 * * * * 1 0 1 * WPS * +0x6205 CHMW 9 16 3 0 * * * * 1 0 1 * WPL * +0x6210 CHFW 9 16 3 0 * * * * 1 0 1 * WPM * +0x6211 CEFW 9 16 3 0 * * * * 1 0 1 * WPM * +0x6212 CDMW 9 16 3 0 * * * * 1 0 1 * WPS * +0x6213 CDMW 9 16 3 0 * * * * 1 0 1 * WPS * +0x6214 CDMW 9 16 3 0 * * * * 1 0 1 * WPS * +0x6215 CHFW 9 16 3 0 * * * * 1 0 1 * WPM * +0x6300 CHMT 9 16 3 0 * * * * 1 0 0 * WPL * +0x6301 CEMT 9 16 3 0 * * * * 1 0 0 * WPM * +0x6302 CDMT 9 16 3 0 * * * * 1 0 0 * WPS * +0x6303 CIMT 9 16 3 0 * * * * 1 0 0 * WPS * +0x6304 CDMT 9 16 3 0 * * * * 1 0 0 * WPS * +0x6305 CHMT 9 16 3 0 * * * * 1 0 0 * WPL * +0x6310 CHFT 9 16 3 0 * * * * 1 0 0 * WPM * +0x6311 CEFT 9 16 3 0 * * * * 1 0 0 * WPM * +0x6312 CDMT 9 16 3 0 * * * * 1 0 0 * WPS * +0x6313 CIFT 9 16 3 0 * * * * 1 0 0 * WPS * +0x6314 CIFT 9 16 3 0 * * * * 1 0 0 * WPS * +0x6315 CHFT 9 16 3 0 * * * * 1 0 0 * WPM * +0x6400 UDRZ 9 16 3 0 * * * * 1 * 1 0 WPM * +0x6401 UELM 9 16 3 0 * * * * 0 * 0 0 WPM * +0x6402 CMNK 9 16 3 0 * * * * 1 * 0 * WPM * +0x6403 MSKL 9 16 3 0 * * * * 1 * 1 * WPM * +0x6404 USAR 9 16 3 0 * * * * 0 * 0 0 WPL * +0x6405 MDGU 9 16 3 0 * * * * 1 * 1 * WPM * +0x6406 MDGU 9 24 5 0 * * * * 1 * 1 * WPM * +0x7000 MOGH 11 16 3 0 * * * * 1 * * * * * +0x7001 MOGN 11 16 3 0 * * * * 1 * * * * * +0x7100 MBAS 11 32 5 0 * * * * 0 * * * * * +0x7101 MBAS 11 32 5 0 MBAS_GR * * * 0 * * * * * +0x7200 MBER 11 16 3 0 MBER_BL * * * 0 * * * * * +0x7201 MBER 11 16 3 0 * * * * 0 * * * * * +0x7202 MBER 11 16 3 0 MBER_CA * * * 0 * * * * * +0x7203 MBER 11 16 3 0 MBER_PO * * * 0 * * * * * +0x7400 MDOG 11 16 3 0 MDOG_WI * * * 0 * * * * * +0x7401 MDOG 11 16 3 0 MDOG_WA * * * 0 * * * * * +0x7500 MDOP 11 16 3 0 * * * * 0 * * * * * +0x7501 MDOP 11 16 3 0 MDOP_GR * * * 0 * * * * * +0x7600 METT 11 16 3 0 * * * * 0 * * * * * +0x7700 MGHL 11 16 3 0 * * * * 0 * * * * * +0x7701 MGHL 11 16 3 0 MGHL_RE * * * 0 * * * * * +0x7702 MGHL 11 16 3 0 MGHL_GA * * * 0 * * * * * +0x7800 MGIB 11 16 3 0 * * * * 0 * * * * * +0x7900 MSLI 11 24 4 0 MSLI_GR * * 1 0 * * * * * +0x7901 MSLI 11 24 4 0 MSLI_OL * * 1 0 * * * * * +0x7902 MSLI 11 24 4 0 MSLI_MU * * 1 0 * * * * * +0x7903 MSLI 11 24 4 0 MSLI_OC * * 1 0 * * * * * +0x7904 MSLI 11 24 4 0 * * * 1 0 * * * * * +0x7a00 MSPI 11 16 3 0 MSPI_GI * * * 0 * * * * * +0x7a01 MSPI 11 16 3 0 MSPI_HU * * * 0 * * * * * +0x7a02 MSPI 11 16 3 0 MSPI_PH * * * 0 * * * * * +0x7a03 MSPI 11 16 3 0 MSPI_SW * * * 0 * * * * * +0x7a04 MSPI 11 16 3 0 MSPI_WR * * * 0 * * * * * +0x7b00 MWLF 11 16 3 0 * * * * 0 * * * * * +0x7b01 MWLF 11 16 3 0 MWLF_WO * * * 0 * * * * * +0x7b02 MWLF 11 16 3 0 MWLF_DI * * * 0 * * * * * +0x7b03 MWLF 11 16 3 0 MWLF_WI * * * 0 * * * * * +0x7b04 MWLF 11 16 3 0 MWLF_VA * * * 0 * * * * * +0x7b05 MWLF 11 16 3 0 MWLF_DR * * * 0 * * * * * +0x7c00 MXVT 11 16 3 0 * * * * 1 * * * * * +0x7c01 MTAS 11 16 3 0 * * * * 0 * * * * * +0x7d00 MZOM 11 16 3 0 * * * * 1 * * * * * +0x7e00 MWER 11 16 3 0 * * * * 0 * * * * * +0x7e01 MGWE 11 16 3 0 * * * * 0 * * * * * +0x8000 MGNL 4 16 3 0 * * * * * * * * S1 HB +0x8100 MHOB 4 16 3 0 * * * * * * * * S1 BW +0x8200 MKOB 4 16 3 0 * * * * * * * * SS BW +0x9000 MOGR 12 16 3 0 * * * * 1 * * * * * +0xa000 MWYV 13 16 3 0 * * * * 0 * * * * * +0xa100 MCAR 13 16 3 0 * * * * 0 * * * * * +0xb000 ACOW 14 32 5 0 * * * * 0 * * * * * +0xb100 AHRS 14 32 5 0 * * * * 0 * * * * * +0xb200 NBEGL 14 16 3 0 * * * * 1 * * * * * +0xb210 NPROL 14 16 3 0 * * * * 1 * * * * * +0xb300 NBOYL 14 16 3 0 * * * * 1 * * * * * +0xb310 NGRLL 14 16 3 0 * * * * 1 * * * * * +0xb400 NFAML 14 16 3 0 * * * * 1 * * * * * +0xb410 NFAWL 14 16 3 0 * * * * 1 * * * * * +0xb500 NSIML 14 16 3 0 * * * * 1 * * * * * +0xb510 NSIWL 14 16 3 0 * * * * 1 * * * * * +0xb600 NNOML 14 16 3 0 * * * * 1 * * * * * +0xb610 NNOWL 14 16 3 0 * * * * 1 * * * * * +0xb700 NSLVL 14 16 3 0 * * * * 1 * * * * * +0xc000 ABAT 15 12 3 0 * * * * 0 * * * * * +0xc100 ACAT 15 12 3 0 * * * * 0 * * * * * +0xc200 ACHK 15 12 3 0 * * * * 0 * * * * * +0xc300 ARAT 15 12 3 0 * * * * 0 * * * * * +0xc400 ASQU 15 12 3 0 * * * * 0 * * * * * +0xc500 ABAT 15 12 3 0 * * * * 0 * * * * * +0xc600 NBEGH 15 16 3 0 * * * * 1 * * * * * +0xc610 NPROH 15 16 3 0 * * * * 1 * * * * * +0xc700 NBOYH 15 16 3 0 * * * * 1 * * * * * +0xc710 NGRLH 15 16 3 0 * * * * 1 * * * * * +0xc800 NFAMH 15 16 3 0 * * * * 1 * * * * * +0xc810 NFAWH 15 16 3 0 * * * * 1 * * * * * +0xc900 NSIMH 15 16 3 0 * * * * 1 * * * * * +0xc910 NSIWH 15 16 3 0 * * * * 1 * * * * * +0xca00 NNOMH 15 16 3 0 * * * * 1 * * * * * +0xca10 NNOWH 15 16 3 0 * * * * 1 * * * * * +0xcb00 NSLVH 15 16 3 0 * * * * 1 * * * * * +0xd000 AEAGG1 16 0 3 0 * * * * 0 * * * * * +0xd100 AGULG1 16 0 3 0 * * * * 0 * * * * * +0xd200 AVULG1 16 0 3 0 * * * * 0 * * * * * +0xd300 ABIRG1 16 0 3 0 * * * * 0 * * * * * +0xd400 ABIRG1 16 0 3 0 * * * * 0 * * * * * diff --git a/src/org/infinity/resource/cre/decoder/tables/avatars-bg2ee.2da b/src/org/infinity/resource/cre/decoder/tables/avatars-bg2ee.2da new file mode 100644 index 000000000..f5176f20a --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/tables/avatars-bg2ee.2da @@ -0,0 +1,515 @@ +2DA V1.0 +* + RESREF TYPE ELLIPSE SPACE BLENDING PALETTE PALETTE2 RESREF2 TRANSLUCENT CLOWN SPLIT HELMET WEAPON HEIGHT HEIGHT_SHIELD +0x0000 SPRING 0 0 0 0 * * * * 1 0 * * * * +0x0001 SPFLAMES 0 0 0 7 * * * * 0 0 * * * * +0x0002 SPRDRASI 0 0 0 7 * * * * 0 0 * * * * +0x0003 SPFLAMES 0 0 0 7 SPFLAMEB * * * 0 0 * * * * +0x0004 SPRDRASI 0 0 0 7 SPGDRASI * * * 0 0 * * * * +0x0100 SPCHUNKS 0 0 0 0 * * SPSHADOW * 1 1 * * * * +0x0101 BGLRYU 0 0 3 7 * * * * * 0 * * * * +0x0102 SPCL236U 0 0 3 7 * * * * * 0 * * * * +0x0200 SPBLOOD 0 0 0 0 * * * * 0 0 0 * * * +0x0210 SPBLOOD 0 0 0 0 * * * * 0 0 1 * * * +0x0220 SPBLOOD 0 0 0 0 * * * * 0 0 2 * * * +0x0230 SPBLOOD 0 0 0 0 * * * * 0 0 3 * * * +0x0240 SPBLOOD 0 0 0 0 * * * * 0 0 4 * * * +0x0300 SPSMPUFF 0 0 0 0 * * * 1 1 * * * * * +0x0301 SPSMPUFF 0 0 0 0 * * * 1 1 * * * * * +0x0400 SKLH 0 0 0 0 * * SPSHADOW * 0 1 * * * * +0x0410 GLPHWRDH 0 0 0 1 * * SPSHADOW * 0 1 * * * * +0x0500 STNKCLDD 0 0 0 0 * * * * 1 0 1 * * * +0x0510 STNKCLDD 0 0 0 0 * * * * 1 0 0 * * * +0x0520 SPHORPUF 0 0 0 1 * * * * 0 0 * * * * +0x0600 STNKCLDD 0 0 0 0 * * * * 1 0 1 * * * +0x0610 SPICESTM 0 0 0 1 * * * * 0 0 0 * * * +0x0700 GREASEH 0 0 0 0 * * * * 0 0 * * * * +0x0710 GREASED 0 0 0 0 * * * * 0 1 * * * * +0x0800 WEBENTH 0 0 0 0 * * * * 1 0 * * * * +0x0810 WEBENTD 0 0 0 0 * * * * 1 0 * * * * +0x0900 STNKCLDD 0 0 0 0 * * * * 1 0 1 * * * +0x0910 SPMETSWM 0 0 0 1 * * * * 0 1 0 * * * +0x0a00 SPHORPUF 0 0 0 1 * * * * 0 0 * * * * +0x0a01 SPWRDFLD 0 0 0 1 * * * * 0 1 * * * * +0x0a02 SPENTAAI 0 0 0 1 * * * * 0 1 * * * * +0x0a03 SPHLYSM2 0 0 0 1 * * * * 0 1 * * * * +0x0a04 SPUNHBL2 0 0 0 1 * * * * 0 1 * * * * +0x0a10 SPHORPUF 0 0 0 1 * * * * 0 0 * * * * +0x0a11 SPWRDFLD 0 0 0 1 * * * * 0 1 * * * * +0x0a12 SPENTAAI 0 0 0 1 * * * * 0 1 * * * * +0x0a13 SPHLYSM2 0 0 0 1 * * * * 0 1 * * * * +0x0a14 SPUNHBL2 0 0 0 1 * * * * 0 1 * * * * +0x0a23 SPHLYSM2 0 0 0 1 * * * * 0 1 * * * * +0x0a24 SPUNHBL2 0 0 0 1 * * * * 0 1 * * * * +0x0b00 SPCSPRA2 0 0 0 1 * * * * 0 0 * * * * +0x0b01 SPCCOLDL 0 0 0 1 * * * * 0 0 * * * * +0x0b02 SPPRISM2 0 0 0 1 * * * * 0 0 * * * * +0x0b03 SPCSPRA3 0 0 0 1 * * * * 0 0 * * * * +0x0b04 SPPRISM3 0 0 0 1 * * * * 0 0 * * * * +0x0c00 SPSTRMVA 0 0 0 1 * * * * 0 1 * * SPSTRMVB * +0x0c10 SPSTRMVA 0 0 0 1 * * * * 0 1 * * SPSTRMVB * +0x1000 MWYV 1 24 4 0 * * * * 0 * * * * * +0x1003 MWYV 1 24 4 0 MWYV_WH * * * 0 * * * * * +0x1004 MWYV 1 24 4 0 MWYV_AL * * * 0 * * * * * +0x1100 MTAN 1 32 5 0 * * * * 0 * 1 * * * +0x1101 MWDR 1 72 7 0 * * * * 0 * * * * * +0x1102 MTAN 1 32 5 0 MTAN_BL * * * 0 * 1 * * * +0x1103 MTAN 1 32 5 0 MTAN_GR * * * 0 * 1 * * * +0x1104 MTAN 1 32 5 0 MTAN_RD * * * 0 * 1 * * * +0x1105 MWDR 1 72 7 0 MWDR_GR * * * 0 * * * * * +0x1200 MDR1 2 72 13 0 * * * * 0 1 * * * * +0x1201 MDR2 2 93 13 0 * * * * 0 1 * * * * +0x1202 MDR3 2 72 13 0 * * * * 0 1 * * * * +0x1203 MDR1 2 72 13 0 MDR1_GR * * * 0 1 * * * * +0x1204 MDR1 2 72 13 0 MDR1_AQ * * * 0 1 * * * * +0x1205 MDR1 2 72 13 0 MDR1_BL * * * 0 1 * * * * +0x1206 MDR1 2 72 13 0 MDR1_BR * * * 0 1 * * * * +0x1207 MDR1 2 72 13 0 MDR1_MC * * * 0 1 * * * * +0x1208 MDR1 2 72 13 0 MDR1_PU * * * 0 1 * * * * +0x1300 MDEM 3 32 5 0 * * * * 0 1 * * * * +0x2000 MSIR 5 16 3 0 * * * * 1 * 1 * * BW +0x2100 UVOL 5 16 3 0 * * * * 0 * 1 * MS * +0x2200 MOGM 5 16 3 0 * * * * 1 * 1 1 S1 * +0x2300 MDKN 5 16 3 0 * * * * 0 * 1 1 * * +0x3000 MAKH 6 24 5 0 * * * * 0 * * * * * +0x4000 SNOMC 7 16 3 0 * * * * 1 * * * * * +0x4001 SNONE 7 0 0 0 * * * * 0 * * * * * +0x4002 SNOMM 7 16 3 0 * * * * 1 * * * * * +0x4010 SNOWC 7 16 3 0 * * * * 1 * * * * * +0x4012 SNOWM 7 16 3 0 * * * * 1 * * * * * +0x4100 SSIMC 7 16 3 0 * * * * 1 * * * * * +0x4101 SSIMS 7 16 3 0 * * * * 1 * * * * * +0x4102 SSIMM 7 16 3 0 * * * * 1 * * * * * +0x4110 SSIWC 7 16 3 0 * * * * 1 * * * * * +0x4112 SSIWM 7 16 3 0 * * * * 1 * * * * * +0x4200 SHMCM 7 16 3 0 * * * * 1 * * * * * +0x4300 MSPLG1 7 32 5 0 * * * * 0 * * * * * +0x4400 LHMC 7 16 3 0 * * * * 1 * * * * * +0x4410 LHFC 7 16 3 0 * * * * 1 * * * * * +0x4500 LFAM 7 16 3 0 * * * * 1 * * * * * +0x4600 LDMF 7 16 3 0 * * * * 1 * * * * * +0x4700 LEMF 7 16 3 0 * * * * 1 * * * * * +0x4710 LEFF 7 16 3 0 * * * * 1 * * * * * +0x4800 LIMC 7 16 3 0 * * * * 1 * * * * * +0x5000 CHMB 8 16 3 0 * * CHMC * 1 1 1 * WQL * +0x5001 CEMB 8 16 3 0 * * CEMC * 1 1 1 * WQM * +0x5002 CDMB 8 16 3 0 * * CDMC * 1 1 1 * WQS * +0x5003 CIMB 8 16 3 0 * * CIMC * 1 1 1 * WQS WQH +0x5010 CHFB 8 16 3 0 * * CHFC * 1 1 1 * WQN * +0x5011 CEFB 8 16 3 0 * * CEFC * 1 1 1 * WQM * +0x5012 CDFB 8 16 3 0 * * CDFC * 1 1 1 * WQS * +0x5013 CIFB 8 16 3 0 * * CIFC * 1 1 1 * WQS * +0x5100 CHMB 8 16 3 0 * * CHMF * 1 1 1 * WQL * +0x5101 CEMB 8 16 3 0 * * CEMF * 1 1 1 * WQM * +0x5102 CDMB 8 16 3 0 * * CDMF * 1 1 1 * WQS * +0x5103 CIMB 8 16 3 0 * * CIMF * 1 1 1 * WQS WQH +0x5110 CHFB 8 16 3 0 * * CHFF * 1 1 1 * WQN * +0x5111 CEFB 8 16 3 0 * * CEFF * 1 1 1 * WQM * +0x5112 CDFB 8 16 3 0 * * CDFF * 1 1 1 * WQS * +0x5113 CIFB 8 16 3 0 * * CIFF * 1 1 1 * WQS * +0x5200 CHMW 8 16 3 0 * * CHMW * 1 1 1 * WQL * +0x5201 CEMW 8 16 3 0 * * CEMW * 1 1 1 * WQM * +0x5202 CDMW 8 16 3 0 * * CDMW * 1 1 1 * WQS * +0x5210 CHFW 8 16 3 0 * * CHFW * 1 1 1 * WQN * +0x5211 CEFW 8 16 3 0 * * CEFW * 1 1 1 * WQM * +0x5212 CDFW 8 16 3 0 * * CDFW * 1 1 1 * WQS * +0x5300 CHMT 8 16 3 0 * * CHMT * 1 1 0 * WQL * +0x5301 CEMT 8 16 3 0 * * CEMT * 1 1 0 * WQM * +0x5302 CDMT 8 16 3 0 * * CDMT * 1 1 0 * WQS * +0x5303 CIMT 8 16 3 0 * * CIMT * 1 1 0 * WQS WQH +0x5310 CHFT 8 16 3 0 * * CHFT * 1 1 0 * WQN * +0x5311 CEFT 8 16 3 0 * * CEFT * 1 1 0 * WQM * +0x5312 CDFT 8 16 3 0 * * CDFT * 1 1 0 * WQS * +0x5313 CIFT 8 16 3 0 * * CIFT * 1 1 0 * WQS * +0x6000 CHMB 8 16 3 0 * * CHMC * 1 1 1 * WQL * +0x6001 CEMB 8 16 3 0 * * CEMC * 1 1 1 * WQM * +0x6002 CDMB 8 16 3 0 * * CDMC * 1 1 1 * WQS * +0x6003 CIMB 8 16 3 0 * * CIMC * 1 1 1 * WQS WQH +0x6004 CDMB 8 16 3 0 * * CDMC * 1 1 1 * WQS WQH +0x6005 CHMB 8 16 3 0 * * CHMC * 1 1 1 * WQL * +0x6010 CHFB 8 16 3 0 * * CHFC * 1 1 1 * WQN * +0x6011 CEFB 8 16 3 0 * * CEFC * 1 1 1 * WQM * +0x6012 CDFB 8 16 3 0 * * CDFC * 1 1 1 * WQS * +0x6013 CIFB 8 16 3 0 * * CIFC * 1 1 1 * WQS * +0x6014 CIFB 8 16 3 0 * * CIFC * 1 1 1 * WQS WQH +0x6015 CHFB 8 16 3 0 * * CHFC * 1 1 1 * WQN * +0x6100 CHMB 8 16 3 0 * * CHMF * 1 1 1 * WQL * +0x6101 CEMB 8 16 3 0 * * CEMF * 1 1 1 * WQM * +0x6102 CDMB 8 16 3 0 * * CDMF * 1 1 1 * WQS * +0x6103 CIMB 8 16 3 0 * * CIMF * 1 1 1 * WQS WQH +0x6104 CDMB 8 16 3 0 * * CDMF * 1 1 1 * WQS WQH +0x6105 CHMB 8 16 3 0 * * CHMF * 1 1 1 * WQL * +0x6110 CHFB 8 16 3 0 * * CHFF * 1 1 1 * WQN * +0x6111 CEFB 8 16 3 0 * * CEFF * 1 1 1 * WQM * +0x6112 CDFB 8 16 3 0 * * CDFF * 1 1 1 * WQS * +0x6113 CIFB 8 16 3 0 * * CIFF * 1 1 1 * WQS * +0x6114 CIFB 8 16 3 0 * * CIFF * 1 1 1 * WQS WQH +0x6115 CHFB 8 16 3 0 * * CHFF * 1 1 1 * WQN * +0x6200 CHMW 8 16 3 0 * * CHMW * 1 1 1 * WQL * +0x6201 CEMW 8 16 3 0 * * CEMW * 1 1 1 * WQM * +0x6202 CDMW 8 16 3 0 * * CDMW * 1 1 1 * WQS * +0x6204 CDMW 8 16 3 0 * * CDMW * 1 1 1 * WQS WQH +0x6205 CHMW 8 16 3 0 * * CHMW * 1 1 1 * WQL * +0x6210 CHFW 8 16 3 0 * * CHFW * 1 1 1 * WQN * +0x6211 CEFW 8 16 3 0 * * CEFW * 1 1 1 * WQM * +0x6212 CDFW 8 16 3 0 * * CDFW * 1 1 1 * WQS * +0x6214 CDFW 8 16 3 0 * * CDFW * 1 1 1 * WQS WQH +0x6215 CHFW 8 16 3 0 * * CHFW * 1 1 1 * WQN * +0x6300 CHMT 8 16 3 0 * * CHMT * 1 1 0 * WQL * +0x6301 CEMT 8 16 3 0 * * CEMT * 1 1 0 * WQM * +0x6302 CDMT 8 16 3 0 * * CDMT * 1 1 0 * WQS * +0x6303 CIMT 8 16 3 0 * * CIMT * 1 1 0 * WQS WQH +0x6304 CDMT 8 16 3 0 * * CDMT * 1 1 0 * WQS WQH +0x6305 CHMT 8 16 3 0 * * CHMT * 1 1 0 * WQL * +0x6310 CHFT 8 16 3 0 * * CHFT * 1 1 0 * WQN * +0x6311 CEFT 8 16 3 0 * * CEFT * 1 1 0 * WQM * +0x6312 CDFT 8 16 3 0 * * CDFT * 1 1 0 * WQS * +0x6313 CIFT 8 16 3 0 * * CIFT * 1 1 0 * WQS * +0x6314 CIFT 8 16 3 0 * * CIFT * 1 1 0 * WQS WQH +0x6315 CHFT 8 16 3 0 * * CHFT * 1 1 0 * WQN * +0x6400 UDRZ 9 16 3 0 * * * * 1 * 1 0 WPM * +0x6401 UELM 9 16 3 0 * * * * 0 * 0 0 WPM * +0x6402 CMNK 9 16 3 0 * * * * 1 * 0 * WPM * +0x6403 MSKL 9 16 3 0 * * * * 1 * 1 * WPM * +0x6404 USAR 9 16 3 0 * * * * 0 * 0 0 WPL * +0x6405 MDGU 9 16 3 0 * * * * 1 * 1 * WPM * +0x6406 MDGU 9 24 7 0 * * * * 1 * 1 * WPM * +0x6500 CHMM 8 16 3 0 * * CHMM * 1 1 1 * WQL * +0x6510 CHFM 8 16 3 0 * * CHFM * 1 1 1 * WQN * +0x6621 XHFF 9 16 3 0 * * * * 1 * 1 * WPM * +0x7000 MOGH 11 16 3 0 * * * * 1 * * * * * +0x7001 MOGN 11 16 3 0 * * * * 1 * * * * * +0x7100 MBAS 11 32 5 0 * * * * 0 * * * * * +0x7101 MBAS 11 32 5 0 MBAS_GR * * * 0 * * * * * +0x7200 MBER 11 16 3 0 MBER_BL * * * 0 * * * * * +0x7201 MBER 11 16 3 0 * * * * 0 * * * * * +0x7202 MBER 11 16 3 0 MBER_CA * * * 0 * * * * * +0x7203 MBER 11 16 3 0 MBER_PO * * * 0 * * * * * +0x7300 MEAE 10 32 5 0 * * * * 0 1 * * * * +0x7301 MEAS 10 16 3 0 * * * * 0 1 * * * * +0x7302 MEAE 10 32 5 0 MEAE_SH * * * 0 1 * * * * +0x7310 MFIE 10 16 3 4 * * * * 0 1 * * * * +0x7311 MFIS 10 16 3 4 * * * * 0 1 * * * * +0x7312 MFIE 10 16 3 4 MFIEG1B MFIEG2B * * 0 1 * * * * +0x7313 MFIS 10 16 3 4 MFISG1B MFISG2B * * 0 1 * * * * +0x7314 MFIE 10 16 3 4 MFIEG31 MFIEG3B * * 0 1 * * * * +0x7320 MAIR 10 32 5 0 * * * 1 0 1 * * * * +0x7321 MAIS 10 16 3 0 * * * 1 0 1 * * * * +0x7400 MDOG 11 16 3 0 MDOG_WI * * * 0 * * * * * +0x7401 MDOG 11 16 3 0 MDOG_WA * * * 0 * * * * * +0x7402 MDOG 11 16 3 0 MDOG_MO * * * 0 * * * * * +0x7500 MDOP 11 16 3 0 * * * * 0 * * * * * +0x7501 MDOP 11 16 3 0 MDOP_GR * * * 0 * * * * * +0x7600 METT 11 16 3 0 * * * * 0 * * * * * +0x7601 MGHL 11 16 3 0 MGHL_MA * * * 0 0 * * * * +0x7602 MSPI 11 16 3 0 MSPI_MY * * * 0 * * * * * +0x7603 MDOG 11 16 3 0 MDOG_GR * * * 0 * * * * * +0x7604 MSPI 11 16 3 0 MSPI_WA * * * 0 * * * * * +0x7700 MGHL 11 16 3 0 * * * * 0 * * * * * +0x7701 MGHL 11 16 3 0 MGHL_RE * * * 0 * * * * * +0x7702 MGHL 11 16 3 0 MGHL_GA * * * 0 * * * * * +0x7703 MSHD 10 16 3 0 * * * 1 0 1 * * * * +0x7800 MGIB 11 16 3 0 * * * * 0 * * * * * +0x7801 MGIB 11 16 3 0 MGIB_BR * * * 0 * * * * * +0x7802 MSLI 11 24 4 0 MSLI_BL * * 0 0 * * * * * +0x7900 MSLI 11 24 4 0 MSLI_GR * * 1 0 * * * * * +0x7901 MSLI 11 24 4 0 MSLI_OL * * 1 0 * * * * * +0x7902 MSLI 11 24 4 0 MSLI_MU * * 1 0 * * * * * +0x7903 MSLI 11 24 4 0 MSLI_OC * * 1 0 * * * * * +0x7904 MSLI 11 24 4 0 * * * 1 0 * * * * * +0x7a00 MSPI 11 16 3 0 MSPI_GI * * * 0 * * * * * +0x7a01 MSPI 11 16 3 0 MSPI_HU * * * 0 * * * * * +0x7a02 MSPI 11 16 3 0 MSPI_PH * * * 0 * * * * * +0x7a03 MSPI 11 16 3 0 MSPI_SW * * * 0 * * * * * +0x7a04 MSPI 11 16 3 0 MSPI_WR * * * 0 * * * * * +0x7b00 MWLF 11 16 3 0 * * * * 0 * * * * * +0x7b01 MWLF 11 16 3 0 MWLF_WO * * * 0 * * * * * +0x7b02 MWLF 11 16 3 0 MWLF_DI * * * 0 * * * * * +0x7b03 MWLF 11 16 3 0 MWLF_WI * * * 0 * * * * * +0x7b04 MWLF 11 16 3 0 MWLF_VA * * * 0 * * * * * +0x7b05 MWLF 11 16 3 0 MWLF_DR * * * 0 * * * * * +0x7b06 MWLS 11 16 3 0 * * * * 0 * * * * * +0x7c00 MXVT 11 16 3 0 * * * * 1 * * * * * +0x7c01 MTAS 11 16 3 0 * * * * 0 * * * * * +0x7d00 MZOM 11 16 3 0 * * * * 1 * * * * * +0x7d01 NSLF 11 16 3 0 * * * * 1 0 * * * * +0x7d02 ACHB 11 12 3 0 * * * * 0 0 * * * * +0x7d03 ACHW 11 12 3 0 * * * * 0 0 * * * * +0x7d04 NPRF 11 16 3 0 * * * * 1 0 * * * * +0x7d05 NNMF 11 16 3 0 * * * * 1 0 * * * * +0x7d06 NNWF 11 16 3 0 * * * * 1 0 * * * * +0x7d07 NSMF 11 16 3 0 * * * * 1 0 * * * * +0x7d08 NSWF 11 16 3 0 * * * * 1 0 * * * * +0x7e00 MWER 11 16 3 0 * * * * 0 * * * * * +0x7e01 MGWE 11 16 3 0 * * * * 0 * * * * * +0x7f00 MTRO 10 16 3 0 * * * * 0 1 * * * * +0x7f01 MMIN 10 16 3 0 * * * * 0 1 * * * * +0x7f02 MBEH 10 32 5 0 * * * * 0 1 * * * * +0x7f03 MIMP 10 16 3 0 * * * * 0 1 * * * * +0x7f04 MIGO 10 32 5 0 * * * * 0 1 * * * * +0x7f05 MDJI 10 16 3 0 * * * * 0 1 * * * * +0x7f06 MDJL 10 16 3 0 * * * * 0 1 * * * * +0x7f07 MGLC 10 16 3 0 * * * * 0 1 * * * * +0x7f08 MOTY 10 24 5 0 * * * * 0 1 * * * * +0x7f09 MSAH 10 16 3 0 * * * * 0 1 * * * * +0x7f0a MGCP 10 16 3 0 * * * * 0 1 * * * * +0x7f0b MGCL 10 16 3 0 * * * * 0 1 * * * * +0x7f0c MKUO 10 16 3 0 * * * * 0 1 * * * * +0x7f0d MLIC 10 16 3 0 * * * * 0 1 * * * * +0x7f0e MDLI 10 16 3 0 * * * * 0 1 * * * * +0x7f0f MTRS 10 16 3 0 * * * * 0 1 * * * * +0x7f10 MRAK 10 16 3 0 * * * * 0 1 * * * * +0x7f11 MUMB 10 16 3 0 * * * * 0 1 * * * * +0x7f12 MVAM 10 16 3 0 * * * * 0 1 * * * * +0x7f13 MSNK 10 16 3 0 * * * * 0 1 * * * * +0x7f14 MGIT 10 16 3 0 * * * * 0 1 * * * * +0x7f15 MBES 10 16 3 0 * * * * 0 1 * * * * +0x7f16 AMOO 10 32 5 0 * * * * 0 1 * * * * +0x7f17 ARAB 10 12 3 0 * * * * 0 1 * * * * +0x7f18 ADER 10 16 3 0 * * * * 0 1 * * * * +0x7f19 MDSW 10 16 3 0 * * * * 0 1 * * * * +0x7f20 AGRO 10 12 3 0 * * * * 0 1 * * * * +0x7f21 APHE 10 12 3 0 * * * * 0 1 * * * * +0x7f22 MVAF 10 16 3 0 * * * * 0 1 * * * * +0x7f23 MSAT 10 16 3 0 * * * * 0 1 * * * * +0x7f24 NPIR 10 16 3 0 * * * * 0 1 * * * * +0x7f27 MDRO 10 16 3 0 * * * * 0 1 * * * * +0x7f28 MKUL 10 16 3 0 * * * * 0 1 * * * * +0x7f29 MFDR 10 16 3 0 * * * * 0 1 * * * * +0x7f2a NSAI 10 16 3 0 * * * * 0 1 * * * * +0x7f2b MMAX 10 16 3 0 * * * * 0 0 * * * * +0x7f2c NSOL 10 16 3 0 * * * * 0 1 * * * * +0x7f2d MWFM 10 16 3 0 * * * * 0 1 * * * * +0x7f2e MRAV 10 32 5 0 * * * * 0 1 * * * * +0x7f2f MSPS 10 16 3 0 * * * * 0 1 * * * * +0x7f30 NBOH 10 16 3 0 * * * * 0 1 * * * * +0x7f31 NELL 10 16 3 0 * * * * 0 1 * * * * +0x7f32 MSLY 10 16 3 0 * * * * 0 1 * * * * +0x7f33 MKUR 10 16 3 0 * * * * 0 0 * * * * +0x7f34 MDOC 10 16 3 0 * * * * 0 0 * * * * +0x7f35 MMIS 10 16 3 0 * * * 1 0 1 * * * * +0x7f36 NSHD 10 16 3 0 * * * * 0 1 * * * * +0x7f37 NIRE 10 16 3 0 * * * * 0 1 * * * * +0x7f38 MEYE 10 16 3 0 * * * * 0 0 * * * * +0x7f39 MMST 10 16 3 1 * * * 0 0 0 * * * * +0x7f3a NIRO 10 16 3 0 * * * * 0 1 * * * * +0x7f3b MSOG 10 16 3 4 * * * 0 0 1 * * * * +0x7f3c MASG 10 16 3 4 * * * 0 0 1 * * * * +0x7f3d MMEL 10 24 3 0 * * * * 0 0 * * * * +0x7f3e MFIG 10 32 5 0 * * * * 0 1 * * * * +0x7f3f MFIG 10 32 5 0 MFIGG1B * * * 0 1 * * * * +0x7f40 MGLM 10 16 3 0 * * * * 0 1 * * * * +0x7f41 MDJL 10 16 3 0 MDJL_E1 MDJL_E2 * * 0 1 * * * * +0x7f42 NIRO 10 16 3 0 NIRO_RD * * * 0 1 * * * * +0x7f43 MOTY 10 16 3 0 MOTY_Y1 MOTY_Y2 * * 0 1 * * * * +0x7f44 NELW 10 16 3 0 * * * * 0 0 * * * * +0x7f45 MSAI 10 16 3 0 * * * 1 0 0 * * * * +0x7f46 MBEG 10 16 3 0 * * * * 0 0 * * * * +0x7f47 MFI2 10 32 5 0 * * * * 0 0 * * * * +0x7f48 MSOF 10 16 3 0 * * * 0 0 1 * * * * +0x7f49 MDMF 10 16 3 0 * * * 0 0 1 * * * * +0x7f4a MDAS 10 16 3 7 * * MDAG 0 0 1 * * * * +0x7f4b MDAF 10 16 3 0 * * * 0 0 1 * * * * +0x7f4c MPLN 10 16 3 7 * * MPLG 0 0 1 * * * * +0x7f4d MPLF 10 16 3 0 * * * 0 0 1 * * * * +0x7f4e MDVM 10 16 3 7 * * MDVG 0 0 1 * * * * +0x7f4f MDVF 10 16 3 0 * * * 0 0 1 * * * * +0x7f50 MMST 10 16 3 1 MMST_NI * * 0 0 0 * * * * +0x7f51 MMST 10 16 3 1 MMST_HA * * 0 0 0 * * * * +0x7f52 NSAI 10 16 3 0 NSAI_G1 NSAI_G2 * * 0 1 * * * * +0x7f53 NSAI 10 16 3 0 NSAI_R1 NSAI_R2 * * 0 1 * * * * +0x7f54 NSAI 10 16 3 0 NSAI_D1 NSAI_D2 * * 0 1 * * * * +0x7f55 NSOL 10 16 3 0 NSOL_C1 NSOL_C2 * * 0 1 * * * * +0x7f56 MWIL 10 16 3 7 * * * * 0 0 * * * * +0x7f57 MWIS 10 12 3 7 * * * * 0 0 * * * * +0x7f58 MABB 10 24 3 0 * * * * 0 0 * * * * +0x7f59 MABG 10 24 3 0 * * * * 0 0 * * * * +0x7f5a MABR 10 24 3 0 * * * * 0 0 * * * * +0x7f5b MABR 10 24 3 0 MABR_BL * * * 0 0 * * * * +0x7f5c MHAG 10 16 3 0 * * * * 0 0 * * * * +0x7f5d MHAG 10 16 3 0 MHAG_GR * * * 0 0 * * * * +0x7f5e MHAG 10 16 3 0 MHAG_SE * * * 0 0 * * * * +0x7f5f MHAN 10 16 3 0 * * * * 0 0 * * * * +0x7f60 MSNK 10 16 3 0 MSNK_W1 MSNK_W2 * * 0 1 * * * * +0x7f61 MDRJ 10 48 9 0 * * * * 0 0 * * * * +0x7f62 MDRJ 10 48 9 0 MDRJ_GR * * * 0 0 * * * * +0x8000 MGNL 4 16 3 0 * * * * * * * * S1 HB +0x8100 MHOB 4 16 3 0 * * * * * * * * S1 BW +0x8200 MKOB 4 16 3 0 * * * * * * * * SS BW +0x9000 MOGR 12 16 3 0 * * * * 1 * * * * * +0xa000 MWYV 13 16 3 0 * * * * 0 * * * * * +0xa100 MCAR 13 16 3 0 * * * * 0 * * * * * +0xa200 MWYV 13 24 4 0 MWYV_WS * * * 0 * * * * * +0xa201 MCEN 13 16 3 0 * * * * 0 * * * * * +0xa202 MTUN 13 16 3 0 * * * * 0 * * * * * +0xb000 ACOW 14 32 5 0 * * * * 0 * * * * * +0xb100 AHRS 14 32 5 0 * * * * 0 * * * * * +0xb200 NBEGL 14 16 3 0 * * * * 1 * * * * * +0xb210 NPROL 14 16 3 0 * * * * 1 * * * * * +0xb300 NBOYL 14 16 3 0 * * * * 1 * * * * * +0xb310 NGRLL 14 16 3 0 * * * * 1 * * * * * +0xb400 NFAML 14 16 3 0 * * * * 1 * * * * * +0xb410 NFAWL 14 16 3 0 * * * * 1 * * * * * +0xb500 NSIML 14 16 3 0 * * * * 1 * * * * * +0xb510 NSIWL 14 16 3 0 * * * * 1 * * * * * +0xb600 NNOML 14 16 3 0 * * * * 1 * * * * * +0xb610 NNOWL 14 16 3 0 * * * * 1 * * * * * +0xb700 NSLVL 14 16 3 0 * * * * 1 * * * * * +0xc000 ABAT 15 12 3 0 * * * * 0 * * * * * +0xc100 ACAT 15 12 3 0 * * * * 0 * * * * * +0xc200 ACHK 15 12 3 0 * * * * 0 * * * * * +0xc300 ARAT 15 12 3 0 * * * * 0 * * * * * +0xc400 ASQU 15 12 3 0 * * * * 0 * * * * * +0xc500 ABAT 15 12 3 0 * * * * 0 * * * * * +0xc600 NBEGH 15 16 3 0 * * * * 1 * * * * * +0xc610 NPROH 15 16 3 0 * * * * 1 * * * * * +0xc700 NBOYH 15 16 3 0 * * * * 1 * * * * * +0xc710 NGRLH 15 16 3 0 * * * * 1 * * * * * +0xc800 NFAMH 15 16 3 0 * * * * 1 * * * * * +0xc810 NFAWH 15 16 3 0 * * * * 1 * * * * * +0xc900 NSIMH 15 16 3 0 * * * * 1 * * * * * +0xc910 NSIWH 15 16 3 0 * * * * 1 * * * * * +0xca00 NNOMH 15 16 3 0 * * * * 1 * * * * * +0xca10 NNOWH 15 16 3 0 * * * * 1 * * * * * +0xcb00 NSLVH 15 16 3 0 * * * * 1 * * * * * +0xcc00 MKG1 15 16 3 0 * * * * 0 * * * * * +0xcc01 MKG2 15 16 3 0 * * * * 0 * * * * * +0xcc02 MKG3 15 16 3 0 * * * * 0 * * * * * +0xcc04 ARAT 15 0 1 0 * * * * 0 * * * * * +0xd000 AEAGG1 16 0 3 0 * * * * 0 * * * * * +0xd100 AGULG1 16 0 3 0 * * * * 0 * * * * * +0xd200 AVULG1 16 0 3 0 * * * * 0 * * * * * +0xd300 ABIRG1 16 0 3 0 * * * * 0 * * * * * +0xd400 ABIRG1 16 0 3 0 * * * * 0 * * * * * +0xe000 MCYC 17 48 5 0 * * * * * * * * * * +0xe010 METN 17 48 5 0 * * * * * * * * * * +0xe020 MTAN 17 32 5 0 * * * * * * * * * * +0xe040 MHIS 17 16 3 0 * * * * * * * * * * +0xe050 MLER 17 16 3 0 * * * * * * * * * * +0xe060 MLIC 17 16 3 0 * * * * * * * * * * +0xe070 MMIN 17 24 3 0 * * * * * * * * * * +0xe080 MMUM 17 16 3 0 * * * * * * * * * * +0xe090 MTAN 17 16 3 0 * * * * * * * * * * +0xe0a0 MTIC 17 20 3 0 * * * * * * * * * * +0xe0b0 MTRO 17 16 3 0 * * * * * * * * * * +0xe0c0 MTSN 17 24 3 0 * * * * * * * * * * +0xe0d0 MUMB 17 24 3 0 * * * * * * * * * * +0xe0e0 MCOR 17 24 3 0 * * * * * * * * * * +0xe0f0 MGIC 17 32 5 0 * * * * * * * * * * +0xe0f1 MGLA 17 24 3 0 * * * * * * * * * * +0xe0f2 MWAV 17 16 3 0 * * * 1 * * * * * * +0xe200 MBET 17 24 3 0 * * * * * * * * * * +0xe210 MBFI 17 12 3 0 * * * * * * * * * * +0xe220 MBBM 17 16 3 0 * * * * * * * * * * +0xe230 MBRH 17 64 7 0 * * * * * * * * * * +0xe240 MANI 17 24 3 0 * * * * * * * * * * +0xe241 MAN2 17 24 3 0 * * * * * * * * * * +0xe242 MAN3 17 24 3 0 * * * * * * * * * * +0xe243 MARU 17 16 3 0 * * * * * * * * * * +0xe244 MBA1 17 16 3 0 * * * * * * * * * * +0xe245 MBA2 17 16 3 0 * * * * * * * * * * +0xe246 MBA3 17 16 3 0 * * * * * * * * * * +0xe247 MBA4 17 16 3 0 * * * * * * * * * * +0xe248 MBA5 17 16 3 0 * * * * * * * * * * +0xe249 MBA6 17 16 3 0 * * * * * * * * * * +0xe24a MBAI 17 16 3 0 * * * * * * * * * * +0xe24b MELE 17 36 3 0 * * * * * * * * * * +0xe24c MELF 17 36 3 0 * * * * * * * * * * +0xe24d MELW 17 36 3 0 * * * * * * * * * * +0xe24e MGFI 17 48 3 0 * * * * * * * * * * +0xe24f MGFR 17 48 5 0 * * * * * * * * * * +0xe250 MGIR 17 24 3 0 * * * * * * * * * * +0xe251 MGVE 17 32 5 0 * * * * * * * * * * +0xe252 MHAR 17 16 3 0 * * * * * * * * * * +0xe253 MREM 17 48 5 0 * * * * * * * * * * +0xe254 MSCR 17 24 3 0 * * * * * * * * * * +0xe255 MSEE 17 16 3 0 * * * * * * * * * * +0xe259 MSVI 17 16 3 0 * * * * * * * * * * +0xe25a MSV2 17 16 3 0 * * * * * * * * * * +0xe25b MUM2 17 24 3 0 * * * * * * * * * * +0xe25c MTA2 17 16 3 0 * * * * * * * * * * +0xe25d MYET 17 24 3 0 * * * * * * * * * * +0xe25e MWI4 17 16 3 0 * * * * * * * * * * +0xe25f MDRD 17 16 3 0 * * * * * * * * * * +0xe260 MCRD 17 24 3 0 * * * * * * * * * * +0xe261 MSAH 17 24 3 0 * * * * * * * * * * +0xe262 MSAT 17 24 3 0 * * * * * * * * * * +0xe263 MSV3 17 16 3 0 * * * * * * * * * * +0xe264 MSV4 17 16 3 0 * * * * * * * * * * +0xe265 MBOA 17 24 3 0 * * * * * * * * * * +0xe266 MBU2 17 16 3 0 * * * * * * * * * * +0xe267 MBUG 17 16 3 0 * * * * * * * * * * +0xe26a MDH2 17 24 3 0 * * * * * * * * * * +0xe26b MDTR 17 24 3 0 * * * * * * * * * * +0xe26d MFE2 17 24 3 0 * * * * * * * * * * +0xe26e MFEY 17 24 3 0 * * * * * * * * * * +0xe26f MGFO 17 24 4 0 * * * * * * * * * * +0xe270 MGO5 17 16 3 0 * * * * * * * * * * +0xe271 MGOC 17 16 3 0 * * * * * * * * * * +0xe272 MGWO 17 24 3 0 * * * * * * * * * * +0xe273 MGW2 17 24 3 0 * * * * * * * * * * +0xe274 MHOH 17 24 3 0 * * * * * * * * * * +0xe276 MLEM 17 16 3 0 * * * * * * * * * * +0xe279 MNOS 17 24 3 0 * * * * * * * * * * +0xe27d MWEB 17 16 3 0 * * * * * * * * * * +0xe27e MWRA 17 16 3 0 * * * * * * * * * * +0xe27f MBOA 17 24 3 0 MBOA_BR * * * * * * * * * +0xe280 MWOR 17 24 3 0 * * * * * * * * * * +0xe281 MYUH 17 16 3 0 * * * * * * * * * * +0xe282 MDRF 17 32 4 0 * * * * * * * * * * +0xe283 MDRM 17 32 4 0 * * * * * * * * * * +0xe288 MGWO 17 24 3 0 MGWO_DK * * * * * * * * * +0xe289 MGW2 17 24 3 0 MGW2_DK * * * * * * * * * +0xe28a MTRO 17 16 3 0 MTRO_DK * * * * * * * * * +0xe28b MABW 17 24 3 0 * * * * * * * * * * +0xe28c MWD2 17 36 5 0 * * * * * * * * * * +0xe28d MWD2 17 36 5 0 MWD2_SI * * * * * * * * * +0xe28e MWD2 17 36 5 0 MWD2_GR * * * * * * * * * +0xe28f MDRD 17 16 3 0 MDRD_RE * * * * * * * * * +0xe290 MDH2 17 24 3 0 MDH2_GR * * * * * * * * * +0xe291 MBU2 17 16 3 0 MBU2_SH * * * * * * * * * +0xe292 METN 17 48 5 0 METN_GH * * * * * * * * * +0xe293 MGHI 17 40 5 0 * * * * * * * * * * +0xe294 MBON 17 16 3 0 * * * * * * * * * * +0xe300 MGHO 17 16 3 0 * * * 1 * * * * * * +0xe310 MGH2 17 16 3 0 * * * * * * * * * * +0xe320 MGH3 17 16 3 0 * * * * * * * * * * +0xe400 MGO1 17 16 3 0 * * * * * * * * * * +0xe410 MGO2 17 16 3 0 * * * * * * * * * * +0xe420 MGO3 17 16 3 0 * * * * * * * * * * +0xe430 MGO4 17 16 3 0 * * * * * * * * * * +0xe440 MGO6 17 16 3 0 * * * * * * * * * * +0xe441 MGO7 17 16 3 0 * * * * * * * * * * +0xe442 MGO8 17 16 3 0 * * * * * * * * * * +0xe443 MGO9 17 16 3 0 * * * * * * * * * * +0xe444 MGO10 17 16 3 0 * * * * * * * * * * +0xe500 MLIZ 17 24 3 0 * * * * * * * * * * +0xe510 MLI2 17 24 3 0 * * * * * * * * * * +0xe520 MLI3 17 32 5 0 * * * * * * * * * * +0xe600 MMYC 17 16 3 0 * * * * * * * * * * +0xe610 MMY2 17 16 3 0 * * * * * * * * * * +0xe700 MNO1 17 20 3 0 * * * * * * * * * * +0xe710 MNO2 17 20 3 0 * * * * * * * * * * +0xe720 MNO3 17 24 3 0 * * * * * * * * * * +0xe800 MOR1 17 16 3 0 * * * * * * * * * * +0xe810 MOR2 17 16 3 0 * * * * * * * * * * +0xe820 MOR3 17 16 3 0 * * * * * * * * * * +0xe830 MOR4 17 16 3 0 * * * * * * * * * * +0xe840 MOR5 17 16 3 0 * * * * * * * * * * +0xe900 MSAL 17 16 3 0 * * * * * * * * * * +0xe910 MSA2 17 16 3 0 * * * * * * * * * * +0xea00 MSHR 17 24 3 0 * * * * * * * * * * +0xea10 MSH1 17 16 3 0 * * * 1 * * * * * * +0xea20 MSH2 17 24 3 0 * * * 1 * * * * * * +0xeb00 MSKT 17 16 3 0 * * * * * * * * * * +0xeb10 MSKA 17 16 3 0 * * * * * * * * * * +0xeb20 MSKB 17 24 3 0 * * * * * * * * * * +0xec00 MWIG 17 16 3 0 * * * * * * * * * * +0xec10 MWI2 17 16 3 0 * * * * * * * * * * +0xec20 MWI3 17 16 3 0 * * * * * * * * * * +0xed00 MYU1 17 16 3 0 * * * * * * * * * * +0xed10 MYU2 17 16 3 0 * * * * * * * * * * +0xed20 MYU3 17 16 3 0 * * * * * * * * * * +0xee00 MZO2 17 16 3 0 * * * * * * * * * * +0xee10 MZO3 17 16 3 0 * * * * * * * * * * +0xef10 MWWE 17 24 3 0 * * * * * * * * * * diff --git a/src/org/infinity/resource/cre/decoder/tables/avatars-bg2soa.2da b/src/org/infinity/resource/cre/decoder/tables/avatars-bg2soa.2da new file mode 100644 index 000000000..a926cd892 --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/tables/avatars-bg2soa.2da @@ -0,0 +1,357 @@ +2DA V1.0 +* + RESREF TYPE ELLIPSE SPACE BLENDING PALETTE PALETTE2 RESREF2 TRANSLUCENT CLOWN SPLIT HELMET WEAPON HEIGHT HEIGHT_SHIELD +0x0000 SPRING 0 0 0 0 * * * * 1 0 * * * * +0x0001 SPFLAMES 0 0 0 7 * * * * 0 0 * * * * +0x0002 SPRDRASI 0 0 0 7 * * * * 0 0 * * * * +0x0003 SPFLAMES 0 0 0 7 SPFLAMEB * * * 0 0 * * * * +0x0004 SPRDRASI 0 0 0 7 SPGDRASI * * * 0 0 * * * * +0x0100 SPCHUNKS 0 0 0 0 * * SPSHADOW * 1 1 * * * * +0x0200 SPBLOOD 0 0 0 0 * * * * 0 0 0 * * * +0x0210 SPBLOOD 0 0 0 0 * * * * 0 0 1 * * * +0x0220 SPBLOOD 0 0 0 0 * * * * 0 0 2 * * * +0x0230 SPBLOOD 0 0 0 0 * * * * 0 0 3 * * * +0x0240 SPBLOOD 0 0 0 0 * * * * 0 0 4 * * * +0x0300 SPSMPUFF 0 0 0 0 * * * 1 1 * * * * * +0x0400 SKLH 0 0 0 0 * * SPSHADOW * 0 1 * * * * +0x0410 GLPHWRDH 0 0 0 1 * * SPSHADOW * 0 1 * * * * +0x0500 STNKCLDD 0 0 0 0 * * * * 1 0 1 * * * +0x0510 STNKCLDD 0 0 0 0 * * * * 1 0 0 * * * +0x0520 SPHORPUF 0 0 0 1 * * * * 0 0 * * * * +0x0600 STNKCLDD 0 0 0 0 * * * * 1 0 1 * * * +0x0610 SPICESTM 0 0 0 1 * * * * 0 0 0 * * * +0x0700 GREASEH 0 0 0 0 * * * * 0 0 * * * * +0x0710 GREASED 0 0 0 0 * * * * 0 1 * * * * +0x0800 WEBENTH 0 0 0 0 * * * * 1 0 * * * * +0x0810 WEBENTD 0 0 0 0 * * * * 1 0 * * * * +0x0900 STNKCLDD 0 0 0 0 * * * * 1 0 1 * * * +0x0910 SPMETSWM 0 0 0 1 * * * * 0 1 0 * * * +0x0a00 SPHORPUF 0 0 0 1 * * * * 0 0 * * * * +0x0a01 SPWRDFLD 0 0 0 1 * * * * 0 1 * * * * +0x0a02 SPENTAAI 0 0 0 1 * * * * 0 1 * * * * +0x0a03 SPHLYSM2 0 0 0 1 * * * * 0 1 * * * * +0x0a04 SPUNHBL2 0 0 0 1 * * * * 0 1 * * * * +0x0a10 SPHORPUF 0 0 0 1 * * * * 0 0 * * * * +0x0a11 SPWRDFLD 0 0 0 1 * * * * 0 1 * * * * +0x0a12 SPENTAAI 0 0 0 1 * * * * 0 1 * * * * +0x0a13 SPHLYSM2 0 0 0 1 * * * * 0 1 * * * * +0x0a14 SPUNHBL2 0 0 0 1 * * * * 0 1 * * * * +0x0a23 SPHLYSM2 0 0 0 1 * * * * 0 1 * * * * +0x0a24 SPUNHBL2 0 0 0 1 * * * * 0 1 * * * * +0x0b00 SPCSPRA2 0 0 0 1 * * * * 0 0 * * * * +0x0b01 SPCCOLDL 0 0 0 1 * * * * 0 0 * * * * +0x0b02 SPPRISM2 0 0 0 1 * * * * 0 0 * * * * +0x0b03 SPCSPRA3 0 0 0 1 * * * * 0 0 * * * * +0x0b04 SPPRISM3 0 0 0 1 * * * * 0 0 * * * * +0x1000 MWYV 1 24 4 0 * * * * 0 * * * * * +0x1100 MTAN 1 32 5 0 * * * * 0 * 1 * * * +0x1200 MDR1 2 72 13 0 * * * * 0 1 * * * * +0x1201 MDR2 2 93 13 0 * * * * 0 1 * * * * +0x1202 MDR3 2 72 13 0 * * * * 0 1 * * * * +0x1203 MDR1 2 72 13 0 MDR1_GR * * * 0 1 * * * * +0x1204 MDR1 2 72 13 0 MDR1_AQ * * * 0 1 * * * * +0x1205 MDR1 2 72 13 0 MDR1_BL * * * 0 1 * * * * +0x1206 MDR1 2 72 13 0 MDR1_BR * * * 0 1 * * * * +0x1207 MDR1 2 72 13 0 MDR1_MC * * * 0 1 * * * * +0x1208 MDR1 2 72 13 0 MDR1_PU * * * 0 1 * * * * +0x2000 MSIR 5 16 3 0 * * * * 1 * 1 * * BW +0x2100 UVOL 5 16 3 0 * * * * 0 * 1 * MS * +0x2200 MOGM 5 16 3 0 * * * * 1 * 1 1 S1 * +0x2300 MDKN 5 16 3 0 * * * * 0 * 1 1 * * +0x3000 MAKH 6 24 5 0 * * * * 0 * * * * * +0x4000 SNOMC 7 16 3 0 * * * * 1 * * * * * +0x4002 SNOMM 7 16 3 0 * * * * 1 * * * * * +0x4010 SNOWC 7 16 3 0 * * * * 1 * * * * * +0x4012 SNOWM 7 16 3 0 * * * * 1 * * * * * +0x4100 SSIMC 7 16 3 0 * * * * 1 * * * * * +0x4101 SSIMS 7 16 3 0 * * * * 1 * * * * * +0x4102 SSIMM 7 16 3 0 * * * * 1 * * * * * +0x4110 SSIWC 7 16 3 0 * * * * 1 * * * * * +0x4112 SSIWM 7 16 3 0 * * * * 1 * * * * * +0x4200 SHMCM 7 16 3 0 * * * * 1 * * * * * +0x4300 MSPLG1 7 32 5 0 * * * * 0 * * * * * +0x4400 LHMC 7 16 3 0 * * * * 1 * * * * * +0x4410 LHFC 7 16 3 0 * * * * 1 * * * * * +0x4500 LFAM 7 16 3 0 * * * * 1 * * * * * +0x4600 LDMF 7 16 3 0 * * * * 1 * * * * * +0x4700 LEMF 7 16 3 0 * * * * 1 * * * * * +0x4710 LEFF 7 16 3 0 * * * * 1 * * * * * +0x4800 LIMC 7 16 3 0 * * * * 1 * * * * * +0x5000 CHMB 8 16 3 0 * * CHMC * 1 1 1 * WQL * +0x5001 CEMB 8 16 3 0 * * CEMC * 1 1 1 * WQM * +0x5002 CDMB 8 16 3 0 * * CDMC * 1 1 1 * WQS * +0x5003 CIMB 8 16 3 0 * * CIMC * 1 1 1 * WQS WQH +0x5010 CHFB 8 16 3 0 * * CHFC * 1 1 1 * WQN * +0x5011 CEFB 8 16 3 0 * * CEFC * 1 1 1 * WQM * +0x5012 CDMB 8 16 3 0 * * CDMC * 1 1 1 * WQS * +0x5013 CIFB 8 16 3 0 * * CIFC * 1 1 1 * WQS * +0x5100 CHMB 8 16 3 0 * * CHMF * 1 1 1 * WQL * +0x5101 CEMB 8 16 3 0 * * CEMF * 1 1 1 * WQM * +0x5102 CDMB 8 16 3 0 * * CDMF * 1 1 1 * WQS * +0x5103 CIMB 8 16 3 0 * * CIMF * 1 1 1 * WQS WQH +0x5110 CHFB 8 16 3 0 * * CHFF * 1 1 1 * WQN * +0x5111 CEFB 8 16 3 0 * * CEFF * 1 1 1 * WQM * +0x5112 CDMB 8 16 3 0 * * CDMF * 1 1 1 * WQS * +0x5113 CIFB 8 16 3 0 * * CIFF * 1 1 1 * WQS * +0x5200 CHMW 8 16 3 0 * * CHMW * 1 1 1 * WQL * +0x5201 CEMW 8 16 3 0 * * CEMW * 1 1 1 * WQM * +0x5202 CDMW 8 16 3 0 * * CDMW * 1 1 1 * WQS * +0x5210 CHFW 8 16 3 0 * * CHFW * 1 1 1 * WQN * +0x5211 CEFW 8 16 3 0 * * CEFW * 1 1 1 * WQM * +0x5212 CDMW 8 16 3 0 * * CDMW * 1 1 1 * WQS * +0x5300 CHMB 8 16 3 0 * * CHMT * 1 1 0 * WQL * +0x5301 CEMB 8 16 3 0 * * CEMT * 1 1 0 * WQM * +0x5302 CDMB 8 16 3 0 * * CDMT * 1 1 0 * WQS * +0x5303 CIMB 8 16 3 0 * * CIMT * 1 1 0 * WQS WQH +0x5310 CHFB 8 16 3 0 * * CHFT * 1 1 0 * WQN * +0x5311 CEFB 8 16 3 0 * * CEFT * 1 1 0 * WQM * +0x5312 CDFB 8 16 3 0 * * CDFT * 1 1 0 * WQS * +0x5313 CIFB 8 16 3 0 * * CIFT * 1 1 0 * WQS * +0x6000 CHMB 8 16 3 0 * * CHMC * 1 1 1 * WQL * +0x6001 CEMB 8 16 3 0 * * CEMC * 1 1 1 * WQM * +0x6002 CDMB 8 16 3 0 * * CDMC * 1 1 1 * WQS * +0x6003 CIMB 8 16 3 0 * * CIMC * 1 1 1 * WQS WQH +0x6004 CDMB 8 16 3 0 * * CDMC * 1 1 1 * WQS WQH +0x6005 CHMB 8 16 3 0 * * CHMC * 1 1 1 * WQL * +0x6010 CHFB 8 16 3 0 * * CHFC * 1 1 1 * WQN * +0x6011 CEFB 8 16 3 0 * * CEFC * 1 1 1 * WQM * +0x6012 CDMB 8 16 3 0 * * CDFC * 1 1 1 * WQS * +0x6013 CIFB 8 16 3 0 * * CIFC * 1 1 1 * WQS * +0x6014 CIFB 8 16 3 0 * * CIFC * 1 1 1 * WQS WQH +0x6015 CHFB 8 16 3 0 * * CHFC * 1 1 1 * WQN * +0x6100 CHMB 8 16 3 0 * * CHMF * 1 1 1 * WQL * +0x6101 CEMB 8 16 3 0 * * CEMF * 1 1 1 * WQM * +0x6102 CDMB 8 16 3 0 * * CDMF * 1 1 1 * WQS * +0x6103 CIMB 8 16 3 0 * * CIMF * 1 1 1 * WQS WQH +0x6104 CDMB 8 16 3 0 * * CDMF * 1 1 1 * WQS WQH +0x6105 CHMB 8 16 3 0 * * CHMF * 1 1 1 * WQL * +0x6110 CHFB 8 16 3 0 * * CHFF * 1 1 1 * WQN * +0x6111 CEFB 8 16 3 0 * * CEFF * 1 1 1 * WQM * +0x6112 CDMB 8 16 3 0 * * CDMF * 1 1 1 * WQS * +0x6113 CIFB 8 16 3 0 * * CIFF * 1 1 1 * WQS * +0x6114 CIFB 8 16 3 0 * * CIFF * 1 1 1 * WQS WQH +0x6115 CHFB 8 16 3 0 * * CHFF * 1 1 1 * WQN * +0x6200 CHMW 8 16 3 0 * * CHMW * 1 1 1 * WQL * +0x6201 CEMW 8 16 3 0 * * CEMW * 1 1 1 * WQM * +0x6202 CDMW 8 16 3 0 * * CDMW * 1 1 1 * WQS * +0x6204 CDMW 8 16 3 0 * * CDMW * 1 1 1 * WQS WQH +0x6205 CHMW 8 16 3 0 * * CHMW * 1 1 1 * WQL * +0x6210 CHFW 8 16 3 0 * * CHFW * 1 1 1 * WQN * +0x6211 CEFW 8 16 3 0 * * CEFW * 1 1 1 * WQM * +0x6212 CDMW 8 16 3 0 * * CDMW * 1 1 1 * WQS * +0x6214 CDMW 8 16 3 0 * * CDMW * 1 1 1 * WQS WQH +0x6215 CHFW 8 16 3 0 * * CHFW * 1 1 1 * WQN * +0x6300 CHMB 8 16 3 0 * * CHMT * 1 1 0 * WQL * +0x6301 CEMB 8 16 3 0 * * CEMT * 1 1 0 * WQM * +0x6302 CDMB 8 16 3 0 * * CDMT * 1 1 0 * WQS * +0x6303 CIMB 8 16 3 0 * * CIMT * 1 1 0 * WQS WQH +0x6304 CDMB 8 16 3 0 * * CDMT * 1 1 0 * WQS WQH +0x6305 CHMB 8 16 3 0 * * CHMT * 1 1 0 * WQL * +0x6310 CHFB 8 16 3 0 * * CHFT * 1 1 0 * WQN * +0x6311 CEFB 8 16 3 0 * * CEFT * 1 1 0 * WQM * +0x6312 CDMB 8 16 3 0 * * CDMT * 1 1 0 * WQS * +0x6313 CIFB 8 16 3 0 * * CIFT * 1 1 0 * WQS * +0x6314 CIFB 8 16 3 0 * * CIFT * 1 1 0 * WQS WQH +0x6315 CHFB 8 16 3 0 * * CHFT * 1 1 0 * WQN * +0x6400 UDRZ 9 16 3 0 * * * * 1 * 1 0 WPM * +0x6401 UELM 9 16 3 0 * * * * 0 * 0 0 WPM * +0x6402 CMNK 9 16 3 0 * * * * 1 * 0 * WPM * +0x6403 MSKL 9 16 3 0 * * * * 1 * 1 * WPM * +0x6404 USAR 9 16 3 0 * * * * 0 * 0 0 WPL * +0x6405 MDGU 9 16 3 0 * * * * 1 * 1 * WPM * +0x6406 MDGU 9 24 7 0 * * * * 1 * 1 * WPM * +0x6500 CHMM 8 16 3 0 * * CHMM * 1 1 1 * WQL * +0x6510 CHFM 8 16 3 0 * * CHFM * 1 1 1 * WQN * +0x7000 MOGH 11 16 3 0 * * * * 1 * * * * * +0x7001 MOGN 11 16 3 0 * * * * 1 * * * * * +0x7100 MBAS 11 32 5 0 * * * * 0 * * * * * +0x7101 MBAS 11 32 5 0 MBAS_GR * * * 0 * * * * * +0x7200 MBER 11 16 3 0 MBER_BL * * * 0 * * * * * +0x7201 MBER 11 16 3 0 * * * * 0 * * * * * +0x7202 MBER 11 16 3 0 MBER_CA * * * 0 * * * * * +0x7203 MBER 11 16 3 0 MBER_PO * * * 0 * * * * * +0x7300 MEAE 10 32 5 0 * * * * 0 1 * * * * +0x7301 MEAS 10 16 3 0 * * * * 0 1 * * * * +0x7302 MEAE 10 32 5 0 MEAE_SH * * * 0 1 * * * * +0x7310 MFIE 10 16 3 4 * * * * 0 1 * * * * +0x7311 MFIS 10 16 3 4 * * * * 0 1 * * * * +0x7320 MAIR 10 32 5 0 * * * 1 0 1 * * * * +0x7321 MAIS 10 16 3 0 * * * 1 0 1 * * * * +0x7400 MDOG 11 16 3 0 MDOG_WI * * * 0 * * * * * +0x7401 MDOG 11 16 3 0 MDOG_WA * * * 0 * * * * * +0x7402 MDOG 11 16 3 0 MDOG_MO * * * 0 * * * * * +0x7500 MDOP 11 16 3 0 * * * * 0 * * * * * +0x7501 MDOP 11 16 3 0 MDOP_GR * * * 0 * * * * * +0x7600 METT 11 16 3 0 * * * * 0 * * * * * +0x7700 MGHL 11 16 3 0 * * * * 0 * * * * * +0x7701 MGHL 11 16 3 0 MGHL_RE * * * 0 * * * * * +0x7702 MGHL 11 16 3 0 MGHL_GA * * * 0 * * * * * +0x7703 MSHD 10 16 3 0 * * * 1 0 1 * * * * +0x7800 MGIB 11 16 3 0 * * * * 0 * * * * * +0x7900 MSLI 11 24 4 0 MSLI_GR * * 1 0 * * * * * +0x7901 MSLI 11 24 4 0 MSLI_OL * * 1 0 * * * * * +0x7902 MSLI 11 24 4 0 MSLI_MU * * 1 0 * * * * * +0x7903 MSLI 11 24 4 0 MSLI_OC * * 1 0 * * * * * +0x7904 MSLI 11 24 4 0 * * * 1 0 * * * * * +0x7a00 MSPI 11 16 3 0 MSPI_GI * * * 0 * * * * * +0x7a01 MSPI 11 16 3 0 MSPI_HU * * * 0 * * * * * +0x7a02 MSPI 11 16 3 0 MSPI_PH * * * 0 * * * * * +0x7a03 MSPI 11 16 3 0 MSPI_SW * * * 0 * * * * * +0x7a04 MSPI 11 16 3 0 MSPI_WR * * * 0 * * * * * +0x7b00 MWLF 11 16 3 0 * * * * 0 * * * * * +0x7b01 MWLF 11 16 3 0 MWLF_WO * * * 0 * * * * * +0x7b02 MWLF 11 16 3 0 MWLF_DI * * * 0 * * * * * +0x7b03 MWLF 11 16 3 0 MWLF_WI * * * 0 * * * * * +0x7b04 MWLF 11 16 3 0 MWLF_VA * * * 0 * * * * * +0x7b05 MWLF 11 16 3 0 MWLF_DR * * * 0 * * * * * +0x7b06 MWLS 11 16 3 0 * * * * 0 * * * * * +0x7c00 MXVT 11 16 3 0 * * * * 1 * * * * * +0x7c01 MTAS 11 16 3 0 * * * * 0 * * * * * +0x7d00 MZOM 11 16 3 0 * * * * 1 * * * * * +0x7e00 MWER 11 16 3 0 * * * * 0 * * * * * +0x7e01 MGWE 11 16 3 0 * * * * 0 * * * * * +0x7f00 MTRO 10 16 3 0 * * * * 0 1 * * * * +0x7f01 MMIN 10 16 3 0 * * * * 0 1 * * * * +0x7f02 MBEH 10 32 5 0 * * * * 0 1 * * * * +0x7f03 MIMP 10 16 3 0 * * * * 0 1 * * * * +0x7f04 MIGO 10 32 5 0 * * * * 0 1 * * * * +0x7f05 MDJI 10 16 3 0 * * * * 0 1 * * * * +0x7f06 MDJL 10 16 3 0 * * * * 0 1 * * * * +0x7f07 MGLC 10 16 3 0 * * * * 0 1 * * * * +0x7f08 MOTY 10 24 5 0 * * * * 0 1 * * * * +0x7f09 MSAH 10 16 3 0 * * * * 0 1 * * * * +0x7f0a MGCP 10 16 3 0 * * * * 0 1 * * * * +0x7f0b MGCL 10 16 3 0 * * * * 0 1 * * * * +0x7f0c MKUO 10 16 3 0 * * * * 0 1 * * * * +0x7f0d MLIC 10 16 3 0 * * * * 0 1 * * * * +0x7f0e MDLI 10 16 3 0 * * * * 0 1 * * * * +0x7f0f MTRS 10 16 3 0 * * * * 0 1 * * * * +0x7f10 MRAK 10 16 3 0 * * * * 0 1 * * * * +0x7f11 MUMB 10 16 3 0 * * * * 0 1 * * * * +0x7f12 MVAM 10 16 3 0 * * * * 0 1 * * * * +0x7f13 MSNK 10 16 3 0 * * * * 0 1 * * * * +0x7f14 MGIT 10 16 3 0 * * * * 0 1 * * * * +0x7f15 MBES 10 16 3 0 * * * * 0 1 * * * * +0x7f16 AMOO 10 32 5 0 * * * * 0 1 * * * * +0x7f17 ARAB 10 12 3 0 * * * * 0 1 * * * * +0x7f18 ADER 10 16 3 0 * * * * 0 1 * * * * +0x7f19 MDSW 10 16 3 0 * * * * 0 1 * * * * +0x7f20 AGRO 10 12 3 0 * * * * 0 1 * * * * +0x7f21 APHE 10 12 3 0 * * * * 0 1 * * * * +0x7f22 MVAF 10 16 3 0 * * * * 0 1 * * * * +0x7f23 MSAT 10 16 3 0 * * * * 0 1 * * * * +0x7f24 NPIR 10 16 3 0 * * * * 0 1 * * * * +0x7f27 MDRO 10 16 3 0 * * * * 0 1 * * * * +0x7f28 MKUL 10 16 3 0 * * * * 0 1 * * * * +0x7f29 MFDR 10 16 3 0 * * * * 0 1 * * * * +0x7f2a NSAI 10 16 3 0 * * * * 0 1 * * * * +0x7f2b MMAX 10 16 3 0 * * * * 0 0 * * * * +0x7f2c NSOL 10 16 3 0 * * * * 0 1 * * * * +0x7f2d MWFM 10 16 3 0 * * * * 0 1 * * * * +0x7f2e MRAV 10 32 5 0 * * * * 0 1 * * * * +0x7f2f MSPS 10 16 3 0 * * * * 0 1 * * * * +0x7f30 NBOH 10 16 3 0 * * * * 0 1 * * * * +0x7f31 NELL 10 16 3 0 * * * * 0 1 * * * * +0x7f32 MSLY 10 16 3 0 * * * * 0 1 * * * * +0x7f33 MKUR 10 16 3 0 * * * * 0 0 * * * * +0x7f34 MDOC 10 16 3 0 * * * * 0 0 * * * * +0x7f35 MMIS 10 16 3 0 * * * 1 0 1 * * * * +0x7f36 NSHD 10 16 3 0 * * * * 0 1 * * * * +0x7f37 NIRE 10 16 3 0 * * * * 0 1 * * * * +0x7f38 MEYE 10 16 3 0 * * * * 0 0 * * * * +0x7f39 MMST 10 16 3 1 * * * 0 0 0 * * * * +0x7f3a NIRO 10 16 3 0 * * * * 0 1 * * * * +0x8000 MGNL 4 16 3 0 * * * * * * * * S1 HB +0x8100 MHOB 4 16 3 0 * * * * * * * * S1 BW +0x8200 MKOB 4 16 3 0 * * * * * * * * SS BW +0x9000 MOGR 12 16 3 0 * * * * 1 * * * * * +0xa000 MWYV 13 16 3 0 * * * * 0 * * * * * +0xa100 MCAR 13 16 3 0 * * * * 0 * * * * * +0xb000 ACOW 14 32 5 0 * * * * 0 * * * * * +0xb100 AHRS 14 32 5 0 * * * * 0 * * * * * +0xb200 NBEGL 14 16 3 0 * * * * 1 * * * * * +0xb210 NPROL 14 16 3 0 * * * * 1 * * * * * +0xb300 NBOYL 14 16 3 0 * * * * 1 * * * * * +0xb310 NGRLL 14 16 3 0 * * * * 1 * * * * * +0xb400 NFAML 14 16 3 0 * * * * 1 * * * * * +0xb410 NFAWL 14 16 3 0 * * * * 1 * * * * * +0xb500 NSIML 14 16 3 0 * * * * 1 * * * * * +0xb510 NSIWL 14 16 3 0 * * * * 1 * * * * * +0xb600 NNOML 14 16 3 0 * * * * 1 * * * * * +0xb610 NNOWL 14 16 3 0 * * * * 1 * * * * * +0xb700 NSLVL 14 16 3 0 * * * * 1 * * * * * +0xc000 ABAT 15 12 3 0 * * * * 0 * * * * * +0xc100 ACAT 15 12 3 0 * * * * 0 * * * * * +0xc200 ACHK 15 12 3 0 * * * * 0 * * * * * +0xc300 ARAT 15 12 3 0 * * * * 0 * * * * * +0xc400 ASQU 15 12 3 0 * * * * 0 * * * * * +0xc500 ABAT 15 12 3 0 * * * * 0 * * * * * +0xc600 NBEGH 15 16 3 0 * * * * 1 * * * * * +0xc610 NPROH 15 16 3 0 * * * * 1 * * * * * +0xc700 NBOYH 15 16 3 0 * * * * 1 * * * * * +0xc710 NGRLH 15 16 3 0 * * * * 1 * * * * * +0xc800 NFAMH 15 16 3 0 * * * * 1 * * * * * +0xc810 NFAWH 15 16 3 0 * * * * 1 * * * * * +0xc900 NSIMH 15 16 3 0 * * * * 1 * * * * * +0xc910 NSIWH 15 16 3 0 * * * * 1 * * * * * +0xca00 NNOMH 15 16 3 0 * * * * 1 * * * * * +0xca10 NNOWH 15 16 3 0 * * * * 1 * * * * * +0xcb00 NSLVH 15 16 3 0 * * * * 1 * * * * * +0xd000 AEAGG1 16 0 3 0 * * * * 0 * * * * * +0xd100 AGULG1 16 0 3 0 * * * * 0 * * * * * +0xd200 AVULG1 16 0 3 0 * * * * 0 * * * * * +0xd300 ABIRG1 16 0 3 0 * * * * 0 * * * * * +0xd400 ABIRG1 16 0 3 0 * * * * 0 * * * * * +0xe000 MCYC 17 48 5 0 * * * * * * * * * * +0xe010 METN 17 48 5 0 * * * * * * * * * * +0xe020 MGFR 17 32 5 0 * * * * * * * * * * +0xe040 MHIS 17 16 3 0 * * * * * * * * * * +0xe050 MLER 17 16 3 0 * * * * * * * * * * +0xe060 MLIC 17 16 3 0 * * * * * * * * * * +0xe070 MMIN 17 24 3 0 * * * * * * * * * * +0xe080 MMUM 17 16 3 0 * * * * * * * * * * +0xe090 MTAN 17 16 3 0 * * * * * * * * * * +0xe0a0 MTIC 17 20 3 0 * * * * * * * * * * +0xe0b0 MTRO 17 16 3 0 * * * * * * * * * * +0xe0c0 MTSN 17 24 3 0 * * * * * * * * * * +0xe0d0 MUMB 17 24 3 0 * * * * * * * * * * +0xe200 MBET 17 24 3 0 * * * * * * * * * * +0xe210 MBFI 17 12 3 0 * * * * * * * * * * +0xe220 MBBM 17 16 3 0 * * * * * * * * * * +0xe230 MBRH 17 64 7 0 * * * * * * * * * * +0xe300 MGHO 17 16 3 0 * * * 1 * * * * * * +0xe310 MGH2 17 16 3 0 * * * * * * * * * * +0xe320 MGH3 17 16 3 0 * * * * * * * * * * +0xe400 MGO1 17 16 3 0 * * * * * * * * * * +0xe410 MGO2 17 16 3 0 * * * * * * * * * * +0xe420 MGO3 17 16 3 0 * * * * * * * * * * +0xe430 MGO4 17 16 3 0 * * * * * * * * * * +0xe500 MLIZ 17 24 3 0 * * * * * * * * * * +0xe510 MLI2 17 24 3 0 * * * * * * * * * * +0xe520 MLI3 17 32 5 0 * * * * * * * * * * +0xe600 MMYC 17 16 3 0 * * * * * * * * * * +0xe610 MMY2 17 16 3 0 * * * * * * * * * * +0xe700 MNO1 17 20 3 0 * * * * * * * * * * +0xe710 MNO2 17 20 3 0 * * * * * * * * * * +0xe720 MNO3 17 24 3 0 * * * * * * * * * * +0xe800 MOR1 17 16 3 0 * * * * * * * * * * +0xe810 MOR2 17 16 3 0 * * * * * * * * * * +0xe820 MOR3 17 16 3 0 * * * * * * * * * * +0xe830 MOR4 17 16 3 0 * * * * * * * * * * +0xe840 MOR5 17 16 3 0 * * * * * * * * * * +0xe900 MSAL 17 16 3 0 * * * * * * * * * * +0xe910 MSA2 17 16 3 0 * * * * * * * * * * +0xea00 MSHR 17 24 3 0 * * * * * * * * * * +0xea10 MSH1 17 16 3 0 * * * 1 * * * * * * +0xea20 MSH2 17 24 3 0 * * * 1 * * * * * * +0xeb00 MSKT 17 16 3 0 * * * * * * * * * * +0xeb10 MSKA 17 16 3 0 * * * * * * * * * * +0xeb20 MSKB 17 24 3 0 * * * * * * * * * * +0xec00 MWIG 17 16 3 0 * * * * * * * * * * +0xec10 MWI2 17 16 3 0 * * * * * * * * * * +0xec20 MWI3 17 16 3 0 * * * * * * * * * * +0xed00 MYU1 17 16 3 0 * * * * * * * * * * +0xed10 MYU2 17 16 3 0 * * * * * * * * * * +0xed20 MYU3 17 16 3 0 * * * * * * * * * * +0xee00 MZO2 17 16 3 0 * * * * * * * * * * +0xee10 MZO3 17 16 3 0 * * * * * * * * * * diff --git a/src/org/infinity/resource/cre/decoder/tables/avatars-bg2tob.2da b/src/org/infinity/resource/cre/decoder/tables/avatars-bg2tob.2da new file mode 100644 index 000000000..a1fe254c8 --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/tables/avatars-bg2tob.2da @@ -0,0 +1,373 @@ +2DA V1.0 +* + RESREF TYPE ELLIPSE SPACE BLENDING PALETTE PALETTE2 RESREF2 TRANSLUCENT CLOWN SPLIT HELMET WEAPON HEIGHT HEIGHT_SHIELD +0x0000 SPRING 0 0 0 0 * * * * 1 0 * * * * +0x0001 SPFLAMES 0 0 0 7 * * * * 0 0 * * * * +0x0002 SPRDRASI 0 0 0 7 * * * * 0 0 * * * * +0x0003 SPFLAMES 0 0 0 7 SPFLAMEB * * * 0 0 * * * * +0x0004 SPRDRASI 0 0 0 7 SPGDRASI * * * 0 0 * * * * +0x0100 SPCHUNKS 0 0 0 0 * * SPSHADOW * 1 1 * * * * +0x0200 SPBLOOD 0 0 0 0 * * * * 0 0 0 * * * +0x0210 SPBLOOD 0 0 0 0 * * * * 0 0 1 * * * +0x0220 SPBLOOD 0 0 0 0 * * * * 0 0 2 * * * +0x0230 SPBLOOD 0 0 0 0 * * * * 0 0 3 * * * +0x0240 SPBLOOD 0 0 0 0 * * * * 0 0 4 * * * +0x0300 SPSMPUFF 0 0 0 0 * * * 1 1 * * * * * +0x0400 SKLH 0 0 0 0 * * SPSHADOW * 0 1 * * * * +0x0410 GLPHWRDH 0 0 0 1 * * SPSHADOW * 0 1 * * * * +0x0500 STNKCLDD 0 0 0 0 * * * * 1 0 1 * * * +0x0510 STNKCLDD 0 0 0 0 * * * * 1 0 0 * * * +0x0520 SPHORPUF 0 0 0 1 * * * * 0 0 * * * * +0x0600 STNKCLDD 0 0 0 0 * * * * 1 0 1 * * * +0x0610 SPICESTM 0 0 0 1 * * * * 0 0 0 * * * +0x0700 GREASEH 0 0 0 0 * * * * 0 0 * * * * +0x0710 GREASED 0 0 0 0 * * * * 0 1 * * * * +0x0800 WEBENTH 0 0 0 0 * * * * 1 0 * * * * +0x0810 WEBENTD 0 0 0 0 * * * * 1 0 * * * * +0x0900 STNKCLDD 0 0 0 0 * * * * 1 0 1 * * * +0x0910 SPMETSWM 0 0 0 1 * * * * 0 1 0 * * * +0x0a00 SPHORPUF 0 0 0 1 * * * * 0 0 * * * * +0x0a01 SPWRDFLD 0 0 0 1 * * * * 0 1 * * * * +0x0a02 SPENTAAI 0 0 0 1 * * * * 0 1 * * * * +0x0a03 SPHLYSM2 0 0 0 1 * * * * 0 1 * * * * +0x0a04 SPUNHBL2 0 0 0 1 * * * * 0 1 * * * * +0x0a10 SPHORPUF 0 0 0 1 * * * * 0 0 * * * * +0x0a11 SPWRDFLD 0 0 0 1 * * * * 0 1 * * * * +0x0a12 SPENTAAI 0 0 0 1 * * * * 0 1 * * * * +0x0a13 SPHLYSM2 0 0 0 1 * * * * 0 1 * * * * +0x0a14 SPUNHBL2 0 0 0 1 * * * * 0 1 * * * * +0x0a23 SPHLYSM2 0 0 0 1 * * * * 0 1 * * * * +0x0a24 SPUNHBL2 0 0 0 1 * * * * 0 1 * * * * +0x0b00 SPCSPRA2 0 0 0 1 * * * * 0 0 * * * * +0x0b01 SPCCOLDL 0 0 0 1 * * * * 0 0 * * * * +0x0b02 SPPRISM2 0 0 0 1 * * * * 0 0 * * * * +0x0b03 SPCSPRA3 0 0 0 1 * * * * 0 0 * * * * +0x0b04 SPPRISM3 0 0 0 1 * * * * 0 0 * * * * +0x0c00 SPSTRMVA 0 0 0 1 * * * * 0 1 * * SPSTRMVB * +0x0c10 SPSTRMVA 0 0 0 1 * * * * 0 1 * * SPSTRMVB * +0x1000 MWYV 1 24 4 0 * * * * 0 * * * * * +0x1100 MTAN 1 32 5 0 * * * * 0 * 1 * * * +0x1200 MDR1 2 72 13 0 * * * * 0 1 * * * * +0x1201 MDR2 2 93 13 0 * * * * 0 1 * * * * +0x1202 MDR3 2 72 13 0 * * * * 0 1 * * * * +0x1203 MDR1 2 72 13 0 MDR1_GR * * * 0 1 * * * * +0x1204 MDR1 2 72 13 0 MDR1_AQ * * * 0 1 * * * * +0x1205 MDR1 2 72 13 0 MDR1_BL * * * 0 1 * * * * +0x1206 MDR1 2 72 13 0 MDR1_BR * * * 0 1 * * * * +0x1207 MDR1 2 72 13 0 MDR1_MC * * * 0 1 * * * * +0x1208 MDR1 2 72 13 0 MDR1_PU * * * 0 1 * * * * +0x1300 MDEM 3 32 5 0 * * * * 0 1 * * * * +0x2000 MSIR 5 16 3 0 * * * * 1 * 1 * * BW +0x2100 UVOL 5 16 3 0 * * * * 0 * 1 * MS * +0x2200 MOGM 5 16 3 0 * * * * 1 * 1 1 S1 * +0x2300 MDKN 5 16 3 0 * * * * 0 * 1 1 * * +0x3000 MAKH 6 24 5 0 * * * * 0 * * * * * +0x4000 SNOMC 7 16 3 0 * * * * 1 * * * * * +0x4002 SNOMM 7 16 3 0 * * * * 1 * * * * * +0x4010 SNOWC 7 16 3 0 * * * * 1 * * * * * +0x4012 SNOWM 7 16 3 0 * * * * 1 * * * * * +0x4100 SSIMC 7 16 3 0 * * * * 1 * * * * * +0x4101 SSIMS 7 16 3 0 * * * * 1 * * * * * +0x4102 SSIMM 7 16 3 0 * * * * 1 * * * * * +0x4110 SSIWC 7 16 3 0 * * * * 1 * * * * * +0x4112 SSIWM 7 16 3 0 * * * * 1 * * * * * +0x4200 SHMCM 7 16 3 0 * * * * 1 * * * * * +0x4300 MSPLG1 7 32 5 0 * * * * 0 * * * * * +0x4400 LHMC 7 16 3 0 * * * * 1 * * * * * +0x4410 LHFC 7 16 3 0 * * * * 1 * * * * * +0x4500 LFAM 7 16 3 0 * * * * 1 * * * * * +0x4600 LDMF 7 16 3 0 * * * * 1 * * * * * +0x4700 LEMF 7 16 3 0 * * * * 1 * * * * * +0x4710 LEFF 7 16 3 0 * * * * 1 * * * * * +0x4800 LIMC 7 16 3 0 * * * * 1 * * * * * +0x5000 CHMB 8 16 3 0 * * CHMC * 1 1 1 * WQL * +0x5001 CEMB 8 16 3 0 * * CEMC * 1 1 1 * WQM * +0x5002 CDMB 8 16 3 0 * * CDMC * 1 1 1 * WQS * +0x5003 CIMB 8 16 3 0 * * CIMC * 1 1 1 * WQS WQH +0x5010 CHFB 8 16 3 0 * * CHFC * 1 1 1 * WQN * +0x5011 CEFB 8 16 3 0 * * CEFC * 1 1 1 * WQM * +0x5012 CDMB 8 16 3 0 * * CDMC * 1 1 1 * WQS * +0x5013 CIFB 8 16 3 0 * * CIFC * 1 1 1 * WQS * +0x5100 CHMB 8 16 3 0 * * CHMF * 1 1 1 * WQL * +0x5101 CEMB 8 16 3 0 * * CEMF * 1 1 1 * WQM * +0x5102 CDMB 8 16 3 0 * * CDMF * 1 1 1 * WQS * +0x5103 CIMB 8 16 3 0 * * CIMF * 1 1 1 * WQS WQH +0x5110 CHFB 8 16 3 0 * * CHFF * 1 1 1 * WQN * +0x5111 CEFB 8 16 3 0 * * CEFF * 1 1 1 * WQM * +0x5112 CDMB 8 16 3 0 * * CDMF * 1 1 1 * WQS * +0x5113 CIFB 8 16 3 0 * * CIFF * 1 1 1 * WQS * +0x5200 CHMW 8 16 3 0 * * CHMW * 1 1 1 * WQL * +0x5201 CEMW 8 16 3 0 * * CEMW * 1 1 1 * WQM * +0x5202 CDMW 8 16 3 0 * * CDMW * 1 1 1 * WQS * +0x5210 CHFW 8 16 3 0 * * CHFW * 1 1 1 * WQN * +0x5211 CEFW 8 16 3 0 * * CEFW * 1 1 1 * WQM * +0x5212 CDMW 8 16 3 0 * * CDMW * 1 1 1 * WQS * +0x5300 CHMB 8 16 3 0 * * CHMT * 1 1 0 * WQL * +0x5301 CEMB 8 16 3 0 * * CEMT * 1 1 0 * WQM * +0x5302 CDMB 8 16 3 0 * * CDMT * 1 1 0 * WQS * +0x5303 CIMB 8 16 3 0 * * CIMT * 1 1 0 * WQS WQH +0x5310 CHFB 8 16 3 0 * * CHFT * 1 1 0 * WQN * +0x5311 CEFB 8 16 3 0 * * CEFT * 1 1 0 * WQM * +0x5312 CDFB 8 16 3 0 * * CDFT * 1 1 0 * WQS * +0x5313 CIFB 8 16 3 0 * * CIFT * 1 1 0 * WQS * +0x6000 CHMB 8 16 3 0 * * CHMC * 1 1 1 * WQL * +0x6001 CEMB 8 16 3 0 * * CEMC * 1 1 1 * WQM * +0x6002 CDMB 8 16 3 0 * * CDMC * 1 1 1 * WQS * +0x6003 CIMB 8 16 3 0 * * CIMC * 1 1 1 * WQS WQH +0x6004 CDMB 8 16 3 0 * * CDMC * 1 1 1 * WQS WQH +0x6005 CHMB 8 16 3 0 * * CHMC * 1 1 1 * WQL * +0x6010 CHFB 8 16 3 0 * * CHFC * 1 1 1 * WQN * +0x6011 CEFB 8 16 3 0 * * CEFC * 1 1 1 * WQM * +0x6012 CDMB 8 16 3 0 * * CDFC * 1 1 1 * WQS * +0x6013 CIFB 8 16 3 0 * * CIFC * 1 1 1 * WQS * +0x6014 CIFB 8 16 3 0 * * CIFC * 1 1 1 * WQS WQH +0x6015 CHFB 8 16 3 0 * * CHFC * 1 1 1 * WQN * +0x6100 CHMB 8 16 3 0 * * CHMF * 1 1 1 * WQL * +0x6101 CEMB 8 16 3 0 * * CEMF * 1 1 1 * WQM * +0x6102 CDMB 8 16 3 0 * * CDMF * 1 1 1 * WQS * +0x6103 CIMB 8 16 3 0 * * CIMF * 1 1 1 * WQS WQH +0x6104 CDMB 8 16 3 0 * * CDMF * 1 1 1 * WQS WQH +0x6105 CHMB 8 16 3 0 * * CHMF * 1 1 1 * WQL * +0x6110 CHFB 8 16 3 0 * * CHFF * 1 1 1 * WQN * +0x6111 CEFB 8 16 3 0 * * CEFF * 1 1 1 * WQM * +0x6112 CDMB 8 16 3 0 * * CDMF * 1 1 1 * WQS * +0x6113 CIFB 8 16 3 0 * * CIFF * 1 1 1 * WQS * +0x6114 CIFB 8 16 3 0 * * CIFF * 1 1 1 * WQS WQH +0x6115 CHFB 8 16 3 0 * * CHFF * 1 1 1 * WQN * +0x6200 CHMW 8 16 3 0 * * CHMW * 1 1 1 * WQL * +0x6201 CEMW 8 16 3 0 * * CEMW * 1 1 1 * WQM * +0x6202 CDMW 8 16 3 0 * * CDMW * 1 1 1 * WQS * +0x6204 CDMW 8 16 3 0 * * CDMW * 1 1 1 * WQS WQH +0x6205 CHMW 8 16 3 0 * * CHMW * 1 1 1 * WQL * +0x6210 CHFW 8 16 3 0 * * CHFW * 1 1 1 * WQN * +0x6211 CEFW 8 16 3 0 * * CEFW * 1 1 1 * WQM * +0x6212 CDMW 8 16 3 0 * * CDMW * 1 1 1 * WQS * +0x6214 CDMW 8 16 3 0 * * CDMW * 1 1 1 * WQS WQH +0x6215 CHFW 8 16 3 0 * * CHFW * 1 1 1 * WQN * +0x6300 CHMB 8 16 3 0 * * CHMT * 1 1 0 * WQL * +0x6301 CEMB 8 16 3 0 * * CEMT * 1 1 0 * WQM * +0x6302 CDMB 8 16 3 0 * * CDMT * 1 1 0 * WQS * +0x6303 CIMB 8 16 3 0 * * CIMT * 1 1 0 * WQS WQH +0x6304 CDMB 8 16 3 0 * * CDMT * 1 1 0 * WQS WQH +0x6305 CHMB 8 16 3 0 * * CHMT * 1 1 0 * WQL * +0x6310 CHFB 8 16 3 0 * * CHFT * 1 1 0 * WQN * +0x6311 CEFB 8 16 3 0 * * CEFT * 1 1 0 * WQM * +0x6312 CDMB 8 16 3 0 * * CDMT * 1 1 0 * WQS * +0x6313 CIFB 8 16 3 0 * * CIFT * 1 1 0 * WQS * +0x6314 CIFB 8 16 3 0 * * CIFT * 1 1 0 * WQS WQH +0x6315 CHFB 8 16 3 0 * * CHFT * 1 1 0 * WQN * +0x6400 UDRZ 9 16 3 0 * * * * 1 * 1 0 WPM * +0x6401 UELM 9 16 3 0 * * * * 0 * 0 0 WPM * +0x6402 CMNK 9 16 3 0 * * * * 1 * 0 * WPM * +0x6403 MSKL 9 16 3 0 * * * * 1 * 1 * WPM * +0x6404 USAR 9 16 3 0 * * * * 0 * 0 0 WPL * +0x6405 MDGU 9 16 3 0 * * * * 1 * 1 * WPM * +0x6406 MDGU 9 24 7 0 * * * * 1 * 1 * WPM * +0x6500 CHMM 8 16 3 0 * * CHMM * 1 1 1 * WQL * +0x6510 CHFM 8 16 3 0 * * CHFM * 1 1 1 * WQN * +0x7000 MOGH 11 16 3 0 * * * * 1 * * * * * +0x7001 MOGN 11 16 3 0 * * * * 1 * * * * * +0x7100 MBAS 11 32 5 0 * * * * 0 * * * * * +0x7101 MBAS 11 32 5 0 MBAS_GR * * * 0 * * * * * +0x7200 MBER 11 16 3 0 MBER_BL * * * 0 * * * * * +0x7201 MBER 11 16 3 0 * * * * 0 * * * * * +0x7202 MBER 11 16 3 0 MBER_CA * * * 0 * * * * * +0x7203 MBER 11 16 3 0 MBER_PO * * * 0 * * * * * +0x7300 MEAE 10 32 5 0 * * * * 0 1 * * * * +0x7301 MEAS 10 16 3 0 * * * * 0 1 * * * * +0x7302 MEAE 10 32 5 0 MEAE_SH * * * 0 1 * * * * +0x7310 MFIE 10 16 3 4 * * * * 0 1 * * * * +0x7311 MFIS 10 16 3 4 * * * * 0 1 * * * * +0x7312 MFIE 10 16 3 4 MFIEG1B MFIEG2B * * 0 1 * * * * +0x7313 MFIS 10 16 3 4 MFIEG1B MFIEG2B * * 0 1 * * * * +0x7314 MFIE 10 16 3 4 MFIEG31 MFIEG3B * * 0 1 * * * * +0x7320 MAIR 10 32 5 0 * * * 1 0 1 * * * * +0x7321 MAIS 10 16 3 0 * * * 1 0 1 * * * * +0x7400 MDOG 11 16 3 0 MDOG_WI * * * 0 * * * * * +0x7401 MDOG 11 16 3 0 MDOG_WA * * * 0 * * * * * +0x7402 MDOG 11 16 3 0 MDOG_MO * * * 0 * * * * * +0x7500 MDOP 11 16 3 0 * * * * 0 * * * * * +0x7501 MDOP 11 16 3 0 MDOP_GR * * * 0 * * * * * +0x7600 METT 11 16 3 0 * * * * 0 * * * * * +0x7700 MGHL 11 16 3 0 * * * * 0 * * * * * +0x7701 MGHL 11 16 3 0 MGHL_RE * * * 0 * * * * * +0x7702 MGHL 11 16 3 0 MGHL_GA * * * 0 * * * * * +0x7703 MSHD 10 16 3 0 * * * 1 0 1 * * * * +0x7800 MGIB 11 16 3 0 * * * * 0 * * * * * +0x7900 MSLI 11 24 4 0 MSLI_GR * * 1 0 * * * * * +0x7901 MSLI 11 24 4 0 MSLI_OL * * 1 0 * * * * * +0x7902 MSLI 11 24 4 0 MSLI_MU * * 1 0 * * * * * +0x7903 MSLI 11 24 4 0 MSLI_OC * * 1 0 * * * * * +0x7904 MSLI 11 24 4 0 * * * 1 0 * * * * * +0x7a00 MSPI 11 16 3 0 MSPI_GI * * * 0 * * * * * +0x7a01 MSPI 11 16 3 0 MSPI_HU * * * 0 * * * * * +0x7a02 MSPI 11 16 3 0 MSPI_PH * * * 0 * * * * * +0x7a03 MSPI 11 16 3 0 MSPI_SW * * * 0 * * * * * +0x7a04 MSPI 11 16 3 0 MSPI_WR * * * 0 * * * * * +0x7b00 MWLF 11 16 3 0 * * * * 0 * * * * * +0x7b01 MWLF 11 16 3 0 MWLF_WO * * * 0 * * * * * +0x7b02 MWLF 11 16 3 0 MWLF_DI * * * 0 * * * * * +0x7b03 MWLF 11 16 3 0 MWLF_WI * * * 0 * * * * * +0x7b04 MWLF 11 16 3 0 MWLF_VA * * * 0 * * * * * +0x7b05 MWLF 11 16 3 0 MWLF_DR * * * 0 * * * * * +0x7b06 MWLS 11 16 3 0 * * * * 0 * * * * * +0x7c00 MXVT 11 16 3 0 * * * * 1 * * * * * +0x7c01 MTAS 11 16 3 0 * * * * 0 * * * * * +0x7d00 MZOM 11 16 3 0 * * * * 1 * * * * * +0x7e00 MWER 11 16 3 0 * * * * 0 * * * * * +0x7e01 MGWE 11 16 3 0 * * * * 0 * * * * * +0x7f00 MTRO 10 16 3 0 * * * * 0 1 * * * * +0x7f01 MMIN 10 16 3 0 * * * * 0 1 * * * * +0x7f02 MBEH 10 32 5 0 * * * * 0 1 * * * * +0x7f03 MIMP 10 16 3 0 * * * * 0 1 * * * * +0x7f04 MIGO 10 32 5 0 * * * * 0 1 * * * * +0x7f05 MDJI 10 16 3 0 * * * * 0 1 * * * * +0x7f06 MDJL 10 16 3 0 * * * * 0 1 * * * * +0x7f07 MGLC 10 16 3 0 * * * * 0 1 * * * * +0x7f08 MOTY 10 24 5 0 * * * * 0 1 * * * * +0x7f09 MSAH 10 16 3 0 * * * * 0 1 * * * * +0x7f0a MGCP 10 16 3 0 * * * * 0 1 * * * * +0x7f0b MGCL 10 16 3 0 * * * * 0 1 * * * * +0x7f0c MKUO 10 16 3 0 * * * * 0 1 * * * * +0x7f0d MLIC 10 16 3 0 * * * * 0 1 * * * * +0x7f0e MDLI 10 16 3 0 * * * * 0 1 * * * * +0x7f0f MTRS 10 16 3 0 * * * * 0 1 * * * * +0x7f10 MRAK 10 16 3 0 * * * * 0 1 * * * * +0x7f11 MUMB 10 16 3 0 * * * * 0 1 * * * * +0x7f12 MVAM 10 16 3 0 * * * * 0 1 * * * * +0x7f13 MSNK 10 16 3 0 * * * * 0 1 * * * * +0x7f14 MGIT 10 16 3 0 * * * * 0 1 * * * * +0x7f15 MBES 10 16 3 0 * * * * 0 1 * * * * +0x7f16 AMOO 10 32 5 0 * * * * 0 1 * * * * +0x7f17 ARAB 10 12 3 0 * * * * 0 1 * * * * +0x7f18 ADER 10 16 3 0 * * * * 0 1 * * * * +0x7f19 MDSW 10 16 3 0 * * * * 0 1 * * * * +0x7f20 AGRO 10 12 3 0 * * * * 0 1 * * * * +0x7f21 APHE 10 12 3 0 * * * * 0 1 * * * * +0x7f22 MVAF 10 16 3 0 * * * * 0 1 * * * * +0x7f23 MSAT 10 16 3 0 * * * * 0 1 * * * * +0x7f24 NPIR 10 16 3 0 * * * * 0 1 * * * * +0x7f27 MDRO 10 16 3 0 * * * * 0 1 * * * * +0x7f28 MKUL 10 16 3 0 * * * * 0 1 * * * * +0x7f29 MFDR 10 16 3 0 * * * * 0 1 * * * * +0x7f2a NSAI 10 16 3 0 * * * * 0 1 * * * * +0x7f2b MMAX 10 16 3 0 * * * * 0 0 * * * * +0x7f2c NSOL 10 16 3 0 * * * * 0 1 * * * * +0x7f2d MWFM 10 16 3 0 * * * * 0 1 * * * * +0x7f2e MRAV 10 32 5 0 * * * * 0 1 * * * * +0x7f2f MSPS 10 16 3 0 * * * * 0 1 * * * * +0x7f30 NBOH 10 16 3 0 * * * * 0 1 * * * * +0x7f31 NELL 10 16 3 0 * * * * 0 1 * * * * +0x7f32 MSLY 10 16 3 0 * * * * 0 1 * * * * +0x7f33 MKUR 10 16 3 0 * * * * 0 0 * * * * +0x7f34 MDOC 10 16 3 0 * * * * 0 0 * * * * +0x7f35 MMIS 10 16 3 0 * * * 1 0 1 * * * * +0x7f36 NSHD 10 16 3 0 * * * * 0 1 * * * * +0x7f37 NIRE 10 16 3 0 * * * * 0 1 * * * * +0x7f38 MEYE 10 16 3 0 * * * * 0 0 * * * * +0x7f39 MMST 10 16 3 1 * * * 0 0 0 * * * * +0x7f3a NIRO 10 16 3 0 * * * * 0 1 * * * * +0x7f3b MSOG 10 16 3 4 * * * 0 0 1 * * * * +0x7f3c MASG 10 16 3 4 * * * 0 0 1 * * * * +0x7f3d MMEL 10 24 3 0 * * * * 0 0 * * * * +0x7f3e MFIG 10 32 5 0 * * * * 0 1 * * * * +0x7f3f MFIG 10 32 5 0 MFIGG1B * * * 0 1 * * * * +0x8000 MGNL 4 16 3 0 * * * * * * * * S1 HB +0x8100 MHOB 4 16 3 0 * * * * * * * * S1 BW +0x8200 MKOB 4 16 3 0 * * * * * * * * SS BW +0x9000 MOGR 12 16 3 0 * * * * 1 * * * * * +0xa000 MWYV 13 16 3 0 * * * * 0 * * * * * +0xa100 MCAR 13 16 3 0 * * * * 0 * * * * * +0xb000 ACOW 14 32 5 0 * * * * 0 * * * * * +0xb100 AHRS 14 32 5 0 * * * * 0 * * * * * +0xb200 NBEGL 14 16 3 0 * * * * 1 * * * * * +0xb210 NPROL 14 16 3 0 * * * * 1 * * * * * +0xb300 NBOYL 14 16 3 0 * * * * 1 * * * * * +0xb310 NGRLL 14 16 3 0 * * * * 1 * * * * * +0xb400 NFAML 14 16 3 0 * * * * 1 * * * * * +0xb410 NFAWL 14 16 3 0 * * * * 1 * * * * * +0xb500 NSIML 14 16 3 0 * * * * 1 * * * * * +0xb510 NSIWL 14 16 3 0 * * * * 1 * * * * * +0xb600 NNOML 14 16 3 0 * * * * 1 * * * * * +0xb610 NNOWL 14 16 3 0 * * * * 1 * * * * * +0xb700 NSLVL 14 16 3 0 * * * * 1 * * * * * +0xc000 ABAT 15 12 3 0 * * * * 0 * * * * * +0xc100 ACAT 15 12 3 0 * * * * 0 * * * * * +0xc200 ACHK 15 12 3 0 * * * * 0 * * * * * +0xc300 ARAT 15 12 3 0 * * * * 0 * * * * * +0xc400 ASQU 15 12 3 0 * * * * 0 * * * * * +0xc500 ABAT 15 12 3 0 * * * * 0 * * * * * +0xc600 NBEGH 15 16 3 0 * * * * 1 * * * * * +0xc610 NPROH 15 16 3 0 * * * * 1 * * * * * +0xc700 NBOYH 15 16 3 0 * * * * 1 * * * * * +0xc710 NGRLH 15 16 3 0 * * * * 1 * * * * * +0xc800 NFAMH 15 16 3 0 * * * * 1 * * * * * +0xc810 NFAWH 15 16 3 0 * * * * 1 * * * * * +0xc900 NSIMH 15 16 3 0 * * * * 1 * * * * * +0xc910 NSIWH 15 16 3 0 * * * * 1 * * * * * +0xca00 NNOMH 15 16 3 0 * * * * 1 * * * * * +0xca10 NNOWH 15 16 3 0 * * * * 1 * * * * * +0xcb00 NSLVH 15 16 3 0 * * * * 1 * * * * * +0xd000 AEAGG1 16 0 3 0 * * * * 0 * * * * * +0xd100 AGULG1 16 0 3 0 * * * * 0 * * * * * +0xd200 AVULG1 16 0 3 0 * * * * 0 * * * * * +0xd300 ABIRG1 16 0 3 0 * * * * 0 * * * * * +0xd400 ABIRG1 16 0 3 0 * * * * 0 * * * * * +0xe000 MCYC 17 48 5 0 * * * * * * * * * * +0xe010 METN 17 48 5 0 * * * * * * * * * * +0xe020 MTAN 17 32 5 0 * * * * * * * * * * +0xe040 MHIS 17 16 3 0 * * * * * * * * * * +0xe050 MLER 17 16 3 0 * * * * * * * * * * +0xe060 MLIC 17 16 3 0 * * * * * * * * * * +0xe070 MMIN 17 24 3 0 * * * * * * * * * * +0xe080 MMUM 17 16 3 0 * * * * * * * * * * +0xe090 MTAN 17 16 3 0 * * * * * * * * * * +0xe0a0 MTIC 17 20 3 0 * * * * * * * * * * +0xe0b0 MTRO 17 16 3 0 * * * * * * * * * * +0xe0c0 MTSN 17 24 3 0 * * * * * * * * * * +0xe0d0 MUMB 17 24 3 0 * * * * * * * * * * +0xe0e0 MCOR 17 24 3 0 * * * * * * * * * * +0xe0f0 MGIC 17 32 5 0 * * * * * * * * * * +0xe0f1 MGLA 17 24 3 0 * * * * * * * * * * +0xe0f2 MWAV 17 16 3 0 * * * 1 * * * * * * +0xe200 MBET 17 24 3 0 * * * * * * * * * * +0xe210 MBFI 17 12 3 0 * * * * * * * * * * +0xe220 MBBM 17 16 3 0 * * * * * * * * * * +0xe230 MBRH 17 64 7 0 * * * * * * * * * * +0xe300 MGHO 17 16 3 0 * * * 1 * * * * * * +0xe310 MGH2 17 16 3 0 * * * * * * * * * * +0xe320 MGH3 17 16 3 0 * * * * * * * * * * +0xe400 MGO1 17 16 3 0 * * * * * * * * * * +0xe410 MGO2 17 16 3 0 * * * * * * * * * * +0xe420 MGO3 17 16 3 0 * * * * * * * * * * +0xe430 MGO4 17 16 3 0 * * * * * * * * * * +0xe500 MLIZ 17 24 3 0 * * * * * * * * * * +0xe510 MLI2 17 24 3 0 * * * * * * * * * * +0xe520 MLI3 17 32 5 0 * * * * * * * * * * +0xe600 MMYC 17 16 3 0 * * * * * * * * * * +0xe610 MMY2 17 16 3 0 * * * * * * * * * * +0xe700 MNO1 17 20 3 0 * * * * * * * * * * +0xe710 MNO2 17 20 3 0 * * * * * * * * * * +0xe720 MNO3 17 24 3 0 * * * * * * * * * * +0xe800 MOR1 17 16 3 0 * * * * * * * * * * +0xe810 MOR2 17 16 3 0 * * * * * * * * * * +0xe820 MOR3 17 16 3 0 * * * * * * * * * * +0xe830 MOR4 17 16 3 0 * * * * * * * * * * +0xe840 MOR5 17 16 3 0 * * * * * * * * * * +0xe900 MSAL 17 16 3 0 * * * * * * * * * * +0xe910 MSA2 17 16 3 0 * * * * * * * * * * +0xea00 MSHR 17 24 3 0 * * * * * * * * * * +0xea10 MSH1 17 16 3 0 * * * 1 * * * * * * +0xea20 MSH2 17 24 3 0 * * * 1 * * * * * * +0xeb00 MSKT 17 16 3 0 * * * * * * * * * * +0xeb10 MSKA 17 16 3 0 * * * * * * * * * * +0xeb20 MSKB 17 24 3 0 * * * * * * * * * * +0xec00 MWIG 17 16 3 0 * * * * * * * * * * +0xec10 MWI2 17 16 3 0 * * * * * * * * * * +0xec20 MWI3 17 16 3 0 * * * * * * * * * * +0xed00 MYU1 17 16 3 0 * * * * * * * * * * +0xed10 MYU2 17 16 3 0 * * * * * * * * * * +0xed20 MYU3 17 16 3 0 * * * * * * * * * * +0xee00 MZO2 17 16 3 0 * * * * * * * * * * +0xee10 MZO3 17 16 3 0 * * * * * * * * * * +0xef10 MWWE 17 24 3 0 * * * * * * * * * * diff --git a/src/org/infinity/resource/cre/decoder/tables/avatars-bgee.2da b/src/org/infinity/resource/cre/decoder/tables/avatars-bgee.2da new file mode 100644 index 000000000..df737deb8 --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/tables/avatars-bgee.2da @@ -0,0 +1,519 @@ +2DA V1.0 +* + RESREF TYPE ELLIPSE SPACE BLENDING PALETTE PALETTE2 RESREF2 TRANSLUCENT CLOWN SPLIT HELMET WEAPON HEIGHT HEIGHT_SHIELD +0x0000 SPRING 0 0 0 0 * * * * 1 0 * * * * +0x0001 SPFLAMES 0 0 0 7 * * * * 0 0 * * * * +0x0002 SPRDRASI 0 0 0 7 * * * * 0 0 * * * * +0x0003 SPFLAMES 0 0 0 7 SPFLAMEB * * * 0 0 * * * * +0x0004 SPRDRASI 0 0 0 7 SPGDRASI * * * 0 0 * * * * +0x0100 SPCHUNKS 0 0 0 0 * * SPSHADOW * 1 1 * * * * +0x0101 BGLRYU 0 0 3 7 * * * * * 0 * * * * +0x0102 SPCL236U 0 0 3 7 * * * * * 0 * * * * +0x0200 SPBLOOD 0 0 0 0 * * * * 0 0 0 * * * +0x0210 SPBLOOD 0 0 0 0 * * * * 0 0 1 * * * +0x0220 SPBLOOD 0 0 0 0 * * * * 0 0 2 * * * +0x0230 SPBLOOD 0 0 0 0 * * * * 0 0 3 * * * +0x0240 SPBLOOD 0 0 0 0 * * * * 0 0 4 * * * +0x0300 SPSMPUFF 0 0 0 0 * * * 1 1 * * * * * +0x0301 SPSMPUFF 0 0 0 0 * * * 1 1 * * * * * +0x0400 SKLH 0 0 0 0 * * SPSHADOW * 0 1 * * * * +0x0410 GLPHWRDH 0 0 0 1 * * SPSHADOW * 0 1 * * * * +0x0500 STNKCLDD 0 0 0 0 * * * * 1 0 1 * * * +0x0510 STNKCLDD 0 0 0 0 * * * * 1 0 0 * * * +0x0520 SPHORPUF 0 0 0 1 * * * * 0 0 * * * * +0x0600 STNKCLDD 0 0 0 0 * * * * 1 0 1 * * * +0x0610 SPICESTM 0 0 0 1 * * * * 0 0 0 * * * +0x0700 GREASEH 0 0 0 0 * * * * 0 0 * * * * +0x0710 GREASED 0 0 0 0 * * * * 0 1 * * * * +0x0800 WEBENTH 0 0 0 0 * * * * 1 0 * * * * +0x0810 WEBENTD 0 0 0 0 * * * * 1 0 * * * * +0x0900 STNKCLDD 0 0 0 0 * * * * 1 0 1 * * * +0x0910 SPMETSWM 0 0 0 1 * * * * 0 1 0 * * * +0x0a00 SPHORPUF 0 0 0 1 * * * * 0 0 * * * * +0x0a01 SPWRDFLD 0 0 0 1 * * * * 0 1 * * * * +0x0a02 SPENTAAI 0 0 0 1 * * * * 0 1 * * * * +0x0a03 SPHLYSM2 0 0 0 1 * * * * 0 1 * * * * +0x0a04 SPUNHBL2 0 0 0 1 * * * * 0 1 * * * * +0x0a10 SPHORPUF 0 0 0 1 * * * * 0 0 * * * * +0x0a11 SPWRDFLD 0 0 0 1 * * * * 0 1 * * * * +0x0a12 SPENTAAI 0 0 0 1 * * * * 0 1 * * * * +0x0a13 SPHLYSM2 0 0 0 1 * * * * 0 1 * * * * +0x0a14 SPUNHBL2 0 0 0 1 * * * * 0 1 * * * * +0x0a23 SPHLYSM2 0 0 0 1 * * * * 0 1 * * * * +0x0a24 SPUNHBL2 0 0 0 1 * * * * 0 1 * * * * +0x0b00 SPCSPRA2 0 0 0 1 * * * * 0 0 * * * * +0x0b01 SPCCOLDL 0 0 0 1 * * * * 0 0 * * * * +0x0b02 SPPRISM2 0 0 0 1 * * * * 0 0 * * * * +0x0b03 SPCSPRA3 0 0 0 1 * * * * 0 0 * * * * +0x0b04 SPPRISM3 0 0 0 1 * * * * 0 0 * * * * +0x0c00 SPSTRMVA 0 0 0 1 * * * * 0 1 * * SPSTRMVB * +0x0c10 SPSTRMVA 0 0 0 1 * * * * 0 1 * * SPSTRMVB * +0x1000 MWYV 1 24 4 0 * * * * 0 * * * * * +0x1003 MWYV 1 24 4 0 MWYV_WH * * * 0 * * * * * +0x1004 MWYV 1 24 4 0 MWYV_AL * * * 0 * * * * * +0x1100 MTAN 1 32 5 0 * * * * 0 * 1 * * * +0x1101 MWDR 1 72 7 0 * * * * 0 * * * * * +0x1102 MTAN 1 32 5 0 MTAN_BL * * * 0 * 1 * * * +0x1103 MTAN 1 32 5 0 MTAN_GR * * * 0 * 1 * * * +0x1104 MTAN 1 32 5 0 MTAN_RD * * * 0 * 1 * * * +0x1105 MWDR 1 72 7 0 MWDR_GR * * * 0 * * * * * +0x1200 MDR1 2 72 13 0 * * * * 0 1 * * * * +0x1201 MDR2 2 72 13 0 * * * * 0 1 * * * * +0x1202 MDR3 2 72 13 0 * * * * 0 1 * * * * +0x1203 MDR1 2 72 13 0 MDR1_GR * * * 0 1 * * * * +0x1204 MDR1 2 72 13 0 MDR1_AQ * * * 0 1 * * * * +0x1205 MDR1 2 72 13 0 MDR1_BL * * * 0 1 * * * * +0x1206 MDR1 2 72 13 0 MDR1_BR * * * 0 1 * * * * +0x1207 MDR1 2 72 13 0 MDR1_MC * * * 0 1 * * * * +0x1208 MDR1 2 72 13 0 MDR1_PU * * * 0 1 * * * * +0x1300 MDEM 3 32 5 0 * * * * 0 1 * * * * +0x2000 MSIR 5 16 3 0 * * * * 1 * 1 * * BW +0x2100 UVOL 5 16 3 0 * * * * 0 * 1 * MS * +0x2200 MOGM 5 16 3 0 * * * * 1 * 1 1 S1 * +0x2300 MDKN 5 16 3 0 * * * * 0 * 1 1 * * +0x3000 MAKH 6 24 5 0 * * * * 0 * * * * * +0x3001 MNEO 6 60 5 0 * * * * 0 * * * * * +0x4000 SNOMC 7 16 3 0 * * * * 1 * * * * * +0x4001 SNONE 7 0 0 0 * * * * 0 * * * * * +0x4002 SNOMM 7 16 3 0 * * * * 1 * * * * * +0x4010 SNOWC 7 16 3 0 * * * * 1 * * * * * +0x4012 SNOWM 7 16 3 0 * * * * 1 * * * * * +0x4100 SSIMC 7 16 3 0 * * * * 1 * * * * * +0x4101 SSIMS 7 16 3 0 * * * * 1 * * * * * +0x4102 SSIMM 7 16 3 0 * * * * 1 * * * * * +0x4110 SSIWC 7 16 3 0 * * * * 1 * * * * * +0x4112 SSIWM 7 16 3 0 * * * * 1 * * * * * +0x4200 SHMCM 7 16 3 0 * * * * 1 * * * * * +0x4300 MSPLG1 7 32 5 0 * * * * 0 * * * * * +0x4400 LHMC 7 16 3 0 * * * * 1 * * * * * +0x4410 LHFC 7 16 3 0 * * * * 1 * * * * * +0x4500 LFAM 7 16 3 0 * * * * 1 * * * * * +0x4600 LDMF 7 16 3 0 * * * * 1 * * * * * +0x4700 LEMF 7 16 3 0 * * * * 1 * * * * * +0x4710 LEFF 7 16 3 0 * * * * 1 * * * * * +0x4800 LIMC 7 16 3 0 * * * * 1 * * * * * +0x5000 CHMB 8 16 3 0 * * CHMC * 1 1 1 * WQL * +0x5001 CEMB 8 16 3 0 * * CEMC * 1 1 1 * WQM * +0x5002 CDMB 8 16 3 0 * * CDMC * 1 1 1 * WQS * +0x5003 CIMB 8 16 3 0 * * CIMC * 1 1 1 * WQS WQH +0x5010 CHFB 8 16 3 0 * * CHFC * 1 1 1 * WQN * +0x5011 CEFB 8 16 3 0 * * CEFC * 1 1 1 * WQM * +0x5012 CDFB 8 16 3 0 * * CDFC * 1 1 1 * WQS * +0x5013 CIFB 8 16 3 0 * * CIFC * 1 1 1 * WQS * +0x5100 CHMB 8 16 3 0 * * CHMF * 1 1 1 * WQL * +0x5101 CEMB 8 16 3 0 * * CEMF * 1 1 1 * WQM * +0x5102 CDMB 8 16 3 0 * * CDMF * 1 1 1 * WQS * +0x5103 CIMB 8 16 3 0 * * CIMF * 1 1 1 * WQS WQH +0x5110 CHFB 8 16 3 0 * * CHFF * 1 1 1 * WQN * +0x5111 CEFB 8 16 3 0 * * CEFF * 1 1 1 * WQM * +0x5112 CDFB 8 16 3 0 * * CDFF * 1 1 1 * WQS * +0x5113 CIFB 8 16 3 0 * * CIFF * 1 1 1 * WQS * +0x5200 CHMW 8 16 3 0 * * CHMW * 1 1 1 * WQL * +0x5201 CEMW 8 16 3 0 * * CEMW * 1 1 1 * WQM * +0x5202 CDMW 8 16 3 0 * * CDMW * 1 1 1 * WQS * +0x5210 CHFW 8 16 3 0 * * CHFW * 1 1 1 * WQN * +0x5211 CEFW 8 16 3 0 * * CEFW * 1 1 1 * WQM * +0x5212 CDFW 8 16 3 0 * * CDFW * 1 1 1 * WQS * +0x5300 CHMT 8 16 3 0 * * CHMT * 1 1 0 * WQL * +0x5301 CEMT 8 16 3 0 * * CEMT * 1 1 0 * WQM * +0x5302 CDMT 8 16 3 0 * * CDMT * 1 1 0 * WQS * +0x5303 CIMT 8 16 3 0 * * CIMT * 1 1 0 * WQS WQH +0x5310 CHFT 8 16 3 0 * * CHFT * 1 1 0 * WQN * +0x5311 CEFT 8 16 3 0 * * CEFT * 1 1 0 * WQM * +0x5312 CDFT 8 16 3 0 * * CDFT * 1 1 0 * WQS * +0x5313 CIFT 8 16 3 0 * * CIFT * 1 1 0 * WQS * +0x6000 CHMB 8 16 3 0 * * CHMC * 1 1 1 * WQL * +0x6001 CEMB 8 16 3 0 * * CEMC * 1 1 1 * WQM * +0x6002 CDMB 8 16 3 0 * * CDMC * 1 1 1 * WQS * +0x6003 CIMB 8 16 3 0 * * CIMC * 1 1 1 * WQS WQH +0x6004 CDMB 8 16 3 0 * * CDMC * 1 1 1 * WQS WQH +0x6005 CHMB 8 16 3 0 * * CHMC * 1 1 1 * WQL * +0x6010 CHFB 8 16 3 0 * * CHFC * 1 1 1 * WQN * +0x6011 CEFB 8 16 3 0 * * CEFC * 1 1 1 * WQM * +0x6012 CDFB 8 16 3 0 * * CDFC * 1 1 1 * WQS * +0x6013 CIFB 8 16 3 0 * * CIFC * 1 1 1 * WQS * +0x6014 CIFB 8 16 3 0 * * CIFC * 1 1 1 * WQS WQH +0x6015 CHFB 8 16 3 0 * * CHFC * 1 1 1 * WQN * +0x6100 CHMB 8 16 3 0 * * CHMF * 1 1 1 * WQL * +0x6101 CEMB 8 16 3 0 * * CEMF * 1 1 1 * WQM * +0x6102 CDMB 8 16 3 0 * * CDMF * 1 1 1 * WQS * +0x6103 CIMB 8 16 3 0 * * CIMF * 1 1 1 * WQS WQH +0x6104 CDMB 8 16 3 0 * * CDMF * 1 1 1 * WQS WQH +0x6105 CHMB 8 16 3 0 * * CHMF * 1 1 1 * WQL * +0x6110 CHFB 8 16 3 0 * * CHFF * 1 1 1 * WQN * +0x6111 CEFB 8 16 3 0 * * CEFF * 1 1 1 * WQM * +0x6112 CDFB 8 16 3 0 * * CDFF * 1 1 1 * WQS * +0x6113 CIFB 8 16 3 0 * * CIFF * 1 1 1 * WQS * +0x6114 CIFB 8 16 3 0 * * CIFF * 1 1 1 * WQS WQH +0x6115 CHFB 8 16 3 0 * * CHFF * 1 1 1 * WQN * +0x6200 CHMW 8 16 3 0 * * CHMW * 1 1 1 * WQL * +0x6201 CEMW 8 16 3 0 * * CEMW * 1 1 1 * WQM * +0x6202 CDMW 8 16 3 0 * * CDMW * 1 1 1 * WQS * +0x6204 CDMW 8 16 3 0 * * CDMW * 1 1 1 * WQS WQH +0x6205 CHMW 8 16 3 0 * * CHMW * 1 1 1 * WQL * +0x6210 CHFW 8 16 3 0 * * CHFW * 1 1 1 * WQN * +0x6211 CEFW 8 16 3 0 * * CEFW * 1 1 1 * WQM * +0x6212 CDFW 8 16 3 0 * * CDFW * 1 1 1 * WQS * +0x6214 CDFW 8 16 3 0 * * CDFW * 1 1 1 * WQS WQH +0x6215 CHFW 8 16 3 0 * * CHFW * 1 1 1 * WQN * +0x6300 CHMT 8 16 3 0 * * CHMT * 1 1 0 * WQL * +0x6301 CEMT 8 16 3 0 * * CEMT * 1 1 0 * WQM * +0x6302 CDMT 8 16 3 0 * * CDMT * 1 1 0 * WQS * +0x6303 CIMT 8 16 3 0 * * CIMT * 1 1 0 * WQS WQH +0x6304 CDMT 8 16 3 0 * * CDMT * 1 1 0 * WQS WQH +0x6305 CHMT 8 16 3 0 * * CHMT * 1 1 0 * WQL * +0x6310 CHFT 8 16 3 0 * * CHFT * 1 1 0 * WQN * +0x6311 CEFT 8 16 3 0 * * CEFT * 1 1 0 * WQM * +0x6312 CDFT 8 16 3 0 * * CDFT * 1 1 0 * WQS * +0x6313 CIFT 8 16 3 0 * * CIFT * 1 1 0 * WQS * +0x6314 CIFT 8 16 3 0 * * CIFT * 1 1 0 * WQS WQH +0x6315 CHFT 8 16 3 0 * * CHFT * 1 1 0 * WQN * +0x6400 UDRZ 9 16 3 0 * * * * 1 * 1 0 WPM * +0x6401 UELM 9 16 3 0 * * * * 0 * 0 0 WPM * +0x6402 CMNK 9 16 3 0 * * * * 1 * 0 * WPM * +0x6403 MSKL 9 16 3 0 * * * * 1 * 1 * WPM * +0x6404 USAR 9 16 3 0 * * * * 0 * 0 0 WPL * +0x6405 MDGU 9 16 3 0 * * * * 1 * 1 * WPM * +0x6406 MDGU 9 24 5 0 * * * * 1 * 1 * WPM * +0x6500 CHMM 8 16 3 0 * * CHMM * 1 1 1 * WQL * +0x6510 CHFM 8 16 3 0 * * CHFM * 1 1 1 * WQN * +0x6621 XHFF 9 16 3 0 * * * * 1 * 1 * WPM * +0x7000 MOGH 11 16 3 0 * * * * 1 * * * * * +0x7001 MOGN 11 16 3 0 * * * * 1 * * * * * +0x7100 MBAS 11 32 5 0 * * * * 0 * * * * * +0x7101 MBAS 11 32 5 0 MBAS_GR * * * 0 * * * * * +0x7200 MBER 11 16 3 0 MBER_BL * * * 0 * * * * * +0x7201 MBER 11 16 3 0 * * * * 0 * * * * * +0x7202 MBER 11 16 3 0 MBER_CA * * * 0 * * * * * +0x7203 MBER 11 16 3 0 MBER_PO * * * 0 * * * * * +0x7300 MEAE 10 32 5 0 * * * * 0 1 * * * * +0x7301 MEAS 10 16 3 0 * * * * 0 1 * * * * +0x7302 MEAE 10 32 5 0 MEAE_SH * * * 0 1 * * * * +0x7310 MFIE 10 16 3 4 * * * * 0 1 * * * * +0x7311 MFIS 10 16 3 4 * * * * 0 1 * * * * +0x7312 MFIE 10 16 3 4 MFIEG1B MFIEG2B * * 0 1 * * * * +0x7313 MFIS 10 16 3 4 MFISG1B MFISG2B * * 0 1 * * * * +0x7314 MFIE 10 16 3 4 MFIEG31 MFIEG3B * * 0 1 * * * * +0x7320 MAIR 10 32 5 0 * * * 1 0 1 * * * * +0x7321 MAIS 10 16 3 0 * * * 1 0 1 * * * * +0x7400 MDOG 11 16 3 0 MDOG_WI * * * 0 * * * * * +0x7401 MDOG 11 16 3 0 MDOG_WA * * * 0 * * * * * +0x7402 MDOG 11 16 3 0 MDOG_MO * * * 0 * * * * * +0x7500 MDOP 11 16 3 0 * * * * 0 * * * * * +0x7501 MDOP 11 16 3 0 MDOP_GR * * * 0 * * * * * +0x7600 METT 11 16 3 0 * * * * 0 * * * * * +0x7601 MGHL 11 16 3 0 MGHL_MA * * * 0 0 * * * * +0x7602 MSPI 11 16 3 0 MSPI_MY * * * 0 * * * * * +0x7603 MDOG 11 16 3 0 MDOG_GR * * * 0 * * * * * +0x7604 MSPI 11 16 3 0 MSPI_WA * * * 0 * * * * * +0x7700 MGHL 11 16 3 0 * * * * 0 * * * * * +0x7701 MGHL 11 16 3 0 MGHL_RE * * * 0 * * * * * +0x7702 MGHL 11 16 3 0 MGHL_GA * * * 0 * * * * * +0x7703 MSHD 10 16 3 0 * * * 1 0 1 * * * * +0x7800 MGIB 11 16 3 0 * * * * 0 * * * * * +0x7801 MGIB 11 16 3 0 MGIB_BR * * * 0 * * * * * +0x7802 MSLI 11 24 4 0 MSLI_BL * * 0 0 * * * * * +0x7900 MSLI 11 24 4 0 MSLI_GR * * 1 0 * * * * * +0x7901 MSLI 11 24 4 0 MSLI_OL * * 1 0 * * * * * +0x7902 MSLI 11 24 4 0 MSLI_MU * * 1 0 * * * * * +0x7903 MSLI 11 24 4 0 MSLI_OC * * 1 0 * * * * * +0x7904 MSLI 11 24 4 0 * * * 1 0 * * * * * +0x7a00 MSPI 11 16 3 0 MSPI_GI * * * 0 * * * * * +0x7a01 MSPI 11 16 3 0 MSPI_HU * * * 0 * * * * * +0x7a02 MSPI 11 16 3 0 MSPI_PH * * * 0 * * * * * +0x7a03 MSPI 11 16 3 0 MSPI_SW * * * 0 * * * * * +0x7a04 MSPI 11 16 3 0 MSPI_WR * * * 0 * * * * * +0x7b00 MWLF 11 16 3 0 * * * * 0 * * * * * +0x7b01 MWLF 11 16 3 0 MWLF_WO * * * 0 * * * * * +0x7b02 MWLF 11 16 3 0 MWLF_DI * * * 0 * * * * * +0x7b03 MWLF 11 16 3 0 MWLF_WI * * * 0 * * * * * +0x7b04 MWLF 11 16 3 0 MWLF_VA * * * 0 * * * * * +0x7b05 MWLF 11 16 3 0 MWLF_DR * * * 0 * * * * * +0x7b06 MWLS 11 16 3 0 * * * * 0 * * * * * +0x7c00 MXVT 11 16 3 0 * * * * 1 * * * * * +0x7c01 MTAS 11 16 3 0 * * * * 0 * * * * * +0x7d00 MZOM 11 16 3 0 * * * * 1 * * * * * +0x7d01 NSLF 11 16 3 0 * * * * 1 0 * * * * +0x7d02 ACHB 11 12 3 0 * * * * 0 0 * * * * +0x7d03 ACHW 11 12 3 0 * * * * 0 0 * * * * +0x7d04 NPRF 11 16 3 0 * * * * 1 0 * * * * +0x7d05 NNMF 11 16 3 0 * * * * 1 0 * * * * +0x7d06 NNWF 11 16 3 0 * * * * 1 0 * * * * +0x7d07 NSMF 11 16 3 0 * * * * 1 0 * * * * +0x7d08 NSWF 11 16 3 0 * * * * 1 0 * * * * +0x7e00 MWER 11 16 3 0 * * * * 0 * * * * * +0x7e01 MGWE 11 16 3 0 * * * * 0 * * * * * +0x7f00 MTRO 10 16 3 0 * * * * 0 1 * * * * +0x7f01 MMIN 10 16 3 0 * * * * 0 1 * * * * +0x7f02 MBEH 10 32 5 0 * * * * 0 1 * * * * +0x7f03 MIMP 10 16 3 0 * * * * 0 1 * * * * +0x7f04 MIGO 10 32 5 0 * * * * 0 1 * * * * +0x7f05 MDJI 10 16 3 0 * * * * 0 1 * * * * +0x7f06 MDJL 10 16 3 0 * * * * 0 1 * * * * +0x7f07 MGLC 10 16 3 0 * * * * 0 1 * * * * +0x7f08 MOTY 10 24 5 0 * * * * 0 1 * * * * +0x7f09 MSAH 10 16 3 0 * * * * 0 1 * * * * +0x7f0a MGCP 10 16 3 0 * * * * 0 1 * * * * +0x7f0b MGCL 10 16 3 0 * * * * 0 1 * * * * +0x7f0c MKUO 10 16 3 0 * * * * 0 1 * * * * +0x7f0d MLIC 10 16 3 0 * * * * 0 1 * * * * +0x7f0e MDLI 10 16 3 0 * * * * 0 1 * * * * +0x7f0f MTRS 10 16 3 0 * * * * 0 1 * * * * +0x7f10 MRAK 10 16 3 0 * * * * 0 1 * * * * +0x7f11 MUMB 10 16 3 0 * * * * 0 1 * * * * +0x7f12 MVAM 10 16 3 0 * * * * 0 1 * * * * +0x7f13 MSNK 10 16 3 0 * * * * 0 1 * * * * +0x7f14 MGIT 10 16 3 0 * * * * 0 1 * * * * +0x7f15 MBES 10 16 3 0 * * * * 0 1 * * * * +0x7f16 AMOO 10 32 5 0 * * * * 0 1 * * * * +0x7f17 ARAB 10 12 3 0 * * * * 0 1 * * * * +0x7f18 ADER 10 16 3 0 * * * * 0 1 * * * * +0x7f19 MDSW 10 16 3 0 * * * * 0 1 * * * * +0x7f20 AGRO 10 12 3 0 * * * * 0 1 * * * * +0x7f21 APHE 10 12 3 0 * * * * 0 1 * * * * +0x7f22 MVAF 10 16 3 0 * * * * 0 1 * * * * +0x7f23 MSAT 10 16 3 0 * * * * 0 1 * * * * +0x7f24 NPIR 10 16 3 0 * * * * 0 1 * * * * +0x7f27 MDRO 10 16 3 0 * * * * 0 1 * * * * +0x7f28 MKUL 10 16 3 0 * * * * 0 1 * * * * +0x7f29 MFDR 10 16 3 0 * * * * 0 1 * * * * +0x7f2a NSAI 10 16 3 0 * * * * 0 1 * * * * +0x7f2b MMAX 10 16 3 0 * * * * 0 0 * * * * +0x7f2c NSOL 10 16 3 0 * * * * 0 1 * * * * +0x7f2d MWFM 10 16 3 0 * * * * 0 1 * * * * +0x7f2e MRAV 10 32 5 0 * * * * 0 1 * * * * +0x7f2f MSPS 10 16 3 0 * * * * 0 1 * * * * +0x7f30 NBOH 10 16 3 0 * * * * 0 1 * * * * +0x7f31 NELL 10 16 3 0 * * * * 0 1 * * * * +0x7f32 MSLY 10 16 3 0 * * * * 0 1 * * * * +0x7f33 MKUR 10 16 3 0 * * * * 0 0 * * * * +0x7f34 MDOC 10 16 3 0 * * * * 0 0 * * * * +0x7f35 MMIS 10 16 3 0 * * * 1 0 1 * * * * +0x7f36 NSHD 10 16 3 0 * * * * 0 1 * * * * +0x7f37 NIRE 10 16 3 0 * * * * 0 1 * * * * +0x7f38 MEYE 10 16 3 0 * * * * 0 0 * * * * +0x7f39 MMST 10 16 3 1 * * * 0 0 0 * * * * +0x7f3a NIRO 10 16 3 0 * * * * 0 1 * * * * +0x7f3b MSOG 10 16 3 4 * * * 0 0 1 * * * * +0x7f3c MASL 10 16 3 7 * * MASG 0 0 1 * * * * +0x7f3d MMEL 10 24 3 0 * * * * 0 0 * * * * +0x7f3e MFIG 10 32 5 0 * * * * 0 1 * * * * +0x7f3f MFIG 10 32 5 0 MFIGG1B * * * 0 1 * * * * +0x7f40 MGLM 10 16 3 0 * * * * 0 1 * * * * +0x7f41 MDJL 10 16 3 0 MDJL_E1 MDJL_E2 * * 0 1 * * * * +0x7f42 NIRO 10 16 3 0 NIRO_RD * * * 0 1 * * * * +0x7f43 MOTY 10 16 3 0 MOTY_Y1 MOTY_Y2 * * 0 1 * * * * +0x7f44 NELW 10 16 3 0 * * * * 0 0 * * * * +0x7f45 MSAI 10 16 3 0 * * * 1 0 0 * * * * +0x7f46 MBEG 10 16 3 0 * * * * 0 0 * * * * +0x7f47 MFI2 10 32 5 0 * * * * 0 0 * * * * +0x7f48 MSOF 10 16 3 0 * * * 0 0 1 * * * * +0x7f49 MDMF 10 16 3 0 * * * 0 0 1 * * * * +0x7f4a MDAS 10 16 3 7 * * MDAG 0 0 1 * * * * +0x7f4b MDAF 10 16 3 0 * * * 0 0 1 * * * * +0x7f4c MPLN 10 16 3 7 * * MPLG 0 0 1 * * * * +0x7f4d MPLF 10 16 3 0 * * * 0 0 1 * * * * +0x7f4e MDVM 10 16 3 7 * * MDVG 0 0 1 * * * * +0x7f4f MDVF 10 16 3 0 * * * 0 0 1 * * * * +0x7f50 MMST 10 16 3 1 MMST_NI * * 0 0 0 * * * * +0x7f51 MMST 10 16 3 1 MMST_HA * * 0 0 0 * * * * +0x7f52 NSAI 10 16 3 0 NSAI_G1 NSAI_G2 * * 0 1 * * * * +0x7f53 NSAI 10 16 3 0 NSAI_R1 NSAI_R2 * * 0 1 * * * * +0x7f54 NSAI 10 16 3 0 NSAI_D1 NSAI_D2 * * 0 1 * * * * +0x7f55 NSOL 10 16 3 0 NSOL_C1 NSOL_C2 * * 0 1 * * * * +0x7f56 MWIL 10 16 3 7 * * * * 0 0 * * * * +0x7f57 MWIS 10 12 3 7 * * * * 0 0 * * * * +0x7f58 MABB 10 24 3 0 * * * * 0 0 * * * * +0x7f59 MABG 10 24 3 0 * * * * 0 0 * * * * +0x7f5a MABR 10 24 3 0 * * * * 0 0 * * * * +0x7f5b MABR 10 24 3 0 MABR_BL * * * 0 0 * * * * +0x7f5c MHAG 10 16 3 0 * * * * 0 0 * * * * +0x7f5d MHAG 10 16 3 0 MHAG_GR * * * 0 0 * * * * +0x7f5e MHAG 10 16 3 0 MHAG_SE * * * 0 0 * * * * +0x7f5f MHAN 10 16 3 0 * * * * 0 0 * * * * +0x7f60 MSNK 10 16 3 0 MSNK_W1 MSNK_W2 * * 0 1 * * * * +0x7f61 MDRJ 10 48 9 0 * * * * 0 0 * * * * +0x7f62 MDRJ 10 72 9 0 MDRJ_GR * * * 0 0 * * * * +0x8000 MGNL 4 16 3 0 * * * * * * * * S1 HB +0x8100 MHOB 4 16 3 0 * * * * * * * * S1 BW +0x8200 MKOB 4 16 3 0 * * * * * * * * SS BW +0x9000 MOGR 12 16 3 0 * * * * 1 * * * * * +0xa000 MWYV 13 16 3 0 * * * * 0 * * * * * +0xa100 MCAR 13 16 3 0 * * * * 0 * * * * * +0xa200 MWYV 13 24 4 0 MWYV_WS * * * 0 * * * * * +0xa201 MCEN 13 16 3 0 * * * * 0 * * * * * +0xa202 MTUN 13 16 3 0 * * * * 0 * * * * * +0xb000 ACOW 14 32 5 0 * * * * 0 * * * * * +0xb100 AHRS 14 32 5 0 * * * * 0 * * * * * +0xb200 NBEGL 14 16 3 0 * * * * 1 * * * * * +0xb210 NPROL 14 16 3 0 * * * * 1 * * * * * +0xb300 NBOYL 14 16 3 0 * * * * 1 * * * * * +0xb310 NGRLL 14 16 3 0 * * * * 1 * * * * * +0xb400 NFAML 14 16 3 0 * * * * 1 * * * * * +0xb410 NFAWL 14 16 3 0 * * * * 1 * * * * * +0xb500 NSIML 14 16 3 0 * * * * 1 * * * * * +0xb510 NSIWL 14 16 3 0 * * * * 1 * * * * * +0xb600 NNOML 14 16 3 0 * * * * 1 * * * * * +0xb610 NNOWL 14 16 3 0 * * * * 1 * * * * * +0xb700 NSLVL 14 16 3 0 * * * * 1 * * * * * +0xc000 ABAT 15 12 3 0 * * * * 0 * * * * * +0xc100 ACAT 15 12 3 0 * * * * 0 * * * * * +0xc200 ACHK 15 12 3 0 * * * * 0 * * * * * +0xc300 ARAT 15 12 3 0 * * * * 0 * * * * * +0xc400 ASQU 15 12 3 0 * * * * 0 * * * * * +0xc500 ABAT 15 12 3 0 * * * * 0 * * * * * +0xc600 NBEGH 15 16 3 0 * * * * 1 * * * * * +0xc610 NPROH 15 16 3 0 * * * * 1 * * * * * +0xc700 NBOYH 15 16 3 0 * * * * 1 * * * * * +0xc710 NGRLH 15 16 3 0 * * * * 1 * * * * * +0xc800 NFAMH 15 16 3 0 * * * * 1 * * * * * +0xc810 NFAWH 15 16 3 0 * * * * 1 * * * * * +0xc900 NSIMH 15 16 3 0 * * * * 1 * * * * * +0xc910 NSIWH 15 16 3 0 * * * * 1 * * * * * +0xca00 NNOMH 15 16 3 0 * * * * 1 * * * * * +0xca10 NNOWH 15 16 3 0 * * * * 1 * * * * * +0xcb00 NSLVH 15 16 3 0 * * * * 1 * * * * * +0xcc00 MKG1 15 16 3 0 * * * * 0 * * * * * +0xcc01 MKG2 15 16 3 0 * * * * 0 * * * * * +0xcc02 MKG3 15 16 3 0 * * * * 0 * * * * * +0xcc04 ARAT 15 0 1 0 * * * * 0 * * * * * +0xd000 AEAGG1 16 0 3 0 * * * * 0 * * * * * +0xd100 AGULG1 16 0 3 0 * * * * 0 * * * * * +0xd200 AVULG1 16 0 3 0 * * * * 0 * * * * * +0xd300 ABIRG1 16 0 3 0 * * * * 0 * * * * * +0xd400 ABIRG1 16 0 3 0 * * * * 0 * * * * * +0xe000 MCYC 17 48 5 0 * * * * * * * * * * +0xe010 METN 17 48 5 0 * * * * * * * * * * +0xe020 MTAN 17 32 5 0 * * * * * * * * * * +0xe040 MHIS 17 16 3 0 * * * * * * * * * * +0xe050 MLER 17 16 3 0 * * * * * * * * * * +0xe060 MLIC 17 16 3 0 * * * * * * * * * * +0xe070 MMIN 17 24 3 0 * * * * * * * * * * +0xe080 MMUM 17 16 3 0 * * * * * * * * * * +0xe090 MTAN 17 16 3 0 * * * * * * * * * * +0xe0a0 MTIC 17 20 3 0 * * * * * * * * * * +0xe0b0 MTRO 17 16 3 0 * * * * * * * * * * +0xe0c0 MTSN 17 24 3 0 * * * * * * * * * * +0xe0d0 MUMB 17 24 3 0 * * * * * * * * * * +0xe0e0 MCOR 17 24 3 0 * * * * * * * * * * +0xe0f0 MGIC 17 32 5 0 * * * * * * * * * * +0xe0f1 MGLA 17 24 3 0 * * * * * * * * * * +0xe0f2 MWAV 17 16 3 0 * * * 1 * * * * * * +0xe200 MBET 17 24 3 0 * * * * * * * * * * +0xe210 MBFI 17 12 3 0 * * * * * * * * * * +0xe220 MBBM 17 16 3 0 * * * * * * * * * * +0xe230 MBRH 17 64 7 0 * * * * * * * * * * +0xe240 MANI 17 24 3 0 * * * * * * * * * * +0xe241 MAN2 17 24 3 0 * * * * * * * * * * +0xe242 MAN3 17 24 3 0 * * * * * * * * * * +0xe243 MARU 17 16 3 0 * * * * * * * * * * +0xe244 MBA1 17 16 3 0 * * * * * * * * * * +0xe245 MBA2 17 16 3 0 * * * * * * * * * * +0xe246 MBA3 17 16 3 0 * * * * * * * * * * +0xe247 MBA4 17 16 3 0 * * * * * * * * * * +0xe248 MBA5 17 16 3 0 * * * * * * * * * * +0xe249 MBA6 17 16 3 0 * * * * * * * * * * +0xe24a MBAI 17 16 3 0 * * * * * * * * * * +0xe24b MELE 17 36 3 0 * * * * * * * * * * +0xe24c MELF 17 36 3 0 * * * * * * * * * * +0xe24d MELW 17 36 3 0 * * * * * * * * * * +0xe24e MGFI 17 48 3 0 * * * * * * * * * * +0xe24f MGFR 17 48 5 0 * * * * * * * * * * +0xe250 MGIR 17 24 3 0 * * * * * * * * * * +0xe251 MGVE 17 32 5 0 * * * * * * * * * * +0xe252 MHAR 17 16 3 0 * * * * * * * * * * +0xe253 MREM 17 48 5 0 * * * * * * * * * * +0xe254 MSCR 17 24 3 0 * * * * * * * * * * +0xe255 MSEE 17 16 3 0 * * * * * * * * * * +0xe256 MBE1 17 32 4 0 * * * * * * * * * * +0xe257 MBE2 17 16 3 0 * * * * * * * * * * +0xe258 MBE2 17 16 3 0 MBE2_HE * * * * * * * * * +0xe259 MSVI 17 16 3 0 * * * * * * * * * * +0xe25a MSV2 17 16 3 0 * * * * * * * * * * +0xe25b MUM2 17 24 3 0 * * * * * * * * * * +0xe25c MTA2 17 16 3 0 * * * * * * * * * * +0xe25d MYET 17 24 3 0 * * * * * * * * * * +0xe25e MWI4 17 16 3 0 * * * * * * * * * * +0xe25f MDRD 17 16 3 0 * * * * * * * * * * +0xe260 MCRD 17 24 3 0 * * * * * * * * * * +0xe261 MSAH 17 24 3 0 * * * * * * * * * * +0xe262 MSAT 17 24 3 0 * * * * * * * * * * +0xe263 MSV3 17 16 3 0 * * * * * * * * * * +0xe264 MSV4 17 16 3 0 * * * * * * * * * * +0xe265 MBOA 17 24 3 0 * * * * * * * * * * +0xe266 MBU2 17 16 3 0 * * * * * * * * * * +0xe267 MBUG 17 16 3 0 * * * * * * * * * * +0xe26a MDH2 17 24 3 0 * * * * * * * * * * +0xe26b MDTR 17 24 3 0 * * * * * * * * * * +0xe26d MFE2 17 24 3 0 * * * * * * * * * * +0xe26e MFEY 17 24 3 0 * * * * * * * * * * +0xe26f MGFO 17 24 4 0 * * * * * * * * * * +0xe270 MGO5 17 16 3 0 * * * * * * * * * * +0xe271 MGOC 17 16 3 0 * * * * * * * * * * +0xe272 MGWO 17 24 3 0 * * * * * * * * * * +0xe273 MGW2 17 24 3 0 * * * * * * * * * * +0xe274 MHOH 17 24 3 0 * * * * * * * * * * +0xe276 MLEM 17 16 3 0 * * * * * * * * * * +0xe279 MNOS 17 24 3 0 * * * * * * * * * * +0xe27d MWEB 17 16 3 0 * * * * * * * * * * +0xe27e MWRA 17 16 3 0 * * * * * * * * * * +0xe27f MBOA 17 24 3 0 MBOA_BR * * * * * * * * * +0xe280 MWOR 17 24 3 0 * * * * * * * * * * +0xe281 MYUH 17 16 3 0 * * * * * * * * * * +0xe282 MDRF 17 32 4 0 * * * * * * * * * * +0xe283 MDRM 17 32 4 0 * * * * * * * * * * +0xe288 MGWO 17 24 3 0 MGWO_DK * * * * * * * * * +0xe289 MGW2 17 24 3 0 MGW2_DK * * * * * * * * * +0xe28a MTRO 17 16 3 0 MTRO_DK * * * * * * * * * +0xe28b MABW 17 24 3 0 * * * * * * * * * * +0xe28c MWD2 17 36 5 0 * * * * * * * * * * +0xe28d MWD2 17 36 5 0 MWD2_SI * * * * * * * * * +0xe28e MWD2 17 36 5 0 MWD2_GR * * * * * * * * * +0xe28f MDRD 17 16 3 0 MDRD_RE * * * * * * * * * +0xe290 MDH2 17 24 3 0 MDH2_GR * * * * * * * * * +0xe291 MBU2 17 16 3 0 MBU2_SH * * * * * * * * * +0xe292 METN 17 48 5 0 METN_GH * * * * * * * * * +0xe293 MGVE 17 40 5 0 * * * * * * * * * * +0xe294 MBON 17 16 3 0 * * * * * * * * * * +0xe300 MGHO 17 16 3 0 * * * 1 * * * * * * +0xe310 MGH2 17 16 3 0 * * * * * * * * * * +0xe320 MGH3 17 16 3 0 * * * * * * * * * * +0xe400 MGO1 17 16 3 0 * * * * * * * * * * +0xe410 MGO2 17 16 3 0 * * * * * * * * * * +0xe420 MGO3 17 16 3 0 * * * * * * * * * * +0xe430 MGO4 17 16 3 0 * * * * * * * * * * +0xe440 MGO6 17 16 3 0 * * * * * * * * * * +0xe441 MGO7 17 16 3 0 * * * * * * * * * * +0xe442 MGO8 17 16 3 0 * * * * * * * * * * +0xe443 MGO9 17 16 3 0 * * * * * * * * * * +0xe444 MGO10 17 16 3 0 * * * * * * * * * * +0xe500 MLIZ 17 24 3 0 * * * * * * * * * * +0xe510 MLI2 17 24 3 0 * * * * * * * * * * +0xe520 MLI3 17 32 5 0 * * * * * * * * * * +0xe600 MMYC 17 16 3 0 * * * * * * * * * * +0xe610 MMY2 17 16 3 0 * * * * * * * * * * +0xe700 MNO1 17 20 3 0 * * * * * * * * * * +0xe710 MNO2 17 20 3 0 * * * * * * * * * * +0xe720 MNO3 17 24 3 0 * * * * * * * * * * +0xe800 MOR1 17 16 3 0 * * * * * * * * * * +0xe810 MOR2 17 16 3 0 * * * * * * * * * * +0xe820 MOR3 17 16 3 0 * * * * * * * * * * +0xe830 MOR4 17 16 3 0 * * * * * * * * * * +0xe840 MOR5 17 16 3 0 * * * * * * * * * * +0xe900 MSAL 17 16 3 0 * * * * * * * * * * +0xe910 MSA2 17 16 3 0 * * * * * * * * * * +0xea00 MSHR 17 24 3 0 * * * * * * * * * * +0xea10 MSH1 17 16 3 0 * * * 1 * * * * * * +0xea20 MSH2 17 24 3 0 * * * 1 * * * * * * +0xeb00 MSKT 17 16 3 0 * * * * * * * * * * +0xeb10 MSKA 17 16 3 0 * * * * * * * * * * +0xeb20 MSKB 17 24 3 0 * * * * * * * * * * +0xec00 MWIG 17 16 3 0 * * * * * * * * * * +0xec10 MWI2 17 16 3 0 * * * * * * * * * * +0xec20 MWI3 17 16 3 0 * * * * * * * * * * +0xed00 MYU1 17 16 3 0 * * * * * * * * * * +0xed10 MYU2 17 16 3 0 * * * * * * * * * * +0xed20 MYU3 17 16 3 0 * * * * * * * * * * +0xee00 MZO2 17 16 3 0 * * * * * * * * * * +0xee10 MZO3 17 16 3 0 * * * * * * * * * * +0xef10 MWWE 17 24 3 0 * * * * * * * * * * diff --git a/src/org/infinity/resource/cre/decoder/tables/avatars-eet.2da b/src/org/infinity/resource/cre/decoder/tables/avatars-eet.2da new file mode 100644 index 000000000..265437607 --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/tables/avatars-eet.2da @@ -0,0 +1,519 @@ +2DA V1.0 +* + RESREF TYPE ELLIPSE SPACE BLENDING PALETTE PALETTE2 RESREF2 TRANSLUCENT CLOWN SPLIT HELMET WEAPON HEIGHT HEIGHT_SHIELD +0x0000 SPRING 0 0 0 0 * * * * 1 0 * * * * +0x0001 SPFLAMES 0 0 0 7 * * * * 0 0 * * * * +0x0002 SPRDRASI 0 0 0 7 * * * * 0 0 * * * * +0x0003 SPFLAMES 0 0 0 7 SPFLAMEB * * * 0 0 * * * * +0x0004 SPRDRASI 0 0 0 7 SPGDRASI * * * 0 0 * * * * +0x0100 SPCHUNKS 0 0 0 0 * * SPSHADOW * 1 1 * * * * +0x0101 BGLRYU 0 0 3 7 * * * * * 0 * * * * +0x0102 SPCL236U 0 0 3 7 * * * * * 0 * * * * +0x0200 SPBLOOD 0 0 0 0 * * * * 0 0 0 * * * +0x0210 SPBLOOD 0 0 0 0 * * * * 0 0 1 * * * +0x0220 SPBLOOD 0 0 0 0 * * * * 0 0 2 * * * +0x0230 SPBLOOD 0 0 0 0 * * * * 0 0 3 * * * +0x0240 SPBLOOD 0 0 0 0 * * * * 0 0 4 * * * +0x0300 SPSMPUFF 0 0 0 0 * * * 1 1 * * * * * +0x0301 SPSMPUFF 0 0 0 0 * * * 1 1 * * * * * +0x0400 SKLH 0 0 0 0 * * SPSHADOW * 0 1 * * * * +0x0410 GLPHWRDH 0 0 0 1 * * SPSHADOW * 0 1 * * * * +0x0500 STNKCLDD 0 0 0 0 * * * * 1 0 1 * * * +0x0510 STNKCLDD 0 0 0 0 * * * * 1 0 0 * * * +0x0520 SPHORPUF 0 0 0 1 * * * * 0 0 * * * * +0x0600 STNKCLDD 0 0 0 0 * * * * 1 0 1 * * * +0x0610 SPICESTM 0 0 0 1 * * * * 0 0 0 * * * +0x0700 GREASEH 0 0 0 0 * * * * 0 0 * * * * +0x0710 GREASED 0 0 0 0 * * * * 0 1 * * * * +0x0800 WEBENTH 0 0 0 0 * * * * 1 0 * * * * +0x0810 WEBENTD 0 0 0 0 * * * * 1 0 * * * * +0x0900 STNKCLDD 0 0 0 0 * * * * 1 0 1 * * * +0x0910 SPMETSWM 0 0 0 1 * * * * 0 1 0 * * * +0x0a00 SPHORPUF 0 0 0 1 * * * * 0 0 * * * * +0x0a01 SPWRDFLD 0 0 0 1 * * * * 0 1 * * * * +0x0a02 SPENTAAI 0 0 0 1 * * * * 0 1 * * * * +0x0a03 SPHLYSM2 0 0 0 1 * * * * 0 1 * * * * +0x0a04 SPUNHBL2 0 0 0 1 * * * * 0 1 * * * * +0x0a10 SPHORPUF 0 0 0 1 * * * * 0 0 * * * * +0x0a11 SPWRDFLD 0 0 0 1 * * * * 0 1 * * * * +0x0a12 SPENTAAI 0 0 0 1 * * * * 0 1 * * * * +0x0a13 SPHLYSM2 0 0 0 1 * * * * 0 1 * * * * +0x0a14 SPUNHBL2 0 0 0 1 * * * * 0 1 * * * * +0x0a23 SPHLYSM2 0 0 0 1 * * * * 0 1 * * * * +0x0a24 SPUNHBL2 0 0 0 1 * * * * 0 1 * * * * +0x0b00 SPCSPRA2 0 0 0 1 * * * * 0 0 * * * * +0x0b01 SPCCOLDL 0 0 0 1 * * * * 0 0 * * * * +0x0b02 SPPRISM2 0 0 0 1 * * * * 0 0 * * * * +0x0b03 SPCSPRA3 0 0 0 1 * * * * 0 0 * * * * +0x0b04 SPPRISM3 0 0 0 1 * * * * 0 0 * * * * +0x0c00 SPSTRMVA 0 0 0 1 * * * * 0 1 * * SPSTRMVB * +0x0c10 SPSTRMVA 0 0 0 1 * * * * 0 1 * * SPSTRMVB * +0x1000 MWYV 1 24 4 0 * * * * 0 * * * * * +0x1003 MWYV 1 24 4 0 MWYV_WH * * * 0 * * * * * +0x1004 MWYV 1 24 4 0 MWYV_AL * * * 0 * * * * * +0x1100 MTAN 1 32 5 0 * * * * 0 * 1 * * * +0x1101 MWDR 1 72 7 0 * * * * 0 * * * * * +0x1102 MTAN 1 32 5 0 MTAN_BL * * * 0 * 1 * * * +0x1103 MTAN 1 32 5 0 MTAN_GR * * * 0 * 1 * * * +0x1104 MTAN 1 32 5 0 MTAN_RD * * * 0 * 1 * * * +0x1105 MWDR 1 72 7 0 MWDR_GR * * * 0 * * * * * +0x1200 MDR1 2 72 13 0 * * * * 0 1 * * * * +0x1201 MDR2 2 93 13 0 * * * * 0 1 * * * * +0x1202 MDR3 2 72 13 0 * * * * 0 1 * * * * +0x1203 MDR1 2 72 13 0 MDR1_GR * * * 0 1 * * * * +0x1204 MDR1 2 72 13 0 MDR1_AQ * * * 0 1 * * * * +0x1205 MDR1 2 72 13 0 MDR1_BL * * * 0 1 * * * * +0x1206 MDR1 2 72 13 0 MDR1_BR * * * 0 1 * * * * +0x1207 MDR1 2 72 13 0 MDR1_MC * * * 0 1 * * * * +0x1208 MDR1 2 72 13 0 MDR1_PU * * * 0 1 * * * * +0x1300 MDEM 3 32 5 0 * * * * 0 1 * * * * +0x2000 MSIR 5 16 3 0 * * * * 1 * 1 * * BW +0x2100 UVOL 5 16 3 0 * * * * 0 * 1 * MS * +0x2200 MOGM 5 16 3 0 * * * * 1 * 1 1 S1 * +0x2300 MDKN 5 16 3 0 * * * * 0 * 1 1 * * +0x3000 MAKH 6 24 5 0 * * * * 0 * * * * * +0x3001 MNEO 6 60 5 0 * * * * 0 * * * * * +0x4000 SNOMC 7 16 3 0 * * * * 1 * * * * * +0x4001 SNONE 7 0 0 0 * * * * 0 * * * * * +0x4002 SNOMM 7 16 3 0 * * * * 1 * * * * * +0x4010 SNOWC 7 16 3 0 * * * * 1 * * * * * +0x4012 SNOWM 7 16 3 0 * * * * 1 * * * * * +0x4100 SSIMC 7 16 3 0 * * * * 1 * * * * * +0x4101 SSIMS 7 16 3 0 * * * * 1 * * * * * +0x4102 SSIMM 7 16 3 0 * * * * 1 * * * * * +0x4110 SSIWC 7 16 3 0 * * * * 1 * * * * * +0x4112 SSIWM 7 16 3 0 * * * * 1 * * * * * +0x4200 SHMCM 7 16 3 0 * * * * 1 * * * * * +0x4300 MSPLG1 7 32 5 0 * * * * 0 * * * * * +0x4400 LHMC 7 16 3 0 * * * * 1 * * * * * +0x4410 LHFC 7 16 3 0 * * * * 1 * * * * * +0x4500 LFAM 7 16 3 0 * * * * 1 * * * * * +0x4600 LDMF 7 16 3 0 * * * * 1 * * * * * +0x4700 LEMF 7 16 3 0 * * * * 1 * * * * * +0x4710 LEFF 7 16 3 0 * * * * 1 * * * * * +0x4800 LIMC 7 16 3 0 * * * * 1 * * * * * +0x5000 CHMB 8 16 3 0 * * CHMC * 1 1 1 * WQL * +0x5001 CEMB 8 16 3 0 * * CEMC * 1 1 1 * WQM * +0x5002 CDMB 8 16 3 0 * * CDMC * 1 1 1 * WQS * +0x5003 CIMB 8 16 3 0 * * CIMC * 1 1 1 * WQS WQH +0x5010 CHFB 8 16 3 0 * * CHFC * 1 1 1 * WQN * +0x5011 CEFB 8 16 3 0 * * CEFC * 1 1 1 * WQM * +0x5012 CDFB 8 16 3 0 * * CDFC * 1 1 1 * WQS * +0x5013 CIFB 8 16 3 0 * * CIFC * 1 1 1 * WQS * +0x5100 CHMB 8 16 3 0 * * CHMF * 1 1 1 * WQL * +0x5101 CEMB 8 16 3 0 * * CEMF * 1 1 1 * WQM * +0x5102 CDMB 8 16 3 0 * * CDMF * 1 1 1 * WQS * +0x5103 CIMB 8 16 3 0 * * CIMF * 1 1 1 * WQS WQH +0x5110 CHFB 8 16 3 0 * * CHFF * 1 1 1 * WQN * +0x5111 CEFB 8 16 3 0 * * CEFF * 1 1 1 * WQM * +0x5112 CDFB 8 16 3 0 * * CDFF * 1 1 1 * WQS * +0x5113 CIFB 8 16 3 0 * * CIFF * 1 1 1 * WQS * +0x5200 CHMW 8 16 3 0 * * CHMW * 1 1 1 * WQL * +0x5201 CEMW 8 16 3 0 * * CEMW * 1 1 1 * WQM * +0x5202 CDMW 8 16 3 0 * * CDMW * 1 1 1 * WQS * +0x5210 CHFW 8 16 3 0 * * CHFW * 1 1 1 * WQN * +0x5211 CEFW 8 16 3 0 * * CEFW * 1 1 1 * WQM * +0x5212 CDFW 8 16 3 0 * * CDFW * 1 1 1 * WQS * +0x5300 CHMT 8 16 3 0 * * CHMT * 1 1 0 * WQL * +0x5301 CEMT 8 16 3 0 * * CEMT * 1 1 0 * WQM * +0x5302 CDMT 8 16 3 0 * * CDMT * 1 1 0 * WQS * +0x5303 CIMT 8 16 3 0 * * CIMT * 1 1 0 * WQS WQH +0x5310 CHFT 8 16 3 0 * * CHFT * 1 1 0 * WQN * +0x5311 CEFT 8 16 3 0 * * CEFT * 1 1 0 * WQM * +0x5312 CDFT 8 16 3 0 * * CDFT * 1 1 0 * WQS * +0x5313 CIFT 8 16 3 0 * * CIFT * 1 1 0 * WQS * +0x6000 CHMB 8 16 3 0 * * CHMC * 1 1 1 * WQL * +0x6001 CEMB 8 16 3 0 * * CEMC * 1 1 1 * WQM * +0x6002 CDMB 8 16 3 0 * * CDMC * 1 1 1 * WQS * +0x6003 CIMB 8 16 3 0 * * CIMC * 1 1 1 * WQS WQH +0x6004 CDMB 8 16 3 0 * * CDMC * 1 1 1 * WQS WQH +0x6005 CHMB 8 16 3 0 * * CHMC * 1 1 1 * WQL * +0x6010 CHFB 8 16 3 0 * * CHFC * 1 1 1 * WQN * +0x6011 CEFB 8 16 3 0 * * CEFC * 1 1 1 * WQM * +0x6012 CDFB 8 16 3 0 * * CDFC * 1 1 1 * WQS * +0x6013 CIFB 8 16 3 0 * * CIFC * 1 1 1 * WQS * +0x6014 CIFB 8 16 3 0 * * CIFC * 1 1 1 * WQS WQH +0x6015 CHFB 8 16 3 0 * * CHFC * 1 1 1 * WQN * +0x6100 CHMB 8 16 3 0 * * CHMF * 1 1 1 * WQL * +0x6101 CEMB 8 16 3 0 * * CEMF * 1 1 1 * WQM * +0x6102 CDMB 8 16 3 0 * * CDMF * 1 1 1 * WQS * +0x6103 CIMB 8 16 3 0 * * CIMF * 1 1 1 * WQS WQH +0x6104 CDMB 8 16 3 0 * * CDMF * 1 1 1 * WQS WQH +0x6105 CHMB 8 16 3 0 * * CHMF * 1 1 1 * WQL * +0x6110 CHFB 8 16 3 0 * * CHFF * 1 1 1 * WQN * +0x6111 CEFB 8 16 3 0 * * CEFF * 1 1 1 * WQM * +0x6112 CDFB 8 16 3 0 * * CDFF * 1 1 1 * WQS * +0x6113 CIFB 8 16 3 0 * * CIFF * 1 1 1 * WQS * +0x6114 CIFB 8 16 3 0 * * CIFF * 1 1 1 * WQS WQH +0x6115 CHFB 8 16 3 0 * * CHFF * 1 1 1 * WQN * +0x6200 CHMW 8 16 3 0 * * CHMW * 1 1 1 * WQL * +0x6201 CEMW 8 16 3 0 * * CEMW * 1 1 1 * WQM * +0x6202 CDMW 8 16 3 0 * * CDMW * 1 1 1 * WQS * +0x6204 CDMW 8 16 3 0 * * CDMW * 1 1 1 * WQS WQH +0x6205 CHMW 8 16 3 0 * * CHMW * 1 1 1 * WQL * +0x6210 CHFW 8 16 3 0 * * CHFW * 1 1 1 * WQN * +0x6211 CEFW 8 16 3 0 * * CEFW * 1 1 1 * WQM * +0x6212 CDFW 8 16 3 0 * * CDFW * 1 1 1 * WQS * +0x6214 CDFW 8 16 3 0 * * CDFW * 1 1 1 * WQS WQH +0x6215 CHFW 8 16 3 0 * * CHFW * 1 1 1 * WQN * +0x6300 CHMT 8 16 3 0 * * CHMT * 1 1 0 * WQL * +0x6301 CEMT 8 16 3 0 * * CEMT * 1 1 0 * WQM * +0x6302 CDMT 8 16 3 0 * * CDMT * 1 1 0 * WQS * +0x6303 CIMT 8 16 3 0 * * CIMT * 1 1 0 * WQS WQH +0x6304 CDMT 8 16 3 0 * * CDMT * 1 1 0 * WQS WQH +0x6305 CHMT 8 16 3 0 * * CHMT * 1 1 0 * WQL * +0x6310 CHFT 8 16 3 0 * * CHFT * 1 1 0 * WQN * +0x6311 CEFT 8 16 3 0 * * CEFT * 1 1 0 * WQM * +0x6312 CDFT 8 16 3 0 * * CDFT * 1 1 0 * WQS * +0x6313 CIFT 8 16 3 0 * * CIFT * 1 1 0 * WQS * +0x6314 CIFT 8 16 3 0 * * CIFT * 1 1 0 * WQS WQH +0x6315 CHFT 8 16 3 0 * * CHFT * 1 1 0 * WQN * +0x6400 UDRZ 9 16 3 0 * * * * 1 * 1 0 WPM * +0x6401 UELM 9 16 3 0 * * * * 0 * 0 0 WPM * +0x6402 CMNK 9 16 3 0 * * * * 1 * 0 * WPM * +0x6403 MSKL 9 16 3 0 * * * * 1 * 1 * WPM * +0x6404 USAR 9 16 3 0 * * * * 0 * 0 0 WPL * +0x6405 MDGU 9 16 3 0 * * * * 1 * 1 * WPM * +0x6406 MDGU 9 24 7 0 * * * * 1 * 1 * WPM * +0x6500 CHMM 8 16 3 0 * * CHMM * 1 1 1 * WQL * +0x6510 CHFM 8 16 3 0 * * CHFM * 1 1 1 * WQN * +0x6621 XHFF 9 16 3 0 * * * * 1 * 1 * WPM * +0x7000 MOGH 11 16 3 0 * * * * 1 * * * * * +0x7001 MOGN 11 16 3 0 * * * * 1 * * * * * +0x7100 MBAS 11 32 5 0 * * * * 0 * * * * * +0x7101 MBAS 11 32 5 0 MBAS_GR * * * 0 * * * * * +0x7200 MBER 11 16 3 0 MBER_BL * * * 0 * * * * * +0x7201 MBER 11 16 3 0 * * * * 0 * * * * * +0x7202 MBER 11 16 3 0 MBER_CA * * * 0 * * * * * +0x7203 MBER 11 16 3 0 MBER_PO * * * 0 * * * * * +0x7300 MEAE 10 32 5 0 * * * * 0 1 * * * * +0x7301 MEAS 10 16 3 0 * * * * 0 1 * * * * +0x7302 MEAE 10 32 5 0 MEAE_SH * * * 0 1 * * * * +0x7310 MFIE 10 16 3 4 * * * * 0 1 * * * * +0x7311 MFIS 10 16 3 4 * * * * 0 1 * * * * +0x7312 MFIE 10 16 3 4 MFIEG1B MFIEG2B * * 0 1 * * * * +0x7313 MFIS 10 16 3 4 MFISG1B MFISG2B * * 0 1 * * * * +0x7314 MFIE 10 16 3 4 MFIEG31 MFIEG3B * * 0 1 * * * * +0x7320 MAIR 10 32 5 0 * * * 1 0 1 * * * * +0x7321 MAIS 10 16 3 0 * * * 1 0 1 * * * * +0x7400 MDOG 11 16 3 0 MDOG_WI * * * 0 * * * * * +0x7401 MDOG 11 16 3 0 MDOG_WA * * * 0 * * * * * +0x7402 MDOG 11 16 3 0 MDOG_MO * * * 0 * * * * * +0x7500 MDOP 11 16 3 0 * * * * 0 * * * * * +0x7501 MDOP 11 16 3 0 MDOP_GR * * * 0 * * * * * +0x7600 METT 11 16 3 0 * * * * 0 * * * * * +0x7601 MGHL 11 16 3 0 MGHL_MA * * * 0 0 * * * * +0x7602 MSPI 11 16 3 0 MSPI_MY * * * 0 * * * * * +0x7603 MDOG 11 16 3 0 MDOG_GR * * * 0 * * * * * +0x7604 MSPI 11 16 3 0 MSPI_WA * * * 0 * * * * * +0x7700 MGHL 11 16 3 0 * * * * 0 * * * * * +0x7701 MGHL 11 16 3 0 MGHL_RE * * * 0 * * * * * +0x7702 MGHL 11 16 3 0 MGHL_GA * * * 0 * * * * * +0x7703 MSHD 10 16 3 0 * * * 1 0 1 * * * * +0x7800 MGIB 11 16 3 0 * * * * 0 * * * * * +0x7801 MGIB 11 16 3 0 MGIB_BR * * * 0 * * * * * +0x7802 MSLI 11 24 4 0 MSLI_BL * * 0 0 * * * * * +0x7900 MSLI 11 24 4 0 MSLI_GR * * 1 0 * * * * * +0x7901 MSLI 11 24 4 0 MSLI_OL * * 1 0 * * * * * +0x7902 MSLI 11 24 4 0 MSLI_MU * * 1 0 * * * * * +0x7903 MSLI 11 24 4 0 MSLI_OC * * 1 0 * * * * * +0x7904 MSLI 11 24 4 0 * * * 1 0 * * * * * +0x7a00 MSPI 11 16 3 0 MSPI_GI * * * 0 * * * * * +0x7a01 MSPI 11 16 3 0 MSPI_HU * * * 0 * * * * * +0x7a02 MSPI 11 16 3 0 MSPI_PH * * * 0 * * * * * +0x7a03 MSPI 11 16 3 0 MSPI_SW * * * 0 * * * * * +0x7a04 MSPI 11 16 3 0 MSPI_WR * * * 0 * * * * * +0x7b00 MWLF 11 16 3 0 * * * * 0 * * * * * +0x7b01 MWLF 11 16 3 0 MWLF_WO * * * 0 * * * * * +0x7b02 MWLF 11 16 3 0 MWLF_DI * * * 0 * * * * * +0x7b03 MWLF 11 16 3 0 MWLF_WI * * * 0 * * * * * +0x7b04 MWLF 11 16 3 0 MWLF_VA * * * 0 * * * * * +0x7b05 MWLF 11 16 3 0 MWLF_DR * * * 0 * * * * * +0x7b06 MWLS 11 16 3 0 * * * * 0 * * * * * +0x7c00 MXVT 11 16 3 0 * * * * 1 * * * * * +0x7c01 MTAS 11 16 3 0 * * * * 0 * * * * * +0x7d00 MZOM 11 16 3 0 * * * * 1 * * * * * +0x7d01 NSLF 11 16 3 0 * * * * 1 0 * * * * +0x7d02 ACHB 11 12 3 0 * * * * 0 0 * * * * +0x7d03 ACHW 11 12 3 0 * * * * 0 0 * * * * +0x7d04 NPRF 11 16 3 0 * * * * 1 0 * * * * +0x7d05 NNMF 11 16 3 0 * * * * 1 0 * * * * +0x7d06 NNWF 11 16 3 0 * * * * 1 0 * * * * +0x7d07 NSMF 11 16 3 0 * * * * 1 0 * * * * +0x7d08 NSWF 11 16 3 0 * * * * 1 0 * * * * +0x7e00 MWER 11 16 3 0 * * * * 0 * * * * * +0x7e01 MGWE 11 16 3 0 * * * * 0 * * * * * +0x7f00 MTRO 10 16 3 0 * * * * 0 1 * * * * +0x7f01 MMIN 10 16 3 0 * * * * 0 1 * * * * +0x7f02 MBEH 10 32 5 0 * * * * 0 1 * * * * +0x7f03 MIMP 10 16 3 0 * * * * 0 1 * * * * +0x7f04 MIGO 10 32 5 0 * * * * 0 1 * * * * +0x7f05 MDJI 10 16 3 0 * * * * 0 1 * * * * +0x7f06 MDJL 10 16 3 0 * * * * 0 1 * * * * +0x7f07 MGLC 10 16 3 0 * * * * 0 1 * * * * +0x7f08 MOTY 10 24 5 0 * * * * 0 1 * * * * +0x7f09 MSAH 10 16 3 0 * * * * 0 1 * * * * +0x7f0a MGCP 10 16 3 0 * * * * 0 1 * * * * +0x7f0b MGCL 10 16 3 0 * * * * 0 1 * * * * +0x7f0c MKUO 10 16 3 0 * * * * 0 1 * * * * +0x7f0d MLIC 10 16 3 0 * * * * 0 1 * * * * +0x7f0e MDLI 10 16 3 0 * * * * 0 1 * * * * +0x7f0f MTRS 10 16 3 0 * * * * 0 1 * * * * +0x7f10 MRAK 10 16 3 0 * * * * 0 1 * * * * +0x7f11 MUMB 10 16 3 0 * * * * 0 1 * * * * +0x7f12 MVAM 10 16 3 0 * * * * 0 1 * * * * +0x7f13 MSNK 10 16 3 0 * * * * 0 1 * * * * +0x7f14 MGIT 10 16 3 0 * * * * 0 1 * * * * +0x7f15 MBES 10 16 3 0 * * * * 0 1 * * * * +0x7f16 AMOO 10 32 5 0 * * * * 0 1 * * * * +0x7f17 ARAB 10 12 3 0 * * * * 0 1 * * * * +0x7f18 ADER 10 16 3 0 * * * * 0 1 * * * * +0x7f19 MDSW 10 16 3 0 * * * * 0 1 * * * * +0x7f20 AGRO 10 12 3 0 * * * * 0 1 * * * * +0x7f21 APHE 10 12 3 0 * * * * 0 1 * * * * +0x7f22 MVAF 10 16 3 0 * * * * 0 1 * * * * +0x7f23 MSAT 10 16 3 0 * * * * 0 1 * * * * +0x7f24 NPIR 10 16 3 0 * * * * 0 1 * * * * +0x7f27 MDRO 10 16 3 0 * * * * 0 1 * * * * +0x7f28 MKUL 10 16 3 0 * * * * 0 1 * * * * +0x7f29 MFDR 10 16 3 0 * * * * 0 1 * * * * +0x7f2a NSAI 10 16 3 0 * * * * 0 1 * * * * +0x7f2b MMAX 10 16 3 0 * * * * 0 0 * * * * +0x7f2c NSOL 10 16 3 0 * * * * 0 1 * * * * +0x7f2d MWFM 10 16 3 0 * * * * 0 1 * * * * +0x7f2e MRAV 10 32 5 0 * * * * 0 1 * * * * +0x7f2f MSPS 10 16 3 0 * * * * 0 1 * * * * +0x7f30 NBOH 10 16 3 0 * * * * 0 1 * * * * +0x7f31 NELL 10 16 3 0 * * * * 0 1 * * * * +0x7f32 MSLY 10 16 3 0 * * * * 0 1 * * * * +0x7f33 MKUR 10 16 3 0 * * * * 0 0 * * * * +0x7f34 MDOC 10 16 3 0 * * * * 0 0 * * * * +0x7f35 MMIS 10 16 3 0 * * * 1 0 1 * * * * +0x7f36 NSHD 10 16 3 0 * * * * 0 1 * * * * +0x7f37 NIRE 10 16 3 0 * * * * 0 1 * * * * +0x7f38 MEYE 10 16 3 0 * * * * 0 0 * * * * +0x7f39 MMST 10 16 3 1 * * * 0 0 0 * * * * +0x7f3a NIRO 10 16 3 0 * * * * 0 1 * * * * +0x7f3b MSOG 10 16 3 4 * * * 0 0 1 * * * * +0x7f3c MASG 10 16 3 4 * * * 0 0 1 * * * * +0x7f3d MMEL 10 24 3 0 * * * * 0 0 * * * * +0x7f3e MFIG 10 32 5 0 * * * * 0 1 * * * * +0x7f3f MFIG 10 32 5 0 MFIGG1B * * * 0 1 * * * * +0x7f40 MGLM 10 16 3 0 * * * * 0 1 * * * * +0x7f41 MDJL 10 16 3 0 MDJL_E1 MDJL_E2 * * 0 1 * * * * +0x7f42 NIRO 10 16 3 0 NIRO_RD * * * 0 1 * * * * +0x7f43 MOTY 10 16 3 0 MOTY_Y1 MOTY_Y2 * * 0 1 * * * * +0x7f44 NELW 10 16 3 0 * * * * 0 0 * * * * +0x7f45 MSAI 10 16 3 0 * * * 1 0 0 * * * * +0x7f46 MBEG 10 16 3 0 * * * * 0 0 * * * * +0x7f47 MFI2 10 32 5 0 * * * * 0 0 * * * * +0x7f48 MSOF 10 16 3 0 * * * 0 0 1 * * * * +0x7f49 MDMF 10 16 3 0 * * * 0 0 1 * * * * +0x7f4a MDAS 10 16 3 7 * * MDAG 0 0 1 * * * * +0x7f4b MDAF 10 16 3 0 * * * 0 0 1 * * * * +0x7f4c MPLN 10 16 3 7 * * MPLG 0 0 1 * * * * +0x7f4d MPLF 10 16 3 0 * * * 0 0 1 * * * * +0x7f4e MDVM 10 16 3 7 * * MDVG 0 0 1 * * * * +0x7f4f MDVF 10 16 3 0 * * * 0 0 1 * * * * +0x7f50 MMST 10 16 3 1 MMST_NI * * 0 0 0 * * * * +0x7f51 MMST 10 16 3 1 MMST_HA * * 0 0 0 * * * * +0x7f52 NSAI 10 16 3 0 NSAI_G1 NSAI_G2 * * 0 1 * * * * +0x7f53 NSAI 10 16 3 0 NSAI_R1 NSAI_R2 * * 0 1 * * * * +0x7f54 NSAI 10 16 3 0 NSAI_D1 NSAI_D2 * * 0 1 * * * * +0x7f55 NSOL 10 16 3 0 NSOL_C1 NSOL_C2 * * 0 1 * * * * +0x7f56 MWIL 10 16 3 7 * * * * 0 0 * * * * +0x7f57 MWIS 10 12 3 7 * * * * 0 0 * * * * +0x7f58 MABB 10 24 3 0 * * * * 0 0 * * * * +0x7f59 MABG 10 24 3 0 * * * * 0 0 * * * * +0x7f5a MABR 10 24 3 0 * * * * 0 0 * * * * +0x7f5b MABR 10 24 3 0 MABR_BL * * * 0 0 * * * * +0x7f5c MHAG 10 16 3 0 * * * * 0 0 * * * * +0x7f5d MHAG 10 16 3 0 MHAG_GR * * * 0 0 * * * * +0x7f5e MHAG 10 16 3 0 MHAG_SE * * * 0 0 * * * * +0x7f5f MHAN 10 16 3 0 * * * * 0 0 * * * * +0x7f60 MSNK 10 16 3 0 MSNK_W1 MSNK_W2 * * 0 1 * * * * +0x7f61 MDRJ 10 48 9 0 * * * * 0 0 * * * * +0x7f62 MDRJ 10 48 9 0 MDRJ_GR * * * 0 0 * * * * +0x8000 MGNL 4 16 3 0 * * * * * * * * S1 HB +0x8100 MHOB 4 16 3 0 * * * * * * * * S1 BW +0x8200 MKOB 4 16 3 0 * * * * * * * * SS BW +0x9000 MOGR 12 16 3 0 * * * * 1 * * * * * +0xa000 MWYV 13 16 3 0 * * * * 0 * * * * * +0xa100 MCAR 13 16 3 0 * * * * 0 * * * * * +0xa200 MWYV 13 24 4 0 MWYV_WS * * * 0 * * * * * +0xa201 MCEN 13 16 3 0 * * * * 0 * * * * * +0xa202 MTUN 13 16 3 0 * * * * 0 * * * * * +0xb000 ACOW 14 32 5 0 * * * * 0 * * * * * +0xb100 AHRS 14 32 5 0 * * * * 0 * * * * * +0xb200 NBEGL 14 16 3 0 * * * * 1 * * * * * +0xb210 NPROL 14 16 3 0 * * * * 1 * * * * * +0xb300 NBOYL 14 16 3 0 * * * * 1 * * * * * +0xb310 NGRLL 14 16 3 0 * * * * 1 * * * * * +0xb400 NFAML 14 16 3 0 * * * * 1 * * * * * +0xb410 NFAWL 14 16 3 0 * * * * 1 * * * * * +0xb500 NSIML 14 16 3 0 * * * * 1 * * * * * +0xb510 NSIWL 14 16 3 0 * * * * 1 * * * * * +0xb600 NNOML 14 16 3 0 * * * * 1 * * * * * +0xb610 NNOWL 14 16 3 0 * * * * 1 * * * * * +0xb700 NSLVL 14 16 3 0 * * * * 1 * * * * * +0xc000 ABAT 15 12 3 0 * * * * 0 * * * * * +0xc100 ACAT 15 12 3 0 * * * * 0 * * * * * +0xc200 ACHK 15 12 3 0 * * * * 0 * * * * * +0xc300 ARAT 15 12 3 0 * * * * 0 * * * * * +0xc400 ASQU 15 12 3 0 * * * * 0 * * * * * +0xc500 ABAT 15 12 3 0 * * * * 0 * * * * * +0xc600 NBEGH 15 16 3 0 * * * * 1 * * * * * +0xc610 NPROH 15 16 3 0 * * * * 1 * * * * * +0xc700 NBOYH 15 16 3 0 * * * * 1 * * * * * +0xc710 NGRLH 15 16 3 0 * * * * 1 * * * * * +0xc800 NFAMH 15 16 3 0 * * * * 1 * * * * * +0xc810 NFAWH 15 16 3 0 * * * * 1 * * * * * +0xc900 NSIMH 15 16 3 0 * * * * 1 * * * * * +0xc910 NSIWH 15 16 3 0 * * * * 1 * * * * * +0xca00 NNOMH 15 16 3 0 * * * * 1 * * * * * +0xca10 NNOWH 15 16 3 0 * * * * 1 * * * * * +0xcb00 NSLVH 15 16 3 0 * * * * 1 * * * * * +0xcc00 MKG1 15 16 3 0 * * * * 0 * * * * * +0xcc01 MKG2 15 16 3 0 * * * * 0 * * * * * +0xcc02 MKG3 15 16 3 0 * * * * 0 * * * * * +0xcc04 ARAT 15 0 1 0 * * * * 0 * * * * * +0xd000 AEAGG1 16 0 3 0 * * * * 0 * * * * * +0xd100 AGULG1 16 0 3 0 * * * * 0 * * * * * +0xd200 AVULG1 16 0 3 0 * * * * 0 * * * * * +0xd300 ABIRG1 16 0 3 0 * * * * 0 * * * * * +0xd400 ABIRG1 16 0 3 0 * * * * 0 * * * * * +0xe000 MCYC 17 48 5 0 * * * * * * * * * * +0xe010 METN 17 48 5 0 * * * * * * * * * * +0xe020 MTAN 17 32 5 0 * * * * * * * * * * +0xe040 MHIS 17 16 3 0 * * * * * * * * * * +0xe050 MLER 17 16 3 0 * * * * * * * * * * +0xe060 MLIC 17 16 3 0 * * * * * * * * * * +0xe070 MMIN 17 24 3 0 * * * * * * * * * * +0xe080 MMUM 17 16 3 0 * * * * * * * * * * +0xe090 MTAN 17 16 3 0 * * * * * * * * * * +0xe0a0 MTIC 17 20 3 0 * * * * * * * * * * +0xe0b0 MTRO 17 16 3 0 * * * * * * * * * * +0xe0c0 MTSN 17 24 3 0 * * * * * * * * * * +0xe0d0 MUMB 17 24 3 0 * * * * * * * * * * +0xe0e0 MCOR 17 24 3 0 * * * * * * * * * * +0xe0f0 MGIC 17 32 5 0 * * * * * * * * * * +0xe0f1 MGLA 17 24 3 0 * * * * * * * * * * +0xe0f2 MWAV 17 16 3 0 * * * 1 * * * * * * +0xe200 MBET 17 24 3 0 * * * * * * * * * * +0xe210 MBFI 17 12 3 0 * * * * * * * * * * +0xe220 MBBM 17 16 3 0 * * * * * * * * * * +0xe230 MBRH 17 64 7 0 * * * * * * * * * * +0xe240 MANI 17 24 3 0 * * * * * * * * * * +0xe241 MAN2 17 24 3 0 * * * * * * * * * * +0xe242 MAN3 17 24 3 0 * * * * * * * * * * +0xe243 MARU 17 16 3 0 * * * * * * * * * * +0xe244 MBA1 17 16 3 0 * * * * * * * * * * +0xe245 MBA2 17 16 3 0 * * * * * * * * * * +0xe246 MBA3 17 16 3 0 * * * * * * * * * * +0xe247 MBA4 17 16 3 0 * * * * * * * * * * +0xe248 MBA5 17 16 3 0 * * * * * * * * * * +0xe249 MBA6 17 16 3 0 * * * * * * * * * * +0xe24a MBAI 17 16 3 0 * * * * * * * * * * +0xe24b MELE 17 36 3 0 * * * * * * * * * * +0xe24c MELF 17 36 3 0 * * * * * * * * * * +0xe24d MELW 17 36 3 0 * * * * * * * * * * +0xe24e MGFI 17 48 3 0 * * * * * * * * * * +0xe24f MGFR 17 48 5 0 * * * * * * * * * * +0xe250 MGIR 17 24 3 0 * * * * * * * * * * +0xe251 MGVE 17 32 5 0 * * * * * * * * * * +0xe252 MHAR 17 16 3 0 * * * * * * * * * * +0xe253 MREM 17 48 5 0 * * * * * * * * * * +0xe254 MSCR 17 24 3 0 * * * * * * * * * * +0xe255 MSEE 17 16 3 0 * * * * * * * * * * +0xe256 MBE1 17 32 4 0 * * * * * * * * * * +0xe257 MBE2 17 16 3 0 * * * * * * * * * * +0xe258 MBE2 17 16 3 0 MBE2_HE * * * * * * * * * +0xe259 MSVI 17 16 3 0 * * * * * * * * * * +0xe25a MSV2 17 16 3 0 * * * * * * * * * * +0xe25b MUM2 17 24 3 0 * * * * * * * * * * +0xe25c MTA2 17 16 3 0 * * * * * * * * * * +0xe25d MYET 17 24 3 0 * * * * * * * * * * +0xe25e MWI4 17 16 3 0 * * * * * * * * * * +0xe25f MDRD 17 16 3 0 * * * * * * * * * * +0xe260 MCRD 17 24 3 0 * * * * * * * * * * +0xe261 MSAH 17 24 3 0 * * * * * * * * * * +0xe262 MSAT 17 24 3 0 * * * * * * * * * * +0xe263 MSV3 17 16 3 0 * * * * * * * * * * +0xe264 MSV4 17 16 3 0 * * * * * * * * * * +0xe265 MBOA 17 24 3 0 * * * * * * * * * * +0xe266 MBU2 17 16 3 0 * * * * * * * * * * +0xe267 MBUG 17 16 3 0 * * * * * * * * * * +0xe26a MDH2 17 24 3 0 * * * * * * * * * * +0xe26b MDTR 17 24 3 0 * * * * * * * * * * +0xe26d MFE2 17 24 3 0 * * * * * * * * * * +0xe26e MFEY 17 24 3 0 * * * * * * * * * * +0xe26f MGFO 17 24 4 0 * * * * * * * * * * +0xe270 MGO5 17 16 3 0 * * * * * * * * * * +0xe271 MGOC 17 16 3 0 * * * * * * * * * * +0xe272 MGWO 17 24 3 0 * * * * * * * * * * +0xe273 MGW2 17 24 3 0 * * * * * * * * * * +0xe274 MHOH 17 24 3 0 * * * * * * * * * * +0xe276 MLEM 17 16 3 0 * * * * * * * * * * +0xe279 MNOS 17 24 3 0 * * * * * * * * * * +0xe27d MWEB 17 16 3 0 * * * * * * * * * * +0xe27e MWRA 17 16 3 0 * * * * * * * * * * +0xe27f MBOA 17 24 3 0 MBOA_BR * * * * * * * * * +0xe280 MWOR 17 24 3 0 * * * * * * * * * * +0xe281 MYUH 17 16 3 0 * * * * * * * * * * +0xe282 MDRF 17 32 4 0 * * * * * * * * * * +0xe283 MDRM 17 32 4 0 * * * * * * * * * * +0xe288 MGWO 17 24 3 0 MGWO_DK * * * * * * * * * +0xe289 MGW2 17 24 3 0 MGW2_DK * * * * * * * * * +0xe28a MTRO 17 16 3 0 MTRO_DK * * * * * * * * * +0xe28b MABW 17 24 3 0 * * * * * * * * * * +0xe28c MWD2 17 36 5 0 * * * * * * * * * * +0xe28d MWD2 17 36 5 0 MWD2_SI * * * * * * * * * +0xe28e MWD2 17 36 5 0 MWD2_GR * * * * * * * * * +0xe28f MDRD 17 16 3 0 MDRD_RE * * * * * * * * * +0xe290 MDH2 17 24 3 0 MDH2_GR * * * * * * * * * +0xe291 MBU2 17 16 3 0 MBU2_SH * * * * * * * * * +0xe292 METN 17 48 5 0 METN_GH * * * * * * * * * +0xe293 MGHI 17 40 5 0 * * * * * * * * * * +0xe294 MBON 17 16 3 0 * * * * * * * * * * +0xe300 MGHO 17 16 3 0 * * * 1 * * * * * * +0xe310 MGH2 17 16 3 0 * * * * * * * * * * +0xe320 MGH3 17 16 3 0 * * * * * * * * * * +0xe400 MGO1 17 16 3 0 * * * * * * * * * * +0xe410 MGO2 17 16 3 0 * * * * * * * * * * +0xe420 MGO3 17 16 3 0 * * * * * * * * * * +0xe430 MGO4 17 16 3 0 * * * * * * * * * * +0xe440 MGO6 17 16 3 0 * * * * * * * * * * +0xe441 MGO7 17 16 3 0 * * * * * * * * * * +0xe442 MGO8 17 16 3 0 * * * * * * * * * * +0xe443 MGO9 17 16 3 0 * * * * * * * * * * +0xe444 MGO10 17 16 3 0 * * * * * * * * * * +0xe500 MLIZ 17 24 3 0 * * * * * * * * * * +0xe510 MLI2 17 24 3 0 * * * * * * * * * * +0xe520 MLI3 17 32 5 0 * * * * * * * * * * +0xe600 MMYC 17 16 3 0 * * * * * * * * * * +0xe610 MMY2 17 16 3 0 * * * * * * * * * * +0xe700 MNO1 17 20 3 0 * * * * * * * * * * +0xe710 MNO2 17 20 3 0 * * * * * * * * * * +0xe720 MNO3 17 24 3 0 * * * * * * * * * * +0xe800 MOR1 17 16 3 0 * * * * * * * * * * +0xe810 MOR2 17 16 3 0 * * * * * * * * * * +0xe820 MOR3 17 16 3 0 * * * * * * * * * * +0xe830 MOR4 17 16 3 0 * * * * * * * * * * +0xe840 MOR5 17 16 3 0 * * * * * * * * * * +0xe900 MSAL 17 16 3 0 * * * * * * * * * * +0xe910 MSA2 17 16 3 0 * * * * * * * * * * +0xea00 MSHR 17 24 3 0 * * * * * * * * * * +0xea10 MSH1 17 16 3 0 * * * 1 * * * * * * +0xea20 MSH2 17 24 3 0 * * * 1 * * * * * * +0xeb00 MSKT 17 16 3 0 * * * * * * * * * * +0xeb10 MSKA 17 16 3 0 * * * * * * * * * * +0xeb20 MSKB 17 24 3 0 * * * * * * * * * * +0xec00 MWIG 17 16 3 0 * * * * * * * * * * +0xec10 MWI2 17 16 3 0 * * * * * * * * * * +0xec20 MWI3 17 16 3 0 * * * * * * * * * * +0xed00 MYU1 17 16 3 0 * * * * * * * * * * +0xed10 MYU2 17 16 3 0 * * * * * * * * * * +0xed20 MYU3 17 16 3 0 * * * * * * * * * * +0xee00 MZO2 17 16 3 0 * * * * * * * * * * +0xee10 MZO3 17 16 3 0 * * * * * * * * * * +0xef10 MWWE 17 24 3 0 * * * * * * * * * * diff --git a/src/org/infinity/resource/cre/decoder/tables/avatars-iwd.2da b/src/org/infinity/resource/cre/decoder/tables/avatars-iwd.2da new file mode 100644 index 000000000..74f59561f --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/tables/avatars-iwd.2da @@ -0,0 +1,273 @@ +2DA V1.0 +* + RESREF TYPE ELLIPSE SPACE BLENDING PALETTE PALETTE2 RESREF2 TRANSLUCENT CLOWN SPLIT HELMET WEAPON HEIGHT HEIGHT_SHIELD +0x0000 SPRING 0 0 0 0 * * * * 1 0 * * * * +0x0100 SPCHUNKS 0 0 0 0 * * SPSHADOW * 1 1 * * * * +0x0200 SPBLOOD 0 0 0 0 * * * * 0 0 0 * * * +0x0210 SPBLOOD 0 0 0 0 * * * * 0 0 1 * * * +0x0220 SPBLOOD 0 0 0 0 * * * * 0 0 2 * * * +0x0230 SPBLOOD 0 0 0 0 * * * * 0 0 3 * * * +0x0240 SPBLOOD 0 0 0 0 * * * * 0 0 4 * * * +0x0300 SPSMPUFF 0 0 0 0 * * * 1 1 * * * * * +0x0400 SKLH 0 0 0 0 * * SPSHADOW * 0 1 * * * * +0x0410 GLPHWRDH 0 0 0 1 * * SPSHADOW * 0 1 * * * * +0x0500 STNKCLDD 0 0 0 0 * * * * 1 0 1 * * * +0x0510 STNKCLDD 0 0 0 0 * * * * 1 0 0 * * * +0x0600 STNKCLDD 0 0 0 0 * * * * 1 0 1 * * * +0x0700 GREASEH 0 0 0 0 * * * * 0 0 * * * * +0x0710 GREASED 0 0 0 0 * * * * 0 1 * * * * +0x0800 WEBENTH 0 0 0 0 * * * * 1 0 * * * * +0x0810 WEBENTD 0 0 0 0 * * * * 1 0 * * * * +0x0900 STNKCLDD 0 0 0 0 * * * * 1 0 1 * * * +0x1000 MWYV 1 24 5 0 * * * * 0 * * * * * +0x1001 MWYV 1 24 5 0 * * * * 0 * * * * * +0x2000 MSIR 5 16 3 0 * * * * 1 * 1 * * BW +0x2100 UVOL 5 16 3 0 * * * * 0 * 1 * MS * +0x2200 MOGM 5 24 5 0 * * * * 1 * 1 1 S1 * +0x2300 MDKN 5 24 5 0 * * * * 0 * 1 1 * * +0x3000 MAKH 6 24 5 0 * * * * 0 * * * * * +0x4000 SNOMC 7 16 3 0 * * * * 1 * * * * * +0x4002 SNOMM 7 16 3 0 * * * * 1 * * * * * +0x4010 SNOWC 7 16 3 0 * * * * 1 * * * * * +0x4012 SNOWM 7 16 3 0 * * * * 1 * * * * * +0x4100 SSIMC 7 16 3 0 * * * * 1 * * * * * +0x4101 SSIMS 7 16 3 0 * * * * 1 * * * * * +0x4102 SSIMM 7 16 3 0 * * * * 1 * * * * * +0x4110 SSIWC 7 16 3 0 * * * * 1 * * * * * +0x4112 SSIWM 7 16 3 0 * * * * 1 * * * * * +0x4200 SHMCM 7 16 3 0 * * * * 1 * * * * * +0x4300 MSPLG1 7 32 5 0 * * * * 0 * * * * * +0x4400 LHMC 7 20 3 0 * * * * 1 * * * * * +0x4410 LHFC 7 20 3 0 * * * * 1 * * * * * +0x4500 LFAM 7 20 3 0 * * * * 1 * * * * * +0x4600 LDMF 7 20 3 0 * * * * 1 * * * * * +0x4700 LEMF 7 20 3 0 * * * * 1 * * * * * +0x4710 LEFF 7 20 3 0 * * * * 1 * * * * * +0x4800 LIMC 7 20 3 0 * * * * 1 * * * * * +0x5000 CHMC 9 16 3 0 * * * * 1 0 1 * WPL * +0x5001 CEMC 9 16 3 0 * * * * 1 0 1 * WPM * +0x5002 CDMC 9 16 3 0 * * * * 1 0 1 * WPS * +0x5003 CIMC 9 16 3 0 * * * * 1 0 1 * WPS * +0x5010 CHFC 9 16 3 0 * * * * 1 0 1 * WPM * +0x5011 CEFC 9 16 3 0 * * * * 1 0 1 * WPM * +0x5012 CDMC 9 16 3 0 * * * * 1 0 1 * WPS * +0x5013 CIFC 9 16 3 0 * * * * 1 0 1 * WPS * +0x5100 CHMF 9 16 3 0 * * * * 1 0 1 * WPL * +0x5101 CEMF 9 16 3 0 * * * * 1 0 1 * WPM * +0x5102 CDMF 9 16 3 0 * * * * 1 0 1 * WPS * +0x5103 CIMF 9 16 3 0 * * * * 1 0 1 * WPS * +0x5110 CHFF 9 16 3 0 * * * * 1 0 1 * WPM * +0x5111 CEFF 9 16 3 0 * * * * 1 0 1 * WPM * +0x5112 CDMF 9 16 3 0 * * * * 1 0 1 * WPS * +0x5113 CIFF 9 16 3 0 * * * * 1 0 1 * WPS * +0x5200 CHMW 9 16 3 0 * * * * 1 0 1 * WPL * +0x5201 CEMW 9 16 3 0 * * * * 1 0 1 * WPM * +0x5202 CDMW 9 16 3 0 * * * * 1 0 1 * WPS * +0x5203 CDMW 9 16 3 0 * * * * 1 0 1 * WPS * +0x5210 CHFW 9 16 3 0 * * * * 1 0 1 * WPM * +0x5211 CEFW 9 16 3 0 * * * * 1 0 1 * WPM * +0x5212 CDMW 9 16 3 0 * * * * 1 0 1 * WPS * +0x5213 CDMW 9 16 3 0 * * * * 1 0 1 * WPS * +0x5300 CHMT 9 16 3 0 * * * * 1 0 0 * WPL * +0x5301 CEMT 9 16 3 0 * * * * 1 0 0 * WPM * +0x5302 CDMT 9 16 3 0 * * * * 1 0 0 * WPS * +0x5303 CIMT 9 16 3 0 * * * * 1 0 0 * WPS * +0x5310 CHFT 9 16 3 0 * * * * 1 0 0 * WPM * +0x5311 CEFT 9 16 3 0 * * * * 1 0 0 * WPM * +0x5312 CDMT 9 16 3 0 * * * * 1 0 0 * WPS * +0x5313 CIFT 9 16 3 0 * * * * 1 0 0 * WPS * +0x6000 CHMC 9 16 3 0 * * * * 1 0 1 * WPL * +0x6001 CEMC 9 16 3 0 * * * * 1 0 1 * WPM * +0x6002 CDMC 9 16 3 0 * * * * 1 0 1 * WPS * +0x6003 CIMC 9 16 3 0 * * * * 1 0 1 * WPS * +0x6010 CHFC 9 16 3 0 * * * * 1 0 1 * WPM * +0x6011 CEFC 9 16 3 0 * * * * 1 0 1 * WPM * +0x6012 CIFC 9 16 3 0 * * * * 1 0 1 * WPS * +0x6013 CIFC 9 16 3 0 * * * * 1 0 1 * WPS * +0x6100 CHMF 9 16 3 0 * * * * 1 0 1 * WPL * +0x6101 CEMF 9 16 3 0 * * * * 1 0 1 * WPM * +0x6102 CDMF 9 16 3 0 * * * * 1 0 1 * WPS * +0x6103 CIMF 9 16 3 0 * * * * 1 0 1 * WPS * +0x6110 CHFF 9 16 3 0 * * * * 1 0 1 * WPM * +0x6111 CEFF 9 16 3 0 * * * * 1 0 1 * WPM * +0x6112 CIFF 9 16 3 0 * * * * 1 0 1 * WPS * +0x6113 CIFF 9 16 3 0 * * * * 1 0 1 * WPS * +0x6200 CHMW 9 16 3 0 * * * * 1 0 1 * WPL * +0x6201 CEMW 9 16 3 0 * * * * 1 0 1 * WPM * +0x6202 CDMW 9 16 3 0 * * * * 1 0 1 * WPS * +0x6203 CDMW 9 16 3 0 * * * * 1 0 1 * WPS * +0x6210 CHFW 9 16 3 0 * * * * 1 0 1 * WPM * +0x6211 CEFW 9 16 3 0 * * * * 1 0 1 * WPM * +0x6212 CDMW 9 16 3 0 * * * * 1 0 1 * WPS * +0x6213 CDMW 9 16 3 0 * * * * 1 0 1 * WPS * +0x6300 CHMT 9 16 3 0 * * * * 1 0 0 * WPL * +0x6301 CEMT 9 16 3 0 * * * * 1 0 0 * WPM * +0x6302 CDMT 9 16 3 0 * * * * 1 0 0 * WPS * +0x6303 CIMT 9 16 3 0 * * * * 1 0 0 * WPS * +0x6310 CHFT 9 16 3 0 * * * * 1 0 0 * WPM * +0x6311 CEFT 9 16 3 0 * * * * 1 0 0 * WPM * +0x6312 CIFT 9 16 3 0 * * * * 1 0 0 * WPS * +0x6313 CIFT 9 16 3 0 * * * * 1 0 0 * WPS * +0x6400 UDRZ 9 16 3 0 * * * * 1 * 1 0 WPM * +0x6401 CTES 9 16 3 0 * * * * 1 * 0 0 WPM * +0x6402 CMNK 9 16 3 0 * * * * 1 * 0 * WPM * +0x6403 MSKL 9 16 3 0 * * * * 1 * 1 * WPM * +0x6404 USAR 9 16 3 0 * * * * 0 * 0 0 WPL * +0x6405 MDGU 9 16 3 0 * * * * 1 * 1 * WPM * +0x7000 MOGH 11 16 3 0 * * * * 1 * * * * * +0x7001 MOGN 11 16 3 0 * * * * 1 * * * * * +0x7100 MBAS 11 32 5 0 * * * * 0 * * * * * +0x7101 MBAS 11 32 5 0 MBAS_GR * * * 0 * * * * * +0x7200 MBER 11 24 5 0 MBER_BL * * * 0 * * * * * +0x7201 MBER 11 24 5 0 * * * * 0 * * * * * +0x7202 MBER 11 24 5 0 MBER_CA * * * 0 * * * * * +0x7203 MBER 11 24 5 0 MBER_PO * * * 0 * * * * * +0x7300 MEAE 10 32 5 0 * * * * 0 1 * * * * +0x7302 MEAE 10 32 5 0 MEAE_SH * * * 0 1 * * * * +0x7400 MDOG 11 16 3 0 MDOG_WI * * * 0 * * * * * +0x7401 MDOG 11 16 3 0 MDOG_WA * * * 0 * * * * * +0x7402 MDOG 11 16 3 0 MDOG_MO * * * 0 * * * * * +0x7500 MDOP 11 16 3 0 * * * * 0 * * * * * +0x7501 MDOP 11 16 3 0 MDOP_GR * * * 0 * * * * * +0x7600 METT 11 16 3 0 * * * * 0 * * * * * +0x7701 MGHL 11 16 3 0 MGHL_RE * * * 0 * * * * * +0x7702 MGHL 11 16 3 0 MGHL_GA * * * 0 * * * * * +0x7800 MGIB 11 16 3 0 * * * * 0 * * * * * +0x7900 MSLI 11 24 4 0 MSLI_GR * * 1 0 * * * * * +0x7901 MSLI 11 24 4 0 MSLI_OL * * 1 0 * * * * * +0x7902 MSLI 11 24 4 0 MSLI_MU * * 1 0 * * * * * +0x7903 MSLI 11 24 4 0 MSLI_OC * * 1 0 * * * * * +0x7904 MSLI 11 24 4 0 * * * 1 0 * * * * * +0x7a00 MSPI 11 24 4 0 MSPI_GI * * * 0 * * * * * +0x7a01 MSPI 11 24 4 0 MSPI_HU * * * 0 * * * * * +0x7a02 MSPI 11 24 4 0 MSPI_PH * * * 0 * * * * * +0x7a03 MSPI 11 24 4 0 MSPI_SW * * * 0 * * * * * +0x7a04 MSPI 11 24 4 0 MSPI_WR * * * 0 * * * * * +0x7b00 MWLF 11 16 3 0 * * * * 0 * * * * * +0x7b01 MWLF 11 16 3 0 MWLF_WO * * * 0 * * * * * +0x7b02 MWLF 11 16 3 0 MWLF_DI * * * 0 * * * * * +0x7b03 MWLF 11 16 3 0 MWLF_WI * * * 0 * * * * * +0x7b04 MWLF 11 16 3 0 MWLF_VA * * * 0 * * * * * +0x7b05 MWLF 11 16 3 0 MWLF_DR * * * 0 * * * * * +0x7c00 MXVT 11 16 3 0 * * * * 1 * * * * * +0x7c01 MTAS 11 16 3 0 * * * * 0 * * * * * +0x7d00 MZOM 11 16 3 0 * * * * 1 * * * * * +0x7e00 MWER 11 16 3 0 * * * * 0 * * * * * +0x7e01 MGWE 11 16 3 0 * * * * 0 * * * * * +0x7f00 MTRO 10 16 3 0 * * * * 0 1 * * * * +0x7f01 MMIN 10 16 3 0 * * * * 0 1 * * * * +0x7f02 MBEH 10 32 5 0 * * * * 0 1 * * * * +0x7f03 MIMP 10 16 3 0 * * * * 0 1 * * * * +0x7f06 MDJL 10 16 3 0 * * * * 0 1 * * * * +0x7f10 MRAK 10 16 3 0 * * * * 0 1 * * * * +0x8000 MGNL 4 20 3 0 * * * * * * * * S1 HB +0x8100 MHOB 4 16 3 0 * * * * * * * * S1 BW +0x8200 MKOB 4 16 3 0 * * * * * * * * SS BW +0x9000 MOGR 12 24 5 0 * * * * 1 * * * * * +0xa000 MWYV 13 16 3 0 * * * * 0 * * * * * +0xa100 MCAR 13 32 5 0 * * * * 0 * * * * * +0xb000 ACOW 14 32 5 0 * * * * 0 * * * * * +0xb100 AHRS 14 32 5 0 * * * * 0 * * * * * +0xb200 NBEGL 14 16 3 0 * * * * 1 * * * * * +0xb210 NPROL 14 16 3 0 * * * * 1 * * * * * +0xb300 NBOYL 14 16 3 0 * * * * 1 * * * * * +0xb310 NGRLL 14 16 3 0 * * * * 1 * * * * * +0xb400 NFAML 14 16 3 0 * * * * 1 * * * * * +0xb410 NFAWL 14 16 3 0 * * * * 1 * * * * * +0xb500 NSIML 14 16 3 0 * * * * 1 * * * * * +0xb510 NSIWL 14 16 3 0 * * * * 1 * * * * * +0xb600 NNOML 14 16 3 0 * * * * 1 * * * * * +0xb610 NNOWL 14 16 3 0 * * * * 1 * * * * * +0xb700 NSLVL 14 16 3 0 * * * * 1 * * * * * +0xc000 ABAT 15 12 3 0 * * * * 0 * * * * * +0xc100 ACAT 15 12 3 0 * * * * 0 * * * * * +0xc200 ACHK 15 12 3 0 * * * * 0 * * * * * +0xc300 ARAT 15 12 3 0 * * * * 0 * * * * * +0xc400 ASQU 15 12 3 0 * * * * 0 * * * * * +0xc500 ABAT 15 12 3 0 * * * * 0 * * * * * +0xc600 NBEGH 15 16 3 0 * * * * 1 * * * * * +0xc610 NPROH 15 16 3 0 * * * * 1 * * * * * +0xc700 NBOYH 15 16 3 0 * * * * 1 * * * * * +0xc710 NGRLH 15 16 3 0 * * * * 1 * * * * * +0xc800 NFAMH 15 16 3 0 * * * * 1 * * * * * +0xc810 NFAWH 15 16 3 0 * * * * 1 * * * * * +0xc900 NSIMH 15 16 3 0 * * * * 1 * * * * * +0xc910 NSIWH 15 16 3 0 * * * * 1 * * * * * +0xca00 NNOMH 15 16 3 0 * * * * 1 * * * * * +0xca10 NNOWH 15 16 3 0 * * * * 1 * * * * * +0xcb00 NSLVH 15 16 3 0 * * * * 1 * * * * * +0xd000 AEAGG1 16 0 3 0 * * * * 0 * * * * * +0xd100 AGULG1 16 0 3 0 * * * * 0 * * * * * +0xd200 AVULG1 16 0 3 0 * * * * 0 * * * * * +0xd300 ABIRG1 16 0 3 0 * * * * 0 * * * * * +0xd400 ABIRG1 16 0 3 0 * * * * 0 * * * * * +0xe000_000f MBET 17 24 3 0 * * * * * * * * * * +0xe010_000f MBBM 17 20 3 0 * * * * * * * * * * +0xe020_000f MBBR 17 16 3 0 * * * * * * * * * * +0xe030_000f MBFI 17 12 3 0 * * * * * * * * * * +0xe040_000f MBRH 17 64 7 0 * * * * * * * * * * +0xe100_000f MCYC 17 48 7 0 * * * * * * * * * * +0xe110_000f METN 17 48 5 0 * * * * * * * * * * +0xe130_000f MGFR 17 40 5 0 * * * * * * * * * * +0xe140_000f MGVE 17 32 5 0 * * * * * * * * * * +0xe210_000f MELE 17 32 5 0 * * * * * * * * * * +0xe220_000f MELF 17 32 5 0 * * * * * * * * * * +0xe230_000f MELW 17 32 5 0 * * * * * * * * * * +0xe300_000f MGH2 17 16 3 0 * * * * * * * * * * +0xe310_000f MGH3 17 16 3 0 * * * * * * * * * * +0xe320_000f MWIG 17 16 3 0 * * * * * * * * * * +0xe330_000f MZO2 17 16 3 0 * * * * * * * * * * +0xe340_000f MWI2 17 16 3 0 * * * * * * * * * * +0xe350_000f MZO3 17 16 3 0 * * * * * * * * * * +0xe360_000f MWI3 17 16 3 0 * * * * * * * * * * +0xe380_000f MMUM 17 16 3 0 * * * * * * * * * * +0xe390_000f MHIS 17 16 3 0 * * * * * * * * * * +0xe400_000f MGO1 17 16 3 0 * * * * * * * * * * +0xe410_000f MGO2 17 16 3 0 * * * * * * * * * * +0xe420_000f MGO3 17 16 3 0 * * * * * * * * * * +0xe430_000f MGO4 17 16 3 0 * * * * * * * * * * +0xe440_000f MSVI 17 16 3 0 * * * * * * * * * * +0xe450_000f MSV2 17 16 3 0 * * * * * * * * * * +0xe510_000f MGIR 17 24 3 0 * * * * * * * * * * +0xe620_000f MSKB 17 24 3 0 * * * * * * * * * * +0xe700_000f MMIN 17 24 3 0 * * * * * * * * * * +0xe710_000f MTRO 17 16 3 0 * * * * * * * * * * +0xe720_000f MTIC 17 16 3 0 * * * * * * * * * * +0xe730_000f MTSN 17 24 3 0 * * * * * * * * * * +0xe750_000f MUMB 17 24 5 0 * * * * * * * * * * +0xe760_000f MYET 17 24 3 0 * * * * * * * * * * +0xe810_000f MOR1 17 16 3 0 * * * * * * * * * * +0xe820_000f MOR2 17 16 3 0 * * * * * * * * * * +0xe830_000f MOR3 17 16 3 0 * * * * * * * * * * +0xe840_000f MOR4 17 16 3 0 * * * * * * * * * * +0xe850_000f MOR5 17 16 3 0 * * * * * * * * * * +0xe860_000f MNO1 17 16 3 0 * * * * * * * * * * +0xe870_000f MNO2 17 16 3 0 * * * * * * * * * * +0xe880_000f MNO3 17 24 5 0 * * * * * * * * * * +0xe890_000f MLI3 17 32 5 0 * * * * * * * * * * +0xe8a0_000f MYU3 17 16 3 0 * * * * * * * * * * +0xe900_000f MSH1 17 16 3 0 * * * 1 * * * * * * +0xe910_000f MSH2 17 24 3 0 * * * 1 * * * * * * +0xe920_000f MGHO 17 16 3 0 * * * 1 * * * * * * +0xea20_000f MCRD 17 24 3 0 * * * * * * * * * * +0xeb00_000f MANI 17 24 3 0 * * * * * * * * * * +0xeb10_000f MAN2 17 24 3 0 * * * * * * * * * * +0xeb20_000f MAN3 17 24 3 0 * * * * * * * * * * +0xeb30_000f MBE1 17 32 3 0 * * * * * * * * * * +0xeb40_000f MBE2 17 16 3 0 * * * * * * * * * * +0xeb60_000f MLIC 17 16 3 0 * * * * * * * * * * +0xeb70_000f MLER 17 16 3 0 * * * * * * * * * * +0xeb90_000f MMYC 17 16 3 0 * * * * * * * * * * +0xeba0_000f MMY2 17 16 3 0 * * * * * * * * * * +0xebb0_000f MSHR 17 24 3 0 * * * * * * * * * * +0xebc0_000f MTAN 17 24 3 0 * * * * * * * * * * +0xebd0_000f MSAL 17 16 3 0 * * * * * * * * * * +0xebe0_000f MSA2 17 16 3 0 * * * * * * * * * * +0xebf1_000f MARU 17 16 3 0 * * * * * * * * * * +0xf000_000f MSKA 17 16 3 0 * * * * * * * * * * +0xf010_000f MSKT 17 16 3 0 * * * * * * * * * * +0xf100_000f MYU1 17 16 3 0 * * * * * * * * * * +0xf110_000f MYU2 17 16 3 0 * * * * * * * * * * +0xf200_000f MLIZ 17 24 5 0 * * * * * * * * * * +0xf210_000f MLI2 17 24 5 0 * * * * * * * * * * +0xf300_000f MGFI 17 40 5 0 * * * * * * * * * * diff --git a/src/org/infinity/resource/cre/decoder/tables/avatars-iwd2.2da b/src/org/infinity/resource/cre/decoder/tables/avatars-iwd2.2da new file mode 100644 index 000000000..f71fce26b --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/tables/avatars-iwd2.2da @@ -0,0 +1,384 @@ +2DA V1.0 +* + RESREF TYPE ELLIPSE SPACE BLENDING PALETTE PALETTE2 RESREF2 TRANSLUCENT CLOWN SPLIT HELMET WEAPON HEIGHT HEIGHT_SHIELD +0x0000 SPRING 0 0 0 0 * * * * 1 0 * * * * +0x0001 SPFLAMES 0 0 0 7 * * * * 0 0 * * * * +0x0100 SPCHUNKS 0 0 0 0 * * SPSHADOW * 1 1 * * * * +0x0200 SPBLOOD 0 0 0 0 * * * * 0 0 0 * * * +0x0210 SPBLOOD 0 0 0 0 * * * * 0 0 1 * * * +0x0220 SPBLOOD 0 0 0 0 * * * * 0 0 2 * * * +0x0230 SPBLOOD 0 0 0 0 * * * * 0 0 3 * * * +0x0240 SPBLOOD 0 0 0 0 * * * * 0 0 4 * * * +0x0300 SPSMPUFF 0 0 0 0 * * * 1 1 * * * * * +0x0400 SKLH 0 0 0 0 * * SPSHADOW * 0 1 * * * * +0x0410 GLPHWRDH 0 0 0 1 * * SPSHADOW * 0 1 * * * * +0x0500 STNKCLDD 0 0 0 0 * * * * 1 0 1 * * * +0x0510 STNKCLDD 0 0 0 0 * * * * 1 0 0 * * * +0x0600 STNKCLDD 0 0 0 0 * * * * 1 0 1 * * * +0x0700 GREASEH 0 0 0 0 * * * * 0 0 * * * * +0x0710 GREASED 0 0 0 0 * * * * 0 1 * * * * +0x0800 WEBENTH 0 0 0 0 * * * * 1 0 * * * * +0x0810 WEBENTD 0 0 0 0 * * * * 1 0 * * * * +0x0900 STNKCLDD 0 0 0 0 * * * * 1 0 1 * * * +0x1000 MWYV 1 24 5 0 * * * * 0 * * * * * +0x1001 MWY2 1 24 5 0 * * * * 0 * * * * * +0x1200 MDR1 2 72 13 0 * * * * 0 1 * * * * +0x1201 MDR2 2 93 13 0 * * * * 0 1 * * * * +0x1202 MDR3 2 72 13 0 * * * * 0 1 * * * * +0x1203 MDR1 2 72 13 0 MDR1_GR * * * 0 1 * * * * +0x1204 MDR1 2 72 13 0 MDR1_AQ * * * 0 1 * * * * +0x1205 MDR1 2 72 13 0 MDR1_BL * * * 0 1 * * * * +0x1206 MDR1 2 72 13 0 MDR1_BR * * * 0 1 * * * * +0x1207 MDR1 2 72 13 0 MDR1_MC * * * 0 1 * * * * +0x1208 MDR1 2 72 13 0 MDR1_PU * * * 0 1 * * * * +0x2000 MSIR 5 16 3 0 * * * * 1 * 1 * * BW +0x2100 UVOL 5 16 3 0 * * * * 0 * 1 * MS * +0x2200 MOGM 5 24 5 0 * * * * 1 * 1 1 S1 * +0x2300 MDKN 5 24 5 0 * * * * 0 * 1 1 * * +0x3000 MAKH 6 24 5 0 * * * * 0 * * * * * +0x4000 SNOMC 7 16 3 0 * * * * 1 * * * * * +0x4002 SNOMM 7 16 3 0 * * * * 1 * * * * * +0x4010 SNOWC 7 16 3 0 * * * * 1 * * * * * +0x4012 SNOWM 7 16 3 0 * * * * 1 * * * * * +0x4100 SSIMC 7 16 3 0 * * * * 1 * * * * * +0x4101 SSIMS 7 16 3 0 * * * * 1 * * * * * +0x4102 SSIMM 7 16 3 0 * * * * 1 * * * * * +0x4110 SSIWC 7 16 3 0 * * * * 1 * * * * * +0x4112 SSIWM 7 16 3 0 * * * * 1 * * * * * +0x4200 SHMCM 7 16 3 0 * * * * 1 * * * * * +0x4300 MSPLG1 7 32 5 0 * * * * 0 * * * * * +0x4400 LHMC 7 20 3 0 * * * * 1 * * * * * +0x4410 LHFC 7 20 3 0 * * * * 1 * * * * * +0x4500 LFAM 7 20 3 0 * * * * 1 * * * * * +0x4600 LDMF 7 20 3 0 * * * * 1 * * * * * +0x4700 LEMF 7 20 3 0 * * * * 1 * * * * * +0x4710 LEFF 7 20 3 0 * * * * 1 * * * * * +0x4800 LIMC 7 20 3 0 * * * * 1 * * * * * +0x5000 CHMB 8 16 3 0 * * CHMC * 1 1 1 * WQL * +0x5001 CEMB 8 16 3 0 * * CEMC * 1 1 1 * WQM * +0x5002 CDMB 8 16 3 0 * * CDMC * 1 1 1 * WQS * +0x5003 CIMB 8 16 3 0 * * CIMC * 1 1 1 * WQS WQH +0x5010 CHFB 8 16 3 0 * * CHFC * 1 1 1 * WQN * +0x5011 CEFB 8 16 3 0 * * CEFC * 1 1 1 * WQM * +0x5012 CDMB 8 16 3 0 * * CDMC * 1 1 1 * WQS * +0x5013 CIFB 8 16 3 0 * * CIFC * 1 1 1 * WQS * +0x5100 CHMB 8 16 3 0 * * CHMF * 1 1 1 * WQL * +0x5101 CEMB 8 16 3 0 * * CEMF * 1 1 1 * WQM * +0x5102 CDMB 8 16 3 0 * * CDMF * 1 1 1 * WQS * +0x5103 CIMB 8 16 3 0 * * CIMF * 1 1 1 * WQS WQH +0x5110 CHFB 8 16 3 0 * * CHFF * 1 1 1 * WQN * +0x5111 CEFB 8 16 3 0 * * CEFF * 1 1 1 * WQM * +0x5112 CDMB 8 16 3 0 * * CDMF * 1 1 1 * WQS * +0x5113 CIFB 8 16 3 0 * * CIFF * 1 1 1 * WQS * +0x5200 CHMW 8 16 3 0 * * CHMW * 1 1 1 * WQL * +0x5201 CEMW 8 16 3 0 * * CEMW * 1 1 1 * WQM * +0x5202 CDMW 8 16 3 0 * * CDMW * 1 1 1 * WQS * +0x5210 CHFW 8 16 3 0 * * CHFW * 1 1 1 * WQN * +0x5211 CEFW 8 16 3 0 * * CEFW * 1 1 1 * WQM * +0x5212 CDMW 8 16 3 0 * * CDMW * 1 1 1 * WQS * +0x5300 CHMT 8 16 3 0 * * CHMT * 1 1 0 * WQL * +0x5301 CEMT 8 16 3 0 * * CEMT * 1 1 0 * WQM * +0x5302 CDMT 8 16 3 0 * * CDMT * 1 1 0 * WQS * +0x5303 CIMT 8 16 3 0 * * CIMT * 1 1 0 * WQS WQH +0x5310 CHFT 8 16 3 0 * * CHFT * 1 1 0 * WQN * +0x5311 CEFT 8 16 3 0 * * CEFT * 1 1 0 * WQM * +0x5312 CDMT 8 16 3 0 * * CDMT * 1 1 0 * WQS * +0x5313 CIFT 8 16 3 0 * * CIFT * 1 1 0 * WQS * +0x6000 CHMB 8 16 3 0 * * CHMC * 1 1 1 * WQL * +0x6001 CEMB 8 16 3 0 * * CEMC * 1 1 1 * WQM * +0x6002 CDMB 8 16 3 0 * * CDMC * 1 1 1 * WQS * +0x6003 CIMB 8 16 3 0 * * CIMC * 1 1 1 * WQS WQH +0x6004 CDMB 8 16 3 0 * * CDMC * 1 1 1 * WQS WQH +0x6005 CHMB 8 16 3 0 * * CHMC * 1 1 1 * WQL WQH +0x6010 CHFB 8 16 3 0 * * CHFC * 1 1 1 * WQN * +0x6011 CEFB 8 16 3 0 * * CEFC * 1 1 1 * WQM * +0x6012 CIFB 8 16 3 0 * * CIFB * 1 1 1 * WQS * +0x6013 CIFB 8 16 3 0 * * CIFC * 1 1 1 * WQS * +0x6014 CIFB 8 16 3 0 * * CIFC * 1 1 1 * WQS WQH +0x6015 CHFB 8 16 3 0 * * CHFC * 1 1 1 * WQL WQH +0x6100 CHMB 8 16 3 0 * * CHMF * 1 1 1 * WQL * +0x6101 CEMB 8 16 3 0 * * CEMF * 1 1 1 * WQM * +0x6102 CDMB 8 16 3 0 * * CDMF * 1 1 1 * WQS * +0x6103 CIMB 8 16 3 0 * * CIMF * 1 1 1 * WQS WQH +0x6104 CDMB 8 16 3 0 * * CDMF * 1 1 1 * WQS WQH +0x6105 CHMB 8 16 3 0 * * CHMF * 1 1 1 * WQL * +0x6110 CHFB 8 16 3 0 * * CHFF * 1 1 1 * WQN * +0x6111 CEFB 8 16 3 0 * * CEFF * 1 1 1 * WQM * +0x6112 CIFB 8 16 3 0 * * CIFF * 1 1 1 * WQS * +0x6113 CIFB 8 16 3 0 * * CIFF * 1 1 1 * WQS * +0x6114 CIFB 8 16 3 0 * * CIFF * 1 1 1 * WQS WQH +0x6115 CHFB 8 16 3 0 * * CHFF * 1 1 1 * WQN * +0x6200 CHMW 8 16 3 0 * * CHMW * 1 1 1 * WQL * +0x6201 CEMW 8 16 3 0 * * CEMW * 1 1 1 * WQM * +0x6202 CDMW 8 16 3 0 * * CDMW * 1 1 1 * WQS * +0x6204 CDMW 8 16 3 0 * * CDMW * 1 1 1 * WQS WQH +0x6205 CHMW 8 16 3 0 * * CHMW * 1 1 1 * WQL * +0x6210 CHFW 8 16 3 0 * * CHFW * 1 1 1 * WQN * +0x6211 CEFW 8 16 3 0 * * CEFW * 1 1 1 * WQM * +0x6212 CDMW 8 16 3 0 * * CDMW * 1 1 1 * WQS * +0x6214 CDMW 8 16 3 0 * * CDMW * 1 1 1 * WQS WQH +0x6215 CHFW 8 16 3 0 * * CHFW * 1 1 1 * WQN * +0x6300 CHMT 8 16 3 0 * * CHMF * 1 1 0 * WQL * +0x6301 CEMT 8 16 3 0 * * CEMF * 1 1 0 * WQM * +0x6302 CDMT 8 16 3 0 * * CDMF * 1 1 0 * WQS * +0x6303 CIMT 8 16 3 0 * * CIMF * 1 1 0 * WQS WQH +0x6304 CDMT 8 16 3 0 * * CDMF * 1 1 0 * WQS WQH +0x6305 CHMT 8 16 3 0 * * CHMF * 1 1 0 * WQL * +0x6310 CHFT 8 16 3 0 * * CHFF * 1 1 0 * WQN * +0x6311 CEFT 8 16 3 0 * * CEFF * 1 1 0 * WQM * +0x6312 CIFT 8 16 3 0 * * CIFF * 1 1 0 * WQS * +0x6313 CIFT 8 16 3 0 * * CIFF * 1 1 0 * WQS * +0x6314 CIFT 8 16 3 0 * * CIFF * 1 1 0 * WQS WQH +0x6315 CHFT 8 16 3 0 * * CHFF * 1 1 0 * WQN * +0x6400 UDRZ 9 16 3 0 * * * * 1 * 1 0 WPM * +0x6401 CTES 9 16 3 0 * * * * 1 * 0 0 WPM * +0x6402 CMNK 9 16 3 0 * * * * 1 * 0 * WPM * +0x6403 MSKL 9 16 3 0 * * * * 1 * 1 * WPM * +0x6404 USAR 9 16 3 0 * * * * 0 * 0 0 WPL * +0x6405 MDGU 9 16 3 0 * * * * 1 * 1 * WPM * +0x6406 MDGU 9 16 3 0 * * * * 1 * 1 * WPL * +0x6500 CHMM 8 16 3 0 * * CHMM * 1 1 1 * WQL * +0x6510 CHFM 8 16 3 0 * * CHFM * 1 1 1 * WQN * +0x7000 MOGH 11 16 3 0 * * * * 1 * * * * * +0x7001 MOGN 11 16 3 0 * * * * 1 * * * * * +0x7100 MBAS 11 32 5 0 * * * * 0 * * * * * +0x7101 MBAS 11 32 5 0 MBAS_GR * * * 0 * * * * * +0x7200 MBER 11 24 5 0 MBER_BL * * * 0 * * * * * +0x7201 MBER 11 24 5 0 * * * * 0 * * * * * +0x7202 MBER 11 24 5 0 MBER_CA * * * 0 * * * * * +0x7203 MBER 11 24 5 0 MBER_PO * * * 0 * * * * * +0x7300 MEAE 10 32 5 0 * * * * 0 1 * * * * +0x7301 MEAS 10 16 3 0 * * * * 0 1 * * * * +0x7302 MEAE 10 32 5 0 MEAE_SH * * * 0 1 * * * * +0x7310 MFIE 10 16 3 4 * * * * 0 1 * * * * +0x7311 MFIS 10 16 3 4 * * * * 0 1 * * * * +0x7320 MAIR 10 32 5 0 * * * 1 0 1 * * * * +0x7321 MAIS 10 16 3 0 * * * 1 0 1 * * * * +0x7400 MDOG 11 16 3 0 MDOG_WI * * * 0 * * * * * +0x7401 MDOG 11 16 3 0 MDOG_WA * * * 0 * * * * * +0x7402 MDOG 11 16 3 0 MDOG_MO * * * 0 * * * * * +0x7500 MDOP 11 16 3 0 * * * * 0 * * * * * +0x7501 MDOP 11 16 3 0 MDOP_GR * * * 0 * * * * * +0x7600 METT 11 16 3 0 * * * * 0 * * * * * +0x7700 MGHL 11 16 3 0 * * * * 0 * * * * * +0x7701 MGHL 11 16 3 0 MGHL_RE * * * 0 * * * * * +0x7702 MGHL 11 16 3 0 MGHL_GA * * * 0 * * * * * +0x7800 MGIB 11 16 3 0 * * * * 0 * * * * * +0x7900 MSLI 11 24 4 0 MSLI_GR * * 1 0 * * * * * +0x7901 MSLI 11 24 4 0 MSLI_OL * * 1 0 * * * * * +0x7902 MSLI 11 24 4 0 MSLI_MU * * 1 0 * * * * * +0x7903 MSLI 11 24 4 0 MSLI_OC * * 1 0 * * * * * +0x7904 MSLI 11 24 4 0 * * * 1 0 * * * * * +0x7a00 MSPI 11 24 4 0 MSPI_GI * * * 0 * * * * * +0x7a01 MSPI 11 24 4 0 MSPI_HU * * * 0 * * * * * +0x7a02 MSPI 11 24 4 0 MSPI_PH * * * 0 * * * * * +0x7a03 MSPI 11 24 4 0 MSPI_SW * * * 0 * * * * * +0x7a04 MSPI 11 24 4 0 MSPI_WR * * * 0 * * * * * +0x7b00 MWLF 11 16 3 0 * * * * 0 * * * * * +0x7b01 MWLF 11 16 3 0 MWLF_WO * * * 0 * * * * * +0x7b02 MWLF 11 16 3 0 MWLF_DI * * * 0 * * * * * +0x7b03 MWLF 11 16 3 0 MWLF_WI * * * 0 * * * * * +0x7b04 MWLF 11 16 3 0 MWLF_VA * * * 0 * * * * * +0x7b05 MWLF 11 16 3 0 MWLF_DR * * * 0 * * * * * +0x7c00 MXVT 11 16 3 0 * * * * 1 * * * * * +0x7c01 MTAS 11 16 3 0 * * * * 0 * * * * * +0x7d00 MZOM 11 16 3 0 * * * * 1 * * * * * +0x7e00 MWER 11 16 3 0 * * * * 0 * * * * * +0x7e01 MGWE 11 16 3 0 * * * * 0 * * * * * +0x7f00 MTRO 10 16 3 0 * * * * 0 1 * * * * +0x7f01 MMIN 10 16 3 0 * * * * 0 1 * * * * +0x7f02 MBEH 10 32 5 0 * * * * 0 1 * * * * +0x7f03 MIMP 10 16 3 0 * * * * 0 1 * * * * +0x7f04 MIGO 10 32 5 0 * * * * 0 1 * * * * +0x7f05 MDJI 10 16 3 0 * * * * 0 1 * * * * +0x7f06 MDJL 10 16 3 0 * * * * 0 1 * * * * +0x7f07 MGLC 10 16 3 0 * * * * 0 1 * * * * +0x7f08 MOTY 10 24 5 0 * * * * 0 1 * * * * +0x7f0a MGCP 10 16 3 0 * * * * 0 1 * * * * +0x7f0b MGCL 10 16 3 0 * * * * 0 1 * * * * +0x7f10 MRAK 10 16 3 0 * * * * 0 1 * * * * +0x7f13 MSNK 10 16 3 0 * * * * 0 1 * * * * +0x7f18 ADER 10 16 3 0 * * * * 0 1 * * * * +0x7f24 NPIR 10 16 3 0 * * * * 0 1 * * * * +0x7f27 MDRO 10 16 3 0 * * * * 0 1 * * * * +0x7f29 MFDR 10 16 3 0 * * * * 0 1 * * * * +0x7f2a NSAI 10 16 3 0 * * * * 0 1 * * * * +0x7f2e MRAV 10 32 5 0 * * * * 0 1 * * * * +0x7f2f MSPS 10 16 3 0 * * * * 0 1 * * * * +0x7f32 MSLY 10 16 3 0 * * * * 0 1 * * * * +0x7f38 MEYE 10 16 3 0 * * * * 0 0 * * * * +0x8000 MGNL 4 20 3 0 * * * * * * * * S1 HB +0x8100 MHOB 4 16 3 0 * * * * * * * * S1 BW +0x8200 MKOB 4 16 3 0 * * * * * * * * SS BW +0x9000 MOGR 12 24 5 0 * * * * 1 * * * * * +0xa000 MWYV 13 16 3 0 * * * * 0 * * * * * +0xa100 MCAR 13 32 5 0 * * * * 0 * * * * * +0xb000 ACOW 14 32 5 0 * * * * 0 * * * * * +0xb100 AHRS 14 32 5 0 * * * * 0 * * * * * +0xb200 NBEGL 14 16 3 0 * * * * 1 * * * * * +0xb210 NPROL 14 16 3 0 * * * * 1 * * * * * +0xb300 NBOYL 14 16 3 0 * * * * 1 * * * * * +0xb310 NGRLL 14 16 3 0 * * * * 1 * * * * * +0xb400 NFAML 14 16 3 0 * * * * 1 * * * * * +0xb410 NFAWL 14 16 3 0 * * * * 1 * * * * * +0xb500 NSIML 14 16 3 0 * * * * 1 * * * * * +0xb510 NSIWL 14 16 3 0 * * * * 1 * * * * * +0xb600 NNOML 14 16 3 0 * * * * 1 * * * * * +0xb610 NNOWL 14 16 3 0 * * * * 1 * * * * * +0xb700 NSLVL 14 16 3 0 * * * * 1 * * * * * +0xc000 ABAT 15 12 3 0 * * * * 0 * * * * * +0xc100 ACAT 15 12 3 0 * * * * 0 * * * * * +0xc200 ACHK 15 12 3 0 * * * * 0 * * * * * +0xc300 ARAT 15 12 3 0 * * * * 0 * * * * * +0xc400 ASQU 15 12 3 0 * * * * 0 * * * * * +0xc500 ABAT 15 12 3 0 * * * * 0 * * * * * +0xc600 NBEGH 15 16 3 0 * * * * 1 * * * * * +0xc610 NPROH 15 16 3 0 * * * * 1 * * * * * +0xc700 NBOYH 15 16 3 0 * * * * 1 * * * * * +0xc710 NGRLH 15 16 3 0 * * * * 1 * * * * * +0xc800 NFAMH 15 16 3 0 * * * * 1 * * * * * +0xc810 NFAWH 15 16 3 0 * * * * 1 * * * * * +0xc900 NSIMH 15 16 3 0 * * * * 1 * * * * * +0xc910 NSIWH 15 16 3 0 * * * * 1 * * * * * +0xca00 NNOMH 15 16 3 0 * * * * 1 * * * * * +0xca10 NNOWH 15 16 3 0 * * * * 1 * * * * * +0xcb00 NSLVH 15 16 3 0 * * * * 1 * * * * * +0xd000 AEAGG1 16 0 3 0 * * * * 0 * * * * * +0xd100 AGULG1 16 0 3 0 * * * * 0 * * * * * +0xd200 AVULG1 16 0 3 0 * * * * 0 * * * * * +0xd300 ABIRG1 16 0 3 0 * * * * 0 * * * * * +0xd400 ABIRG1 16 0 3 0 * * * * 0 * * * * * +0xe000_000f MBET 17 24 3 0 * * * * * * * * * * +0xe010_000f MBBM 17 20 3 0 * * * * * * * * * * +0xe020_000f MBBR 17 16 3 0 * * * * * * * * * * +0xe030_000f MBFI 17 12 3 0 * * * * * * * * * * +0xe040_000f MBRH 17 64 7 0 * * * * * * * * * * +0xe050_000f MREM 17 48 5 0 * * * * * * * * * * +0xe060_000f MHOH 17 24 3 0 * * * * * * * * * * +0xe100_000f MCYC 17 48 7 0 * * * * * * * * * * +0xe110_000f METN 17 48 5 0 * * * * * * * * * * +0xe130_000f MGFR 17 40 5 0 * * * * * * * * * * +0xe140_000f MGVE 17 32 5 0 * * * * * * * * * * +0xe150_000f MGFO 17 32 5 0 * * * * * * * * * * +0xe210_000f MELE 17 32 5 0 * * * * * * * * * * +0xe220_000f MELF 17 32 5 0 * * * * * * * * * * +0xe230_000f MELW 17 32 5 0 * * * * * * * * * * +0xe240_000f MHAR 17 16 3 0 * * * * * * * * * * +0xe250_000f MWWE 17 24 3 0 * * * * * * * * * * +0xe260_000f MFEY 17 24 3 0 * * * * * * * * * * +0xe270_000f MDTR 17 24 3 0 * * * * * * * * * * +0xe280_000f MFE2 17 24 3 0 * * * * * * * * * * +0xe290_000f MEW2 17 24 3 0 * * * * * * * * * * +0xe300_000f MGH2 17 16 3 0 * * * * * * * * * * +0xe310_000f MGH3 17 16 3 0 * * * * * * * * * * +0xe320_000f MWIG 17 16 3 0 * * * * * * * * * * +0xe330_000f MZO2 17 16 3 0 * * * * * * * * * * +0xe340_000f MZO3 17 16 3 0 * * * * * * * * * * +0xe350_000f MWI2 17 16 3 0 * * * * * * * * * * +0xe360_000f MWI3 17 16 3 0 * * * * * * * * * * +0xe380_000f MMUM 17 16 3 0 * * * * * * * * * * +0xe390_000f MHIS 17 16 3 0 * * * * * * * * * * +0xe3a0_000f MDRD 17 24 3 0 * * * * * * * * * * +0xe3b0_000f MWAV 17 16 3 0 * * * 1 * * * * * * +0xe400_000f MGO1 17 16 3 0 * * * * * * * * * * +0xe410_000f MGO2 17 16 3 0 * * * * * * * * * * +0xe420_000f MGO3 17 16 3 0 * * * * * * * * * * +0xe430_000f MGO4 17 16 3 0 * * * * * * * * * * +0xe440_000f MSVI 17 16 3 0 * * * * * * * * * * +0xe450_000f MSV2 17 16 3 0 * * * * * * * * * * +0xe460_000f MGWO 17 24 3 0 * * * * * * * * * * +0xe470_000f MGOC 17 16 3 0 * * * * * * * * * * +0xe480_000f MGW2 17 24 3 0 * * * * * * * * * * +0xe490_000f MGO5 17 16 3 0 * * * * * * * * * * +0xe510_000f MGIR 17 24 3 0 * * * * * * * * * * +0xe520_000f MGIC 17 32 5 0 * * * * * * * * * * +0xe620_000f MSKB 17 24 3 0 * * * * * * * * * * +0xe700_000f MMIN 17 24 3 0 * * * * * * * * * * +0xe710_000f MTRO 17 16 3 0 * * * * * * * * * * +0xe720_000f MTIC 17 16 3 0 * * * * * * * * * * +0xe730_000f MTSN 17 24 3 0 * * * * * * * * * * +0xe750_000f MUMB 17 24 5 0 * * * * * * * * * * +0xe760_000f MYET 17 24 3 0 * * * * * * * * * * +0xe770_000f MBA4 17 16 3 0 * * * * * * * * * * +0xe780_000f MBA5 17 16 3 0 * * * * * * * * * * +0xe790_000f MBA6 17 16 3 0 * * * * * * * * * * +0xe7a0_000f MBAI 17 16 3 0 * * * * * * * * * * +0xe7b0_000f MBOA 17 24 3 0 * * * * * * * * * * +0xe7c0_000f MABW 17 24 3 0 * * * * * * * * * * +0xe7d0_000f MMAL 17 16 3 0 * * * * * * * * * * +0xe7e0_000f MSCR 17 24 3 0 * * * * * * * * * * +0xe7f0_000f MUM2 17 24 5 0 * * * * * * * * * * +0xe800_000f MOR6 17 16 3 0 * * * * * * * * * * +0xe810_000f MOR1 17 16 3 0 * * * * * * * * * * +0xe820_000f MOR2 17 16 3 0 * * * * * * * * * * +0xe830_000f MOR3 17 16 3 0 * * * * * * * * * * +0xe840_000f MOR4 17 16 3 0 * * * * * * * * * * +0xe850_000f MOR5 17 16 3 0 * * * * * * * * * * +0xe860_000f MNO1 17 16 3 0 * * * * * * * * * * +0xe870_000f MNO2 17 16 3 0 * * * * * * * * * * +0xe880_000f MNO3 17 24 5 0 * * * * * * * * * * +0xe890_000f MLI3 17 32 5 0 * * * * * * * * * * +0xe8a0_000f MYU3 17 16 3 0 * * * * * * * * * * +0xe8b0_000f MYUH 17 16 3 0 * * * * * * * * * * +0xe8c0_000f MBUG 17 16 3 0 * * * * * * * * * * +0xe8d0_000f MNOS 17 24 3 0 * * * * * * * * * * +0xe8e0_000f MBU2 17 16 3 0 * * * * * * * * * * +0xe8f0_000f MOR7 17 16 3 0 * * * * * * * * * * +0xe900_000f MSH1 17 16 3 0 * * * 1 * * * * * * +0xe910_000f MSH2 17 24 3 0 * * * 1 * * * * * * +0xe920_000f MGHO 17 16 3 0 * * * 1 * * * * * * +0xea20_000f MCRD 17 24 3 0 * * * * * * * * * * +0xeb00_000f MANI 17 24 3 0 * * * * * * * * * * +0xeb10_000f MAN2 17 24 3 0 * * * * * * * * * * +0xeb20_000f MAN3 17 24 3 0 * * * * * * * * * * +0xeb30_000f MBE1 17 32 3 0 * * * * * * * * * * +0xeb40_000f MBE2 17 16 3 0 * * * * * * * * * * +0xeb51 MSEE 17 16 3 0 * * * * * * * * * * +0xeb60_000f MLIC 17 16 3 0 * * * * * * * * * * +0xeb70_000f MLER 17 16 3 0 * * * * * * * * * * +0xeb90_000f MMYC 17 16 3 0 * * * * * * * * * * +0xeba0_000f MMY2 17 16 3 0 * * * * * * * * * * +0xebb0_000f MSHR 17 24 3 0 * * * * * * * * * * +0xebc0_000f MTAN 17 24 3 0 * * * * * * * * * * +0xebd0_000f MSAL 17 16 3 0 * * * * * * * * * * +0xebe0_000f MSA2 17 16 3 0 * * * * * * * * * * +0xebf1_000f MARU 17 16 3 0 * * * * * * * * * * +0xec00_000f MWDR 17 72 7 0 * * * * * * * * * * +0xec10_000f MCHY 17 16 3 0 * * * * * * * * * * +0xec20_000f MSHE 17 24 3 0 * * * * * * * * * * +0xec30_000f MCHI 17 48 5 0 * * * * * * * * * * +0xec40_000f MDH1 17 24 3 0 * * * * * * * * * * +0xec50_000f MDH2 17 24 3 0 * * * * * * * * * * +0xed00_000f MCOR 17 24 5 0 * * * * * * * * * * +0xed10_000f MGLA 17 24 5 0 * * * * * * * * * * +0xed20_000f MLEM 17 16 3 0 * * * * * * * * * * +0xee00_000f MWEB 17 24 3 0 * * * * * * * * * * +0xee10_000f MWRA 17 24 3 0 * * * * * * * * * * +0xef00_000f MISA 17 24 3 0 * * * * * * * * * * +0xef10_000f MMAD 17 24 3 0 * * * * * * * * * * +0xef20_000f MWOR 17 24 5 0 * * * * * * * * * * +0xef50_000f MKG1 17 16 3 0 * * * * * * * * * * +0xef60_000f MKG2 17 16 3 0 * * * * * * * * * * +0xef70_000f MKG3 17 16 3 0 * * * * * * * * * * +0xef90_000f MWIL 17 16 3 0 * * * * * * * * * * +0xefa0_000f MGEN 17 16 2 0 * * * * * * * * * * +0xefb0_000f MGEN 17 24 3 0 * * * * * * * * * * +0xefc0_000f MGEN 17 36 4 0 * * * * * * * * * * +0xefd0_000f MGEN 17 48 5 0 * * * * * * * * * * +0xefe0_000f MGEN 17 12 3 0 * * * * * * * * * * +0xeff0_000f MGEN 17 64 7 0 * * * * * * * * * * +0xf000_000f MSKA 17 16 3 0 * * * * * * * * * * +0xf010_000f MSKT 17 16 3 0 * * * * * * * * * * +0xf020_000f MWI4 17 16 3 0 * * * * * * * * * * +0xf100_000f MYU1 17 16 3 0 * * * * * * * * * * +0xf110_000f MYU2 17 16 3 0 * * * * * * * * * * +0xf200_000f MLIZ 17 24 5 0 * * * * * * * * * * +0xf210_000f MLI2 17 24 5 0 * * * * * * * * * * +0xf300_000f MGFI 17 40 5 0 * * * * * * * * * * +0xf400_000f MSAH 17 24 3 0 * * * * * * * * * * +0xf410_000f MSAT 17 32 5 0 * * * * * * * * * * +0xf500_000f MDRM 17 32 4 0 * * * * * * * * * * +0xf510_000f MDRF 17 32 4 0 * * * * * * * * * * +0xf770_000f MBA1 17 16 3 0 * * * * * * * * * * +0xf780_000f MBA2 17 16 3 0 * * * * * * * * * * +0xf790_000f MBA3 17 16 3 0 * * * * * * * * * * diff --git a/src/org/infinity/resource/cre/decoder/tables/avatars-iwdee.2da b/src/org/infinity/resource/cre/decoder/tables/avatars-iwdee.2da new file mode 100644 index 000000000..4f0d14602 --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/tables/avatars-iwdee.2da @@ -0,0 +1,453 @@ +2DA V1.0 +* + RESREF TYPE ELLIPSE SPACE BLENDING PALETTE PALETTE2 RESREF2 TRANSLUCENT CLOWN SPLIT HELMET WEAPON HEIGHT HEIGHT_SHIELD +0x0000 SPRING 0 0 0 0 * * * * 1 0 * * * * +0x0001 SPFLAMES 0 0 0 7 * * * * 0 0 * * * * +0x0002 SPRDRASI 0 0 0 7 * * * * 0 0 * * * * +0x0003 SPFLAMES 0 0 0 7 SPFLAMEB * * * 0 0 * * * * +0x0004 SPRDRASI 0 0 0 7 SPGDRASI * * * 0 0 * * * * +0x0100 SPCHUNKS 0 0 0 0 * * SPSHADOW * 1 1 * * * * +0x0101 BGLRYU 0 0 3 7 * * * * * 0 * * * * +0x0102 SPCL236U 0 0 3 7 * * * * * 0 * * * * +0x0200 SPBLOOD 0 0 0 0 * * * * 0 0 0 * * * +0x0210 SPBLOOD 0 0 0 0 * * * * 0 0 1 * * * +0x0220 SPBLOOD 0 0 0 0 * * * * 0 0 2 * * * +0x0230 SPBLOOD 0 0 0 0 * * * * 0 0 3 * * * +0x0240 SPBLOOD 0 0 0 0 * * * * 0 0 4 * * * +0x0300 SPSMPUFF 0 0 0 0 * * * 1 1 * * * * * +0x0301 SPSMPUFF 0 0 0 0 * * * 1 1 * * * * * +0x0400 SKLH 0 0 0 0 * * SPSHADOW * 0 1 * * * * +0x0410 GLPHWRDH 0 0 0 1 * * SPSHADOW * 0 1 * * * * +0x0500 STNKCLDD 0 0 0 0 * * * * 1 0 1 * * * +0x0510 STNKCLDD 0 0 0 0 * * * * 1 0 0 * * * +0x0520 SPHORPUF 0 0 0 1 * * * * 0 0 * * * * +0x0600 STNKCLDD 0 0 0 0 * * * * 1 0 1 * * * +0x0610 SPICESTM 0 0 0 1 * * * * 0 0 0 * * * +0x0700 GREASEH 0 0 0 0 * * * * 0 0 * * * * +0x0710 GREASED 0 0 0 0 * * * * 0 1 * * * * +0x0800 WEBENTH 0 0 0 0 * * * * 1 0 * * * * +0x0810 WEBENTD 0 0 0 0 * * * * 1 0 * * * * +0x0900 STNKCLDD 0 0 0 0 * * * * 1 0 1 * * * +0x0910 SPMETSWM 0 0 0 1 * * * * 0 1 0 * * * +0x0a00 SPHORPUF 0 0 0 1 * * * * 0 0 * * * * +0x0a01 SPWRDFLD 0 0 0 1 * * * * 0 1 * * * * +0x0a02 SPENTAAI 0 0 0 1 * * * * 0 1 * * * * +0x0a03 SPHLYSM2 0 0 0 1 * * * * 0 1 * * * * +0x0a04 SPUNHBL2 0 0 0 1 * * * * 0 1 * * * * +0x0a10 SPHORPUF 0 0 0 1 * * * * 0 0 * * * * +0x0a11 SPWRDFLD 0 0 0 1 * * * * 0 1 * * * * +0x0a12 SPENTAAI 0 0 0 1 * * * * 0 1 * * * * +0x0a13 SPHLYSM2 0 0 0 1 * * * * 0 1 * * * * +0x0a14 SPUNHBL2 0 0 0 1 * * * * 0 1 * * * * +0x0a23 SPHLYSM2 0 0 0 1 * * * * 0 1 * * * * +0x0a24 SPUNHBL2 0 0 0 1 * * * * 0 1 * * * * +0x0b00 SPCSPRA2 0 0 0 1 * * * * 0 0 * * * * +0x0b01 SPCCOLDL 0 0 0 1 * * * * 0 0 * * * * +0x0b02 SPPRISM2 0 0 0 1 * * * * 0 0 * * * * +0x0b03 SPCSPRA3 0 0 0 1 * * * * 0 0 * * * * +0x0b04 SPPRISM3 0 0 0 1 * * * * 0 0 * * * * +0x0c00 SPSTRMVA 0 0 0 1 * * * * 0 1 * * SPSTRMVB * +0x0c10 SPSTRMVA 0 0 0 1 * * * * 0 1 * * SPSTRMVB * +0x1000 MWYV 1 24 5 0 * * * * 0 * * * * * +0x1003 MWYV 1 24 5 0 MWYV_WH * * * 0 * * * * * +0x1100 MTAN 1 32 5 0 * * * * 0 * 1 * * * +0x1101 MWDR 1 72 7 0 * * * * 0 * * * * * +0x1102 MTAN 1 32 5 0 MTAN_BL * * * 0 * 1 * * * +0x1103 MTAN 1 32 5 0 MTAN_GR * * * 0 * 1 * * * +0x1104 MTAN 1 32 5 0 MTAN_RD * * * 0 * 1 * * * +0x1200 MDR1 2 72 13 0 * * * * 0 1 * * * * +0x1201 MDR2 2 72 13 0 * * * * 0 1 * * * * +0x1202 MDR3 2 72 13 0 * * * * 0 1 * * * * +0x1203 MDR1 2 72 13 0 MDR1_GR * * * 0 1 * * * * +0x1204 MDR1 2 72 13 0 MDR1_AQ * * * 0 1 * * * * +0x1205 MDR1 2 72 13 0 MDR1_BL * * * 0 1 * * * * +0x1206 MDR1 2 72 13 0 MDR1_BR * * * 0 1 * * * * +0x1207 MDR1 2 72 13 0 MDR1_MC * * * 0 1 * * * * +0x1208 MDR1 2 72 13 0 MDR1_PU * * * 0 1 * * * * +0x1300 MDEM 3 40 5 0 * * * * 0 1 * * * * +0x2000 MSIR 5 16 3 0 * * * * 1 * 1 * * BW +0x2100 UVOL 5 16 3 0 * * * * 0 * 1 * MS * +0x2200 MOGM 5 24 5 0 * * * * 1 * 1 1 S1 * +0x2300 MDKN 5 24 5 0 * * * * 0 * 1 1 * * +0x3000 MAKH 6 24 5 0 * * * * 0 * * * * * +0x4000 SNOMC 7 16 3 0 * * * * 1 * * * * * +0x4002 SNOMM 7 16 3 0 * * * * 1 * * * * * +0x4010 SNOWC 7 16 3 0 * * * * 1 * * * * * +0x4012 SNOWM 7 16 3 0 * * * * 1 * * * * * +0x4100 SSIMC 7 16 3 0 * * * * 1 * * * * * +0x4101 SSIMS 7 16 3 0 * * * * 1 * * * * * +0x4102 SSIMM 7 16 3 0 * * * * 1 * * * * * +0x4110 SSIWC 7 16 3 0 * * * * 1 * * * * * +0x4112 SSIWM 7 16 3 0 * * * * 1 * * * * * +0x4200 SHMCM 7 16 3 0 * * * * 1 * * * * * +0x4300 MSPLG1 7 32 5 0 * * * * 0 * * * * * +0x4400 LHMC 7 20 3 0 * * * * 1 * * * * * +0x4410 LHFC 7 20 3 0 * * * * 1 * * * * * +0x4500 LFAM 7 20 3 0 * * * * 1 * * * * * +0x4600 LDMF 7 20 3 0 * * * * 1 * * * * * +0x4700 LEMF 7 20 3 0 * * * * 1 * * * * * +0x4710 LEFF 7 20 3 0 * * * * 1 * * * * * +0x4800 LIMC 7 20 3 0 * * * * 1 * * * * * +0x5000 CHMB 8 16 3 0 * * CHMC * 1 1 1 * WQL * +0x5001 CEMB 8 16 3 0 * * CEMC * 1 1 1 * WQM * +0x5002 CDMB 8 16 3 0 * * CDMC * 1 1 1 * WQS * +0x5003 CIMB 8 16 3 0 * * CIMC * 1 1 1 * WQS WQH +0x5010 CHFB 8 16 3 0 * * CHFC * 1 1 1 * WQN * +0x5011 CEFB 8 16 3 0 * * CEFC * 1 1 1 * WQM * +0x5012 CDFB 8 16 3 0 * * CDFC * 1 1 1 * WQS * +0x5013 CIFB 8 16 3 0 * * CIFC * 1 1 1 * WQS * +0x5100 CHMB 8 16 3 0 * * CHMF * 1 1 1 * WQL * +0x5101 CEMB 8 16 3 0 * * CEMF * 1 1 1 * WQM * +0x5102 CDMB 8 16 3 0 * * CDMF * 1 1 1 * WQS * +0x5103 CIMB 8 16 3 0 * * CIMF * 1 1 1 * WQS WQH +0x5110 CHFB 8 16 3 0 * * CHFF * 1 1 1 * WQN * +0x5111 CEFB 8 16 3 0 * * CEFF * 1 1 1 * WQM * +0x5112 CDFB 8 16 3 0 * * CDFF * 1 1 1 * WQS * +0x5113 CIFB 8 16 3 0 * * CIFF * 1 1 1 * WQS * +0x5200 CHMW 8 16 3 0 * * CHMW * 1 1 1 * WQL * +0x5201 CEMW 8 16 3 0 * * CEMW * 1 1 1 * WQM * +0x5202 CDMW 8 16 3 0 * * CDMW * 1 1 1 * WQS * +0x5210 CHFW 8 16 3 0 * * CHFW * 1 1 1 * WQN * +0x5211 CEFW 8 16 3 0 * * CEFW * 1 1 1 * WQM * +0x5212 CDFW 8 16 3 0 * * CDFW * 1 1 1 * WQS * +0x5300 CHMT 8 16 3 0 * * CHMT * 1 1 0 * WQL * +0x5301 CEMT 8 16 3 0 * * CEMT * 1 1 0 * WQM * +0x5302 CDMT 8 16 3 0 * * CDMT * 1 1 0 * WQS * +0x5303 CIMT 8 16 3 0 * * CIMT * 1 1 0 * WQS WQH +0x5310 CHFT 8 16 3 0 * * CHFT * 1 1 0 * WQN * +0x5311 CEFT 8 16 3 0 * * CEFT * 1 1 0 * WQM * +0x5312 CDFT 8 16 3 0 * * CDFT * 1 1 0 * WQS * +0x5313 CIFT 8 16 3 0 * * CIFT * 1 1 0 * WQS * +0x6000 CHMB 8 16 3 0 * * CHMC * 1 1 1 * WQL * +0x6001 CEMB 8 16 3 0 * * CEMC * 1 1 1 * WQM * +0x6002 CDMB 8 16 3 0 * * CDMC * 1 1 1 * WQS * +0x6003 CIMB 8 16 3 0 * * CIMC * 1 1 1 * WQS WQH +0x6004 CDMB 8 16 3 0 * * CDMC * 1 1 1 * WQS WQH +0x6005 CHMB 8 16 3 0 * * CHMC * 1 1 1 * WQL * +0x6010 CHFB 8 16 3 0 * * CHFC * 1 1 1 * WQN * +0x6011 CEFB 8 16 3 0 * * CEFC * 1 1 1 * WQM * +0x6012 CDFB 8 16 3 0 * * CDFC * 1 1 1 * WQS * +0x6013 CIFB 8 16 3 0 * * CIFC * 1 1 1 * WQS * +0x6014 CIFB 8 16 3 0 * * CIFC * 1 1 1 * WQS WQH +0x6015 CHFB 8 16 3 0 * * CHFC * 1 1 1 * WQN * +0x6100 CHMB 8 16 3 0 * * CHMF * 1 1 1 * WQL * +0x6101 CEMB 8 16 3 0 * * CEMF * 1 1 1 * WQM * +0x6102 CDMB 8 16 3 0 * * CDMF * 1 1 1 * WQS * +0x6103 CIMB 8 16 3 0 * * CIMF * 1 1 1 * WQS WQH +0x6104 CDMB 8 16 3 0 * * CDMF * 1 1 1 * WQS WQH +0x6105 CHMB 8 16 3 0 * * CHMF * 1 1 1 * WQL * +0x6110 CHFB 8 16 3 0 * * CHFF * 1 1 1 * WQN * +0x6111 CEFB 8 16 3 0 * * CEFF * 1 1 1 * WQM * +0x6112 CDFB 8 16 3 0 * * CDFF * 1 1 1 * WQS * +0x6113 CIFB 8 16 3 0 * * CIFF * 1 1 1 * WQS * +0x6114 CIFB 8 16 3 0 * * CIFF * 1 1 1 * WQS WQH +0x6115 CHFB 8 16 3 0 * * CHFF * 1 1 1 * WQN * +0x6200 CHMW 8 16 3 0 * * CHMW * 1 1 1 * WQL * +0x6201 CEMW 8 16 3 0 * * CEMW * 1 1 1 * WQM * +0x6202 CDMW 8 16 3 0 * * CDMW * 1 1 1 * WQS * +0x6204 CDMW 8 16 3 0 * * CDMW * 1 1 1 * WQS WQH +0x6205 CHMW 8 16 3 0 * * CHMW * 1 1 1 * WQL * +0x6210 CHFW 8 16 3 0 * * CHFW * 1 1 1 * WQN * +0x6211 CEFW 8 16 3 0 * * CEFW * 1 1 1 * WQM * +0x6212 CDFW 8 16 3 0 * * CDFW * 1 1 1 * WQS * +0x6214 CDFW 8 16 3 0 * * CDFW * 1 1 1 * WQS WQH +0x6215 CHFW 8 16 3 0 * * CHFW * 1 1 1 * WQN * +0x6300 CHMT 8 16 3 0 * * CHMT * 1 1 0 * WQL * +0x6301 CEMT 8 16 3 0 * * CEMT * 1 1 0 * WQM * +0x6302 CDMT 8 16 3 0 * * CDMT * 1 1 0 * WQS * +0x6303 CIMT 8 16 3 0 * * CIMT * 1 1 0 * WQS WQH +0x6304 CDMT 8 16 3 0 * * CDMT * 1 1 0 * WQS WQH +0x6305 CHMT 8 16 3 0 * * CHMT * 1 1 0 * WQL * +0x6310 CHFT 8 16 3 0 * * CHFT * 1 1 0 * WQN * +0x6311 CEFT 8 16 3 0 * * CEFT * 1 1 0 * WQM * +0x6312 CDFT 8 16 3 0 * * CDFT * 1 1 0 * WQS * +0x6313 CIFT 8 16 3 0 * * CIFT * 1 1 0 * WQS * +0x6314 CIFT 8 16 3 0 * * CIFT * 1 1 0 * WQS WQH +0x6315 CHFT 8 16 3 0 * * CHFT * 1 1 0 * WQN * +0x6400 UDRZ 9 16 3 0 * * * * 1 * 1 0 WPM * +0x6401 UELM 9 16 3 0 * * * * 0 * 0 0 WPM * +0x6402 CMNK 9 16 3 0 * * * * 1 * 0 * WPM * +0x6403 MSKL 9 16 3 0 * * * * 1 * 1 * WPM * +0x6404 USAR 9 16 3 0 * * * * 0 * 0 0 WPL * +0x6405 MDGU 9 16 3 0 * * * * 1 * 1 * WPM * +0x6406 MDGU 9 24 7 0 * * * * 1 * 1 * WPM * +0x6500 CHMM 8 16 3 0 * * CHMM * 1 1 1 * WQL * +0x6510 CHFM 8 16 3 0 * * CHFM * 1 1 1 * WQN * +0x7000 MOGH 11 16 3 0 * * * * 1 * * * * * +0x7001 MOGN 11 16 3 0 * * * * 1 * * * * * +0x7100 MBAS 11 32 5 0 * * * * 0 * * * * * +0x7101 MBAS 11 32 5 0 MBAS_GR * * * 0 * * * * * +0x7200 MBER 11 24 5 0 MBER_BL * * * 0 * * * * * +0x7201 MBER 11 24 5 0 * * * * 0 * * * * * +0x7202 MBER 11 24 5 0 MBER_CA * * * 0 * * * * * +0x7203 MBER 11 24 5 0 MBER_PO * * * 0 * * * * * +0x7300 MEAE 10 32 5 0 * * * * 0 1 * * * * +0x7301 MEAS 10 16 3 0 * * * * 0 1 * * * * +0x7302 MEAE 10 32 5 0 MEAE_SH * * * 0 1 * * * * +0x7310 MFIE 10 24 3 4 * * * * 0 1 * * * * +0x7311 MFIS 10 16 3 4 * * * * 0 1 * * * * +0x7312 MFIE 10 24 3 4 MFIEG1B MFIEG2B * * 0 1 * * * * +0x7313 MFIS 10 16 3 4 MFISG1B MFISG2B * * 0 1 * * * * +0x7314 MFIE 10 24 3 4 MFIEG31 MFIEG3B * * 0 1 * * * * +0x7320 MAIR 10 24 5 0 * * * 1 0 1 * * * * +0x7321 MAIS 10 16 3 0 * * * 1 0 1 * * * * +0x7400 MDOG 11 16 3 0 MDOG_WI * * * 0 * * * * * +0x7401 MDOG 11 16 3 0 MDOG_WA * * * 0 * * * * * +0x7402 MDOG 11 16 3 0 MDOG_MO * * * 0 * * * * * +0x7500 MDOP 11 16 3 0 * * * * 0 * * * * * +0x7501 MDOP 11 16 3 0 MDOP_GR * * * 0 * * * * * +0x7600 METT 11 16 3 0 * * * * 0 * * * * * +0x7601 MGHL 11 16 3 0 MGHL_MA * * * 0 0 * * * * +0x7700 MGHL 11 16 3 0 * * * * 0 * * * * * +0x7701 MGHL 11 16 3 0 MGHL_RE * * * 0 * * * * * +0x7702 MGHL 11 16 3 0 MGHL_GA * * * 0 * * * * * +0x7703 MSHD 10 16 3 0 * * * 1 0 1 * * * * +0x7800 MGIB 11 16 3 0 * * * * 0 * * * * * +0x7900 MSLI 11 24 4 0 MSLI_GR * * 1 0 * * * * * +0x7901 MSLI 11 24 4 0 MSLI_OL * * 1 0 * * * * * +0x7902 MSLI 11 24 4 0 MSLI_MU * * 1 0 * * * * * +0x7903 MSLI 11 24 4 0 MSLI_OC * * 1 0 * * * * * +0x7904 MSLI 11 24 4 0 * * * 1 0 * * * * * +0x7a00 MSPI 11 24 4 0 MSPI_GI * * * 0 * * * * * +0x7a01 MSPI 11 24 4 0 MSPI_HU * * * 0 * * * * * +0x7a02 MSPI 11 24 4 0 MSPI_PH * * * 0 * * * * * +0x7a03 MSPI 11 24 4 0 MSPI_SW * * * 0 * * * * * +0x7a04 MSPI 11 24 4 0 MSPI_WR * * * 0 * * * * * +0x7b00 MWLF 11 16 3 0 * * * * 0 * * * * * +0x7b01 MWLF 11 16 3 0 MWLF_WO * * * 0 * * * * * +0x7b02 MWLF 11 16 3 0 MWLF_DI * * * 0 * * * * * +0x7b03 MWLF 11 16 3 0 MWLF_WI * * * 0 * * * * * +0x7b04 MWLF 11 16 3 0 MWLF_VA * * * 0 * * * * * +0x7b05 MWLF 11 16 3 0 MWLF_DR * * * 0 * * * * * +0x7b06 MWLS 11 16 3 0 * * * * 0 * * * * * +0x7c00 MXVT 11 16 3 0 * * * * 1 * * * * * +0x7c01 MTAS 11 16 3 0 * * * * 0 * * * * * +0x7d00 MZOM 11 16 3 0 * * * * 1 * * * * * +0x7d01 NSLF 11 16 3 0 * * * * 1 0 * * * * +0x7d02 MCHB 11 12 3 0 * * * * 0 0 * * * * +0x7d03 MCHW 11 12 3 0 * * * * 0 0 * * * * +0x7e00 MWER 11 16 3 0 * * * * 0 * * * * * +0x7e01 MGWE 11 16 3 0 * * * * 0 * * * * * +0x7f00 MTRO 10 16 3 0 * * * * 0 1 * * * * +0x7f01 MMIN 10 16 3 0 * * * * 0 1 * * * * +0x7f02 MBEH 10 32 5 0 * * * * 0 1 * * * * +0x7f03 MIMP 10 16 3 0 * * * * 0 1 * * * * +0x7f04 MIGO 10 32 5 0 * * * * 0 1 * * * * +0x7f05 MDJI 10 16 3 0 * * * * 0 1 * * * * +0x7f06 MDJL 10 16 3 0 * * * * 0 1 * * * * +0x7f07 MGLC 10 24 3 0 * * * * 0 1 * * * * +0x7f08 MOTY 10 24 7 0 * * * * 0 1 * * * * +0x7f09 MSAH 10 24 3 0 * * * * 0 1 * * * * +0x7f0a MGCP 10 24 5 0 * * * * 0 1 * * * * +0x7f0b MGCL 10 24 5 0 * * * * 0 1 * * * * +0x7f0c MKUO 10 24 3 0 * * * * 0 1 * * * * +0x7f0d MLIC 10 16 3 0 * * * * 0 1 * * * * +0x7f0e MDLI 10 16 3 0 * * * * 0 1 * * * * +0x7f0f MTRS 10 12 3 0 * * * * 0 1 * * * * +0x7f10 MRAK 10 16 3 0 * * * * 0 1 * * * * +0x7f11 MUMB 10 24 5 0 * * * * 0 1 * * * * +0x7f12 MVAM 10 16 3 0 * * * * 0 1 * * * * +0x7f13 MSNK 10 24 5 0 * * * * 0 1 * * * * +0x7f14 MGIT 10 16 3 0 * * * * 0 1 * * * * +0x7f15 MBES 10 16 3 0 * * * * 0 1 * * * * +0x7f16 AMOO 10 32 5 0 * * * * 0 1 * * * * +0x7f17 ARAB 10 12 3 0 * * * * 0 1 * * * * +0x7f18 ADER 10 16 3 0 * * * * 0 1 * * * * +0x7f19 MDSW 10 16 3 0 * * * * 0 1 * * * * +0x7f20 AGRO 10 12 3 0 * * * * 0 1 * * * * +0x7f21 APHE 10 12 3 0 * * * * 0 1 * * * * +0x7f22 MVAF 10 16 3 0 * * * * 0 1 * * * * +0x7f23 MSAT 10 32 5 0 * * * * 0 1 * * * * +0x7f24 NPIR 10 16 3 0 * * * * 0 1 * * * * +0x7f27 MDRO 10 16 3 0 * * * * 0 1 * * * * +0x7f28 MKUL 10 32 5 0 * * * * 0 1 * * * * +0x7f29 MFDR 10 16 3 0 * * * * 0 1 * * * * +0x7f2a NSAI 10 16 3 0 * * * * 0 1 * * * * +0x7f2b MMAX 10 16 3 0 * * * * 0 0 * * * * +0x7f2c NSOL 10 16 3 0 * * * * 0 1 * * * * +0x7f2d MWFM 10 16 3 0 * * * * 0 1 * * * * +0x7f2e MRAV 10 32 5 0 * * * * 0 1 * * * * +0x7f2f MSPS 10 16 3 0 * * * * 0 1 * * * * +0x7f30 NBOH 10 16 3 0 * * * * 0 1 * * * * +0x7f31 NELL 10 16 3 0 * * * * 0 1 * * * * +0x7f32 MSLY 10 24 5 0 * * * * 0 1 * * * * +0x7f33 MKUR 10 16 3 0 * * * * 0 0 * * * * +0x7f34 MDOC 10 16 3 0 * * * * 0 0 * * * * +0x7f35 MMIS 10 16 3 0 * * * 1 0 1 * * * * +0x7f36 NSHD 10 16 3 0 * * * * 0 1 * * * * +0x7f37 NIRE 10 16 3 0 * * * * 0 1 * * * * +0x7f38 MEYE 10 16 3 0 * * * * 0 0 * * * * +0x7f39 MMST 10 24 3 1 * * * 0 0 0 * * * * +0x7f3a NIRO 10 16 3 0 * * * * 0 1 * * * * +0x7f3b MSOL 10 16 3 7 * * MSOG 0 0 1 * * * * +0x7f3c MASL 10 16 3 7 * * MASG 0 0 1 * * * * +0x7f3d MMEL 10 24 3 0 * * * * 0 0 * * * * +0x7f3e MFIG 10 32 5 0 * * * * 0 1 * * * * +0x7f3f MFIG 10 32 5 0 MFIGG1B * * * 0 1 * * * * +0x7f40 MGLM 10 16 3 0 * * * * 0 1 * * * * +0x7f41 MDJL 10 16 3 0 MDJL_E1 MDJL_E2 * * 0 1 * * * * +0x7f42 NIRO 10 16 3 0 NIRO_RD * * * 0 1 * * * * +0x7f43 MOTY 10 24 7 0 MOTY_Y1 MOTY_Y2 * * 0 1 * * * * +0x7f44 NELW 10 16 3 0 * * * * 0 0 * * * * +0x7f45 MSAI 10 16 3 0 * * * 1 0 0 * * * * +0x7f46 MBEG 10 32 5 0 * * * * 0 0 * * * * +0x7f47 MFI2 10 32 5 0 * * * * 0 0 * * * * +0x7f48 MSOF 10 16 3 0 * * * 0 0 1 * * * * +0x7f49 MDMF 10 16 3 0 * * * 0 0 1 * * * * +0x7f4a MDAS 10 16 3 7 * * MDAG 0 0 1 * * * * +0x7f4b MDAF 10 16 3 0 * * * 0 0 1 * * * * +0x7f4c MPLN 10 16 3 7 * * MPLG 0 0 1 * * * * +0x7f4d MPLF 10 16 3 0 * * * 0 0 1 * * * * +0x7f4e MDVM 10 16 3 7 * * MDVG 0 0 1 * * * * +0x7f4f MDVF 10 16 3 0 * * * 0 0 1 * * * * +0x7f50 MMST 10 24 3 1 MMST_NI * * 0 0 0 * * * * +0x7f51 MMST 10 24 3 1 MMST_HA * * 0 0 0 * * * * +0x7f52 NSAI 10 16 3 0 NSAI_G1 NSAI_G2 * * 0 1 * * * * +0x8000 MGNL 4 20 3 0 * * * * * * * * S1 HB +0x8100 MHOB 4 16 3 0 * * * * * * * * S1 BW +0x8200 MKOB 4 16 3 0 * * * * * * * * SS BW +0x9000 MOGR 12 24 5 0 * * * * 1 * * * * * +0xa000 MWYV 13 16 3 0 * * * * 0 * * * * * +0xa100 MCAR 13 32 5 0 * * * * 0 * * * * * +0xa200 MWYV 13 16 3 0 MWYV_WS * * * 0 * * * * * +0xb000 ACOW 14 32 5 0 * * * * 0 * * * * * +0xb100 AHRS 14 32 5 0 * * * * 0 * * * * * +0xb200 NBEGL 14 16 3 0 * * * * 1 * * * * * +0xb210 NPROL 14 16 3 0 * * * * 1 * * * * * +0xb300 NBOYL 14 16 3 0 * * * * 1 * * * * * +0xb310 NGRLL 14 16 3 0 * * * * 1 * * * * * +0xb400 NFAML 14 16 3 0 * * * * 1 * * * * * +0xb410 NFAWL 14 16 3 0 * * * * 1 * * * * * +0xb500 NSIML 14 16 3 0 * * * * 1 * * * * * +0xb510 NSIWL 14 16 3 0 * * * * 1 * * * * * +0xb600 NNOML 14 16 3 0 * * * * 1 * * * * * +0xb610 NNOWL 14 16 3 0 * * * * 1 * * * * * +0xb700 NSLVL 14 16 3 0 * * * * 1 * * * * * +0xc000 ABAT 15 12 3 0 * * * * 0 * * * * * +0xc100 ACAT 15 12 3 0 * * * * 0 * * * * * +0xc200 ACHK 15 12 3 0 * * * * 0 * * * * * +0xc300 ARAT 15 12 3 0 * * * * 0 * * * * * +0xc400 ASQU 15 12 3 0 * * * * 0 * * * * * +0xc500 ABAT 15 12 3 0 * * * * 0 * * * * * +0xc600 NBEGH 15 16 3 0 * * * * 1 * * * * * +0xc610 NPROH 15 16 3 0 * * * * 1 * * * * * +0xc700 NBOYH 15 16 3 0 * * * * 1 * * * * * +0xc710 NGRLH 15 16 3 0 * * * * 1 * * * * * +0xc800 NFAMH 15 16 3 0 * * * * 1 * * * * * +0xc810 NFAWH 15 16 3 0 * * * * 1 * * * * * +0xc900 NSIMH 15 16 3 0 * * * * 1 * * * * * +0xc910 NSIWH 15 16 3 0 * * * * 1 * * * * * +0xca00 NNOMH 15 16 3 0 * * * * 1 * * * * * +0xca10 NNOWH 15 16 3 0 * * * * 1 * * * * * +0xcb00 NSLVH 15 16 3 0 * * * * 1 * * * * * +0xd000 AEAGG1 16 0 3 0 * * * * 0 * * * * * +0xd100 AGULG1 16 0 3 0 * * * * 0 * * * * * +0xd200 AVULG1 16 0 3 0 * * * * 0 * * * * * +0xd300 ABIRG1 16 0 3 0 * * * * 0 * * * * * +0xd400 ABIRG1 16 0 3 0 * * * * 0 * * * * * +0xe000 MCYC 17 48 7 0 * * * * * * * * * * +0xe010 METN 17 48 5 0 * * * * * * * * * * +0xe020 MTAN 17 32 7 0 * * * * * * * * * * +0xe040 MHIS 17 16 3 0 * * * * * * * * * * +0xe050 MLER 17 16 3 0 * * * * * * * * * * +0xe060 MLIC 17 16 3 0 * * * * * * * * * * +0xe070 MMIN 17 24 3 0 * * * * * * * * * * +0xe080 MMUM 17 16 3 0 * * * * * * * * * * +0xe090 MTAN 17 24 3 0 * * * * * * * * * * +0xe0a0 MTIC 17 16 3 0 * * * * * * * * * * +0xe0b0 MTRO 17 16 3 0 * * * * * * * * * * +0xe0c0 MTSN 17 24 3 0 * * * * * * * * * * +0xe0d0 MUMB 17 24 5 0 * * * * * * * * * * +0xe0e0 MCOR 17 24 5 0 * * * * * * * * * * +0xe0f0 MGIC 17 32 5 0 * * * * * * * * * * +0xe0f1 MGLA 17 24 5 0 * * * * * * * * * * +0xe0f2 MWAV 17 16 3 0 * * * 1 * * * * * * +0xe200 MBET 17 24 3 0 * * * * * * * * * * +0xe210 MBFI 17 12 3 0 * * * * * * * * * * +0xe220 MBBM 17 20 3 0 * * * * * * * * * * +0xe230 MBRH 17 64 7 0 * * * * * * * * * * +0xe240 MANI 17 24 3 0 * * * * * * * * * * +0xe241 MAN2 17 24 3 0 * * * * * * * * * * +0xe242 MAN3 17 24 3 0 * * * * * * * * * * +0xe243 MARU 17 16 3 0 * * * * * * * * * * +0xe244 MBA1 17 16 3 0 * * * * * * * * * * +0xe245 MBA2 17 16 3 0 * * * * * * * * * * +0xe246 MBA3 17 16 3 0 * * * * * * * * * * +0xe247 MBA4 17 16 3 0 * * * * * * * * * * +0xe248 MBA5 17 16 3 0 * * * * * * * * * * +0xe249 MBA6 17 16 3 0 * * * * * * * * * * +0xe24a MBAI 17 16 3 0 * * * * * * * * * * +0xe24b MELE 17 32 5 0 * * * * * * * * * * +0xe24c MELF 17 32 5 0 * * * * * * * * * * +0xe24d MELW 17 32 5 0 * * * * * * * * * * +0xe24e MGFI 17 40 5 0 * * * * * * * * * * +0xe24f MGFR 17 40 5 0 * * * * * * * * * * +0xe250 MGIR 17 24 3 0 * * * * * * * * * * +0xe251 MGVE 17 32 5 0 * * * * * * * * * * +0xe252 MHAR 17 16 3 0 * * * * * * * * * * +0xe253 MREM 17 48 5 0 * * * * * * * * * * +0xe254 MSCR 17 24 3 0 * * * * * * * * * * +0xe255 MSEE 17 16 3 0 * * * * * * * * * * +0xe256 MBE1 17 32 3 0 * * * * * * * * * * +0xe257 MBE2 17 16 3 0 * * * * * * * * * * +0xe258 MBE3 17 16 3 0 * * * * * * * * * * +0xe259 MSVI 17 16 3 0 * * * * * * * * * * +0xe25a MSV2 17 16 3 0 * * * * * * * * * * +0xe25b MUM2 17 24 5 0 * * * * * * * * * * +0xe25c MTA2 17 24 3 0 * * * * * * * * * * +0xe25d MYET 17 24 3 0 * * * * * * * * * * +0xe25e MWI4 17 16 3 0 * * * * * * * * * * +0xe25f MDRD 17 24 3 0 * * * * * * * * * * +0xe260 MCRD 17 24 3 0 * * * * * * * * * * +0xe261 MSAH 17 24 3 0 * * * * * * * * * * +0xe262 MSAT 17 32 5 0 * * * * * * * * * * +0xe263 MSV3 17 16 3 0 * * * * * * * * * * +0xe264 MSV4 17 16 3 0 * * * * * * * * * * +0xe265 MBOA 17 24 3 0 * * * * * * * * * * +0xe26b MDTR 17 24 5 0 * * * * * * * * * * +0xe26e MFEY 17 24 3 0 * * * * * * * * * * +0xe270 MGO5 17 16 3 0 * * * * * * * * * * +0xe276 MLEM 17 24 3 0 * * * * * * * * * * +0xe283 MDRM 17 24 5 0 * * * * * * * * * * +0xe288 MKG1 17 16 3 0 * * * * * * * * * * +0xe289 MKG2 17 16 3 0 * * * * * * * * * * +0xe28a MKG3 17 16 3 0 * * * * * * * * * * +0xe28b MABW 17 24 3 0 * * * * * * * * * * +0xe28c MWD2 17 24 5 0 * * * * * * * * * * +0xe300 MGHO 17 16 3 0 * * * 1 * * * * * * +0xe310 MGH2 17 16 3 0 * * * * * * * * * * +0xe320 MGH3 17 16 3 0 * * * * * * * * * * +0xe400 MGO1 17 16 3 0 * * * * * * * * * * +0xe410 MGO2 17 16 3 0 * * * * * * * * * * +0xe420 MGO3 17 16 3 0 * * * * * * * * * * +0xe430 MGO4 17 16 3 0 * * * * * * * * * * +0xe500 MLIZ 17 24 5 0 * * * * * * * * * * +0xe510 MLI2 17 24 5 0 * * * * * * * * * * +0xe520 MLI3 17 32 5 0 * * * * * * * * * * +0xe600 MMYC 17 16 3 0 * * * * * * * * * * +0xe610 MMY2 17 16 3 0 * * * * * * * * * * +0xe700 MNO1 17 16 3 0 * * * * * * * * * * +0xe710 MNO2 17 16 3 0 * * * * * * * * * * +0xe720 MNO3 17 24 5 0 * * * * * * * * * * +0xe800 MOR1 17 16 3 0 * * * * * * * * * * +0xe810 MOR2 17 16 3 0 * * * * * * * * * * +0xe820 MOR3 17 16 3 0 * * * * * * * * * * +0xe830 MOR4 17 16 3 0 * * * * * * * * * * +0xe840 MOR5 17 16 3 0 * * * * * * * * * * +0xe900 MSAL 17 16 3 0 * * * * * * * * * * +0xe910 MSA2 17 16 3 0 * * * * * * * * * * +0xea00 MSHR 17 24 3 0 * * * * * * * * * * +0xea10 MSH1 17 16 3 0 * * * 1 * * * * * * +0xea20 MSH2 17 24 3 0 * * * 1 * * * * * * +0xeb00 MSKT 17 16 3 0 * * * * * * * * * * +0xeb10 MSKA 17 16 3 0 * * * * * * * * * * +0xeb20 MSKB 17 24 3 0 * * * * * * * * * * +0xec00 MWIG 17 16 3 0 * * * * * * * * * * +0xec10 MWI2 17 16 3 0 * * * * * * * * * * +0xec20 MWI3 17 16 3 0 * * * * * * * * * * +0xed00 MYU1 17 16 3 0 * * * * * * * * * * +0xed10 MYU2 17 16 3 0 * * * * * * * * * * +0xed20 MYU3 17 16 3 0 * * * * * * * * * * +0xee00 MZO2 17 16 3 0 * * * * * * * * * * +0xee10 MZO3 17 16 3 0 * * * * * * * * * * +0xef10 MWWE 17 24 3 0 * * * * * * * * * * diff --git a/src/org/infinity/resource/cre/decoder/tables/avatars-iwdhow.2da b/src/org/infinity/resource/cre/decoder/tables/avatars-iwdhow.2da new file mode 100644 index 000000000..9d9223d78 --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/tables/avatars-iwdhow.2da @@ -0,0 +1,303 @@ +2DA V1.0 +* + RESREF TYPE ELLIPSE SPACE BLENDING PALETTE PALETTE2 RESREF2 TRANSLUCENT CLOWN SPLIT HELMET WEAPON HEIGHT HEIGHT_SHIELD +0x0000 SPRING 0 0 0 0 * * * * 1 0 * * * * +0x0001 SPFLAMES 0 0 0 7 * * * * 0 0 * * * * +0x0100 SPCHUNKS 0 0 0 0 * * SPSHADOW * 1 1 * * * * +0x0200 SPBLOOD 0 0 0 0 * * * * 0 0 0 * * * +0x0210 SPBLOOD 0 0 0 0 * * * * 0 0 1 * * * +0x0220 SPBLOOD 0 0 0 0 * * * * 0 0 2 * * * +0x0230 SPBLOOD 0 0 0 0 * * * * 0 0 3 * * * +0x0240 SPBLOOD 0 0 0 0 * * * * 0 0 4 * * * +0x0300 SPSMPUFF 0 0 0 0 * * * 1 1 * * * * * +0x0400 SKLH 0 0 0 0 * * SPSHADOW * 0 1 * * * * +0x0410 GLPHWRDH 0 0 0 1 * * SPSHADOW * 0 1 * * * * +0x0500 STNKCLDD 0 0 0 0 * * * * 1 0 1 * * * +0x0510 STNKCLDD 0 0 0 0 * * * * 1 0 0 * * * +0x0600 STNKCLDD 0 0 0 0 * * * * 1 0 1 * * * +0x0700 GREASEH 0 0 0 0 * * * * 0 0 * * * * +0x0710 GREASED 0 0 0 0 * * * * 0 1 * * * * +0x0800 WEBENTH 0 0 0 0 * * * * 1 0 * * * * +0x0810 WEBENTD 0 0 0 0 * * * * 1 0 * * * * +0x0900 STNKCLDD 0 0 0 0 * * * * 1 0 1 * * * +0x1000 MWYV 1 24 5 0 * * * * 0 * * * * * +0x1001 MWYV 1 24 5 0 * * * * 0 * * * * * +0x2000 MSIR 5 16 3 0 * * * * 1 * 1 * * BW +0x2100 UVOL 5 16 3 0 * * * * 0 * 1 * MS * +0x2200 MOGM 5 24 5 0 * * * * 1 * 1 1 S1 * +0x2300 MDKN 5 24 5 0 * * * * 0 * 1 1 * * +0x3000 MAKH 6 24 5 0 * * * * 0 * * * * * +0x4000 SNOMC 7 16 3 0 * * * * 1 * * * * * +0x4002 SNOMM 7 16 3 0 * * * * 1 * * * * * +0x4010 SNOWC 7 16 3 0 * * * * 1 * * * * * +0x4012 SNOWM 7 16 3 0 * * * * 1 * * * * * +0x4100 SSIMC 7 16 3 0 * * * * 1 * * * * * +0x4101 SSIMS 7 16 3 0 * * * * 1 * * * * * +0x4102 SSIMM 7 16 3 0 * * * * 1 * * * * * +0x4110 SSIWC 7 16 3 0 * * * * 1 * * * * * +0x4112 SSIWM 7 16 3 0 * * * * 1 * * * * * +0x4200 SHMCM 7 16 3 0 * * * * 1 * * * * * +0x4300 MSPLG1 7 32 5 0 * * * * 0 * * * * * +0x4400 LHMC 7 20 3 0 * * * * 1 * * * * * +0x4410 LHFC 7 20 3 0 * * * * 1 * * * * * +0x4500 LFAM 7 20 3 0 * * * * 1 * * * * * +0x4600 LDMF 7 20 3 0 * * * * 1 * * * * * +0x4700 LEMF 7 20 3 0 * * * * 1 * * * * * +0x4710 LEFF 7 20 3 0 * * * * 1 * * * * * +0x4800 LIMC 7 20 3 0 * * * * 1 * * * * * +0x5000 CHMB 8 16 3 0 * * CHMC * 1 1 1 * WQL * +0x5001 CEMB 8 16 3 0 * * CEMC * 1 1 1 * WQM * +0x5002 CDMB 8 16 3 0 * * CDMC * 1 1 1 * WQS * +0x5003 CIMB 8 16 3 0 * * CIMC * 1 1 1 * WQS WQH +0x5010 CHFB 8 16 3 0 * * CHFC * 1 1 1 * WQN * +0x5011 CEFB 8 16 3 0 * * CEFC * 1 1 1 * WQM * +0x5012 CDMB 8 16 3 0 * * CDMC * 1 1 1 * WQS * +0x5013 CIFB 8 16 3 0 * * CIFC * 1 1 1 * WQS * +0x5100 CHMB 8 16 3 0 * * CHMF * 1 1 1 * WQL * +0x5101 CEMB 8 16 3 0 * * CEMF * 1 1 1 * WQM * +0x5102 CDMB 8 16 3 0 * * CDMF * 1 1 1 * WQS * +0x5103 CIMB 8 16 3 0 * * CIMF * 1 1 1 * WQS WQH +0x5110 CHFB 8 16 3 0 * * CHFF * 1 1 1 * WQN * +0x5111 CEFB 8 16 3 0 * * CEFF * 1 1 1 * WQM * +0x5112 CDMB 8 16 3 0 * * CDMF * 1 1 1 * WQS * +0x5113 CIFB 8 16 3 0 * * CIFF * 1 1 1 * WQS * +0x5200 CHMW 8 16 3 0 * * CHMW * 1 1 1 * WQL * +0x5201 CEMW 8 16 3 0 * * CEMW * 1 1 1 * WQM * +0x5202 CDMW 8 16 3 0 * * CDMW * 1 1 1 * WQS * +0x5210 CHFW 8 16 3 0 * * CHFW * 1 1 1 * WQN * +0x5211 CEFW 8 16 3 0 * * CEFW * 1 1 1 * WQM * +0x5212 CDMW 8 16 3 0 * * CDMW * 1 1 1 * WQS * +0x5300 CHMT 8 16 3 0 * * CHMT * 1 1 0 * WQL * +0x5301 CEMT 8 16 3 0 * * CEMT * 1 1 0 * WQM * +0x5302 CDMT 8 16 3 0 * * CDMT * 1 1 0 * WQS * +0x5303 CIMT 8 16 3 0 * * CIMT * 1 1 0 * WQS WQH +0x5310 CHFT 8 16 3 0 * * CHFT * 1 1 0 * WQN * +0x5311 CEFT 8 16 3 0 * * CEFT * 1 1 0 * WQM * +0x5312 CDMT 8 16 3 0 * * CDMT * 1 1 0 * WQS * +0x5313 CIFT 8 16 3 0 * * CIFT * 1 1 0 * WQS * +0x6000 CHMB 8 16 3 0 * * CHMC * 1 1 1 * WQL * +0x6001 CEMB 8 16 3 0 * * CEMC * 1 1 1 * WQM * +0x6002 CDMB 8 16 3 0 * * CDMC * 1 1 1 * WQS * +0x6003 CIMB 8 16 3 0 * * CIMC * 1 1 1 * WQS WQH +0x6004 CDMB 8 16 3 0 * * CDMC * 1 1 1 * WQS WQH +0x6010 CHFB 8 16 3 0 * * CHFC * 1 1 1 * WQN * +0x6011 CEFB 8 16 3 0 * * CEFC * 1 1 1 * WQM * +0x6012 CIFB 8 16 3 0 * * CIFB * 1 1 1 * WQS * +0x6013 CIFB 8 16 3 0 * * CIFC * 1 1 1 * WQS * +0x6014 CIFB 8 16 3 0 * * CIFC * 1 1 1 * WQS WQH +0x6100 CHMB 8 16 3 0 * * CHMF * 1 1 1 * WQL * +0x6101 CEMB 8 16 3 0 * * CEMF * 1 1 1 * WQM * +0x6102 CDMB 8 16 3 0 * * CDMF * 1 1 1 * WQS * +0x6103 CIMB 8 16 3 0 * * CIMF * 1 1 1 * WQS WQH +0x6104 CDMB 8 16 3 0 * * CDMF * 1 1 1 * WQS WQH +0x6110 CHFB 8 16 3 0 * * CHFF * 1 1 1 * WQN * +0x6111 CEFB 8 16 3 0 * * CEFF * 1 1 1 * WQM * +0x6112 CIFB 8 16 3 0 * * CIFF * 1 1 1 * WQS * +0x6113 CIFB 8 16 3 0 * * CIFF * 1 1 1 * WQS * +0x6114 CIFB 8 16 3 0 * * CIFF * 1 1 1 * WQS WQH +0x6200 CHMW 8 16 3 0 * * CHMW * 1 1 1 * WQL * +0x6201 CEMW 8 16 3 0 * * CEMW * 1 1 1 * WQM * +0x6202 CDMW 8 16 3 0 * * CDMW * 1 1 1 * WQS * +0x6204 CDMW 8 16 3 0 * * CDMW * 1 1 1 * WQS WQH +0x6210 CHFW 8 16 3 0 * * CHFW * 1 1 1 * WQN * +0x6211 CEFW 8 16 3 0 * * CEFW * 1 1 1 * WQM * +0x6212 CDMW 8 16 3 0 * * CDMW * 1 1 1 * WQS * +0x6214 CDMW 8 16 3 0 * * CDMW * 1 1 1 * WQS WQH +0x6300 CHMT 8 16 3 0 * * CHMT * 1 1 0 * WQL * +0x6301 CEMT 8 16 3 0 * * CEMT * 1 1 0 * WQM * +0x6302 CDMT 8 16 3 0 * * CDMT * 1 1 0 * WQS * +0x6303 CIMT 8 16 3 0 * * CIMT * 1 1 0 * WQS WQH +0x6304 CDMT 8 16 3 0 * * CDMT * 1 1 0 * WQS WQH +0x6310 CHFT 8 16 3 0 * * CHFT * 1 1 0 * WQN * +0x6311 CEFT 8 16 3 0 * * CEFT * 1 1 0 * WQM * +0x6312 CIFT 8 16 3 0 * * CIFF * 1 1 0 * WQS * +0x6313 CIFT 8 16 3 0 * * CIFT * 1 1 0 * WQS * +0x6314 CIFT 8 16 3 0 * * CIFT * 1 1 0 * WQS WQH +0x6400 UDRZ 9 16 3 0 * * * * 1 * 1 0 WPM * +0x6401 CTES 9 16 3 0 * * * * 1 * 0 0 WPM * +0x6402 CMNK 9 16 3 0 * * * * 1 * 0 * WPM * +0x6403 MSKL 9 16 3 0 * * * * 1 * 1 * WPM * +0x6404 USAR 9 16 3 0 * * * * 0 * 0 0 WPL * +0x6405 MDGU 9 16 3 0 * * * * 1 * 1 * WPM * +0x7000 MOGH 11 16 3 0 * * * * 1 * * * * * +0x7001 MOGN 11 16 3 0 * * * * 1 * * * * * +0x7100 MBAS 11 32 5 0 * * * * 0 * * * * * +0x7101 MBAS 11 32 5 0 MBAS_GR * * * 0 * * * * * +0x7200 MBER 11 24 5 0 MBER_BL * * * 0 * * * * * +0x7201 MBER 11 24 5 0 * * * * 0 * * * * * +0x7202 MBER 11 24 5 0 MBER_CA * * * 0 * * * * * +0x7203 MBER 11 24 5 0 MBER_PO * * * 0 * * * * * +0x7300 MEAE 10 32 5 0 * * * * 0 1 * * * * +0x7302 MEAE 10 32 5 0 MEAE_SH * * * 0 1 * * * * +0x7400 MDOG 11 16 3 0 MDOG_WI * * * 0 * * * * * +0x7401 MDOG 11 16 3 0 MDOG_WA * * * 0 * * * * * +0x7402 MDOG 11 16 3 0 MDOG_MO * * * 0 * * * * * +0x7500 MDOP 11 16 3 0 * * * * 0 * * * * * +0x7501 MDOP 11 16 3 0 MDOP_GR * * * 0 * * * * * +0x7600 METT 11 16 3 0 * * * * 0 * * * * * +0x7701 MGHL 11 16 3 0 MGHL_RE * * * 0 * * * * * +0x7702 MGHL 11 16 3 0 MGHL_GA * * * 0 * * * * * +0x7800 MGIB 11 16 3 0 * * * * 0 * * * * * +0x7900 MSLI 11 24 4 0 MSLI_GR * * 1 0 * * * * * +0x7901 MSLI 11 24 4 0 MSLI_OL * * 1 0 * * * * * +0x7902 MSLI 11 24 4 0 MSLI_MU * * 1 0 * * * * * +0x7903 MSLI 11 24 4 0 MSLI_OC * * 1 0 * * * * * +0x7904 MSLI 11 24 4 0 * * * 1 0 * * * * * +0x7a00 MSPI 11 24 4 0 MSPI_GI * * * 0 * * * * * +0x7a01 MSPI 11 24 4 0 MSPI_HU * * * 0 * * * * * +0x7a02 MSPI 11 24 4 0 MSPI_PH * * * 0 * * * * * +0x7a03 MSPI 11 24 4 0 MSPI_SW * * * 0 * * * * * +0x7a04 MSPI 11 24 4 0 MSPI_WR * * * 0 * * * * * +0x7b00 MWLF 11 16 3 0 * * * * 0 * * * * * +0x7b01 MWLF 11 16 3 0 MWLF_WO * * * 0 * * * * * +0x7b02 MWLF 11 16 3 0 MWLF_DI * * * 0 * * * * * +0x7b03 MWLF 11 16 3 0 MWLF_WI * * * 0 * * * * * +0x7b04 MWLF 11 16 3 0 MWLF_VA * * * 0 * * * * * +0x7b05 MWLF 11 16 3 0 MWLF_DR * * * 0 * * * * * +0x7c00 MXVT 11 16 3 0 * * * * 1 * * * * * +0x7c01 MTAS 11 16 3 0 * * * * 0 * * * * * +0x7d00 MZOM 11 16 3 0 * * * * 1 * * * * * +0x7e00 MWER 11 16 3 0 * * * * 0 * * * * * +0x7e01 MGWE 11 16 3 0 * * * * 0 * * * * * +0x7f00 MTRO 10 16 3 0 * * * * 0 1 * * * * +0x7f01 MMIN 10 16 3 0 * * * * 0 1 * * * * +0x7f02 MBEH 10 32 5 0 * * * * 0 1 * * * * +0x7f03 MIMP 10 16 3 0 * * * * 0 1 * * * * +0x7f06 MDJL 10 16 3 0 * * * * 0 1 * * * * +0x7f10 MRAK 10 16 3 0 * * * * 0 1 * * * * +0x8000 MGNL 4 20 3 0 * * * * * * * * S1 HB +0x8100 MHOB 4 16 3 0 * * * * * * * * S1 BW +0x8200 MKOB 4 16 3 0 * * * * * * * * SS BW +0x9000 MOGR 12 24 5 0 * * * * 1 * * * * * +0xa000 MWYV 13 16 3 0 * * * * 0 * * * * * +0xa100 MCAR 13 32 5 0 * * * * 0 * * * * * +0xb000 ACOW 14 32 5 0 * * * * 0 * * * * * +0xb100 AHRS 14 32 5 0 * * * * 0 * * * * * +0xb200 NBEGL 14 16 3 0 * * * * 1 * * * * * +0xb210 NPROL 14 16 3 0 * * * * 1 * * * * * +0xb300 NBOYL 14 16 3 0 * * * * 1 * * * * * +0xb310 NGRLL 14 16 3 0 * * * * 1 * * * * * +0xb400 NFAML 14 16 3 0 * * * * 1 * * * * * +0xb410 NFAWL 14 16 3 0 * * * * 1 * * * * * +0xb500 NSIML 14 16 3 0 * * * * 1 * * * * * +0xb510 NSIWL 14 16 3 0 * * * * 1 * * * * * +0xb600 NNOML 14 16 3 0 * * * * 1 * * * * * +0xb610 NNOWL 14 16 3 0 * * * * 1 * * * * * +0xb700 NSLVL 14 16 3 0 * * * * 1 * * * * * +0xc000 ABAT 15 12 3 0 * * * * 0 * * * * * +0xc100 ACAT 15 12 3 0 * * * * 0 * * * * * +0xc200 ACHK 15 12 3 0 * * * * 0 * * * * * +0xc300 ARAT 15 12 3 0 * * * * 0 * * * * * +0xc400 ASQU 15 12 3 0 * * * * 0 * * * * * +0xc500 ABAT 15 12 3 0 * * * * 0 * * * * * +0xc600 NBEGH 15 16 3 0 * * * * 1 * * * * * +0xc610 NPROH 15 16 3 0 * * * * 1 * * * * * +0xc700 NBOYH 15 16 3 0 * * * * 1 * * * * * +0xc710 NGRLH 15 16 3 0 * * * * 1 * * * * * +0xc800 NFAMH 15 16 3 0 * * * * 1 * * * * * +0xc810 NFAWH 15 16 3 0 * * * * 1 * * * * * +0xc900 NSIMH 15 16 3 0 * * * * 1 * * * * * +0xc910 NSIWH 15 16 3 0 * * * * 1 * * * * * +0xca00 NNOMH 15 16 3 0 * * * * 1 * * * * * +0xca10 NNOWH 15 16 3 0 * * * * 1 * * * * * +0xcb00 NSLVH 15 16 3 0 * * * * 1 * * * * * +0xd000 AEAGG1 16 0 3 0 * * * * 0 * * * * * +0xd100 AGULG1 16 0 3 0 * * * * 0 * * * * * +0xd200 AVULG1 16 0 3 0 * * * * 0 * * * * * +0xd300 ABIRG1 16 0 3 0 * * * * 0 * * * * * +0xd400 ABIRG1 16 0 3 0 * * * * 0 * * * * * +0xe000_000f MBET 17 24 3 0 * * * * * * * * * * +0xe010_000f MBBM 17 20 3 0 * * * * * * * * * * +0xe020_000f MBBR 17 16 3 0 * * * * * * * * * * +0xe030_000f MBFI 17 12 3 0 * * * * * * * * * * +0xe040_000f MBRH 17 64 7 0 * * * * * * * * * * +0xe050_000f MREM 17 48 5 0 * * * * * * * * * * +0xe100_000f MCYC 17 48 7 0 * * * * * * * * * * +0xe110_000f METN 17 48 5 0 * * * * * * * * * * +0xe130_000f MGFR 17 40 5 0 * * * * * * * * * * +0xe140_000f MGVE 17 32 5 0 * * * * * * * * * * +0xe210_000f MELE 17 32 5 0 * * * * * * * * * * +0xe220_000f MELF 17 32 5 0 * * * * * * * * * * +0xe230_000f MELW 17 32 5 0 * * * * * * * * * * +0xe240_000f MHAR 17 16 3 0 * * * * * * * * * * +0xe250_000f MWWE 17 24 3 0 * * * * * * * * * * +0xe300_000f MGH2 17 16 3 0 * * * * * * * * * * +0xe310_000f MGH3 17 16 3 0 * * * * * * * * * * +0xe320_000f MWIG 17 16 3 0 * * * * * * * * * * +0xe330_000f MZO2 17 16 3 0 * * * * * * * * * * +0xe340_000f MWI2 17 16 3 0 * * * * * * * * * * +0xe350_000f MZO3 17 16 3 0 * * * * * * * * * * +0xe360_000f MWI3 17 16 3 0 * * * * * * * * * * +0xe380_000f MMUM 17 16 3 0 * * * * * * * * * * +0xe390_000f MHIS 17 16 3 0 * * * * * * * * * * +0xe3a0_000f MDRD 17 24 3 0 * * * * * * * * * * +0xe3b0_000f MWAV 17 16 3 0 * * * 1 * * * * * * +0xe400_000f MGO1 17 16 3 0 * * * * * * * * * * +0xe410_000f MGO2 17 16 3 0 * * * * * * * * * * +0xe420_000f MGO3 17 16 3 0 * * * * * * * * * * +0xe430_000f MGO4 17 16 3 0 * * * * * * * * * * +0xe440_000f MSVI 17 16 3 0 * * * * * * * * * * +0xe450_000f MSV2 17 16 3 0 * * * * * * * * * * +0xe510_000f MGIR 17 24 3 0 * * * * * * * * * * +0xe520_000f MGIC 17 32 5 0 * * * * * * * * * * +0xe620_000f MSKB 17 24 3 0 * * * * * * * * * * +0xe700_000f MMIN 17 24 3 0 * * * * * * * * * * +0xe710_000f MTRO 17 16 3 0 * * * * * * * * * * +0xe720_000f MTIC 17 16 3 0 * * * * * * * * * * +0xe730_000f MTSN 17 24 3 0 * * * * * * * * * * +0xe750_000f MUMB 17 24 5 0 * * * * * * * * * * +0xe760_000f MYET 17 24 3 0 * * * * * * * * * * +0xe770_000f MBA4 17 16 3 0 * * * * * * * * * * +0xe780_000f MBA5 17 16 3 0 * * * * * * * * * * +0xe790_000f MBA6 17 16 3 0 * * * * * * * * * * +0xe7a0_000f MBAI 17 16 3 0 * * * * * * * * * * +0xe7e0_000f MSCR 17 24 3 0 * * * * * * * * * * +0xe7f0_000f MUM2 17 24 5 0 * * * * * * * * * * +0xe810_000f MOR1 17 16 3 0 * * * * * * * * * * +0xe820_000f MOR2 17 16 3 0 * * * * * * * * * * +0xe830_000f MOR3 17 16 3 0 * * * * * * * * * * +0xe840_000f MOR4 17 16 3 0 * * * * * * * * * * +0xe850_000f MOR5 17 16 3 0 * * * * * * * * * * +0xe860_000f MNO1 17 16 3 0 * * * * * * * * * * +0xe870_000f MNO2 17 16 3 0 * * * * * * * * * * +0xe880_000f MNO3 17 24 5 0 * * * * * * * * * * +0xe890_000f MLI3 17 32 5 0 * * * * * * * * * * +0xe8a0_000f MYU3 17 16 3 0 * * * * * * * * * * +0xe900_000f MSH1 17 16 3 0 * * * 1 * * * * * * +0xe910_000f MSH2 17 24 3 0 * * * 1 * * * * * * +0xe920_000f MGHO 17 16 3 0 * * * 1 * * * * * * +0xea20_000f MCRD 17 24 3 0 * * * * * * * * * * +0xeb00_000f MANI 17 24 3 0 * * * * * * * * * * +0xeb10_000f MAN2 17 24 3 0 * * * * * * * * * * +0xeb20_000f MAN3 17 24 3 0 * * * * * * * * * * +0xeb30_000f MBE1 17 32 3 0 * * * * * * * * * * +0xeb40_000f MBE2 17 16 3 0 * * * * * * * * * * +0xeb51 MSEE 17 16 3 0 * * * * * * * * * * +0xeb52 MFIR 17 16 3 0 * * * * * * * * * * +0xeb60_000f MLIC 17 16 3 0 * * * * * * * * * * +0xeb70_000f MLER 17 16 3 0 * * * * * * * * * * +0xeb80_000f MMAN 17 32 5 0 * * * * * * * * * * +0xeb90_000f MMYC 17 16 3 0 * * * * * * * * * * +0xeba0_000f MMY2 17 16 3 0 * * * * * * * * * * +0xebb0_000f MSHR 17 24 3 0 * * * * * * * * * * +0xebc0_000f MTAN 17 24 3 0 * * * * * * * * * * +0xebd0_000f MSAL 17 16 3 0 * * * * * * * * * * +0xebe0_000f MSA2 17 16 3 0 * * * * * * * * * * +0xebf1_000f MARU 17 16 3 0 * * * * * * * * * * +0xec00_000f MWDR 17 72 7 0 * * * * * * * * * * +0xec10_000f MCHY 17 16 3 0 * * * * * * * * * * +0xed00_000f MCOR 17 24 5 0 * * * * * * * * * * +0xed10_000f MGLA 17 24 5 0 * * * * * * * * * * +0xf000_000f MSKA 17 16 3 0 * * * * * * * * * * +0xf010_000f MSKT 17 16 3 0 * * * * * * * * * * +0xf020_000f MWI4 17 16 3 0 * * * * * * * * * * +0xf100_000f MYU1 17 16 3 0 * * * * * * * * * * +0xf110_000f MYU2 17 16 3 0 * * * * * * * * * * +0xf200_000f MLIZ 17 24 5 0 * * * * * * * * * * +0xf210_000f MLI2 17 24 5 0 * * * * * * * * * * +0xf300_000f MGFI 17 40 5 0 * * * * * * * * * * +0xf400_000f MSAH 17 24 3 0 * * * * * * * * * * +0xf410_000f MSAT 17 32 5 0 * * * * * * * * * * +0xf770_000f MBA1 17 16 3 0 * * * * * * * * * * +0xf780_000f MBA2 17 16 3 0 * * * * * * * * * * +0xf790_000f MBA3 17 16 3 0 * * * * * * * * * * diff --git a/src/org/infinity/resource/cre/decoder/tables/avatars-pst.2da b/src/org/infinity/resource/cre/decoder/tables/avatars-pst.2da new file mode 100644 index 000000000..759be528a --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/tables/avatars-pst.2da @@ -0,0 +1,110 @@ +2DA V1.0 +* + RESREF RESREF2 TYPE ELLIPSE SPACE CLOWN ARMOR BESTIARY +0x0000 cabsb * 18 20 4 * 5 0 +0x0001 cadvb * 18 16 3 * 0 3 +0x0002 dannb * 18 16 3 1 0 4 +0x0003 cbaub * 18 16 3 * * 69 +0x0004 ccamb * 18 22 5 * 2 6 +0x0005 ccolb * 18 16 3 * 0 7 +0x0006 dcthb * 18 16 3 4 1 78 +0x0007 ccorb * 18 20 4 * 5 8 +0x0008 ccrnb * 18 12 3 * 5 9 +0x0009 ccgdb * 18 16 3 * 2 10 +0x000a dctfb * 18 16 3 4 0 85 +0x000b dctmb * 18 16 3 4 0 11 +0x000c cdabb * 18 16 3 * 0 12 +0x000d ddkkb * 18 16 3 1 1 13 +0x000e cdeib * 18 16 3 * 6 14 +0x000f cdhlb * 18 20 4 * 0 15 +0x0010 dhkmb * 18 20 4 1 2 16 +0x0011 clkmb * 18 16 5 * 2 17 +0x0012 cwizb * 18 16 3 * 0 18 +0x0013 ddtfb * 18 16 3 2 0 86 +0x0014 ddtmb * 18 16 3 2 0 19 +0x0015 cebbb * 18 16 3 * 2 20 +0x0016 cffgb * 18 16 3 * 0 21 +0x0017 cfhjb * 18 20 4 * 5 23 +0x0018 cgarb * 18 16 3 * 5 24 +0x0019 dghfb * 18 16 3 4 0 87 +0x001a dghmb * 18 16 3 4 0 25 +0x001b cgstb * 18 16 3 * 3 26 +0x001c cacab * 18 16 3 * 0 27 +0x001d dgytb * 18 16 3 4 1 28 +0x001e cglab * 18 30 5 * 5 29 +0x001f dgodb * 18 16 3 4 1 30 +0x0020 cgdgb * 18 16 3 * 2 31 +0x0021 cgrib * 18 16 3 * 5 32 +0x0022 cgrkb * 18 30 5 * 5 33 +0x0023 chrsb * 18 16 5 * 5 34 +0x0024 cigyb * 18 16 3 * 10 35 +0x0025 cjbrb * 18 16 3 * 5 36 +0x0026 cjftb * 18 16 3 * 5 37 +0x0027 clopb * 18 30 5 * 0 38 +0x0028 clemb * 18 20 3 * 5 39 +0x0029 clthb * 18 16 3 * 0 40 +0x002a dmhtb * 18 16 3 4 0 74 +0x002b cmklb * 18 16 3 * 2 41 +0x002c dmidb * 18 16 3 * 0 75 +0x002d cmdrb * 18 16 3 * 7 42 +0x002e cmrtb * 18 16 3 * 4 43 +0x002f dnoab * 18 16 3 * 8 76 +0x0030 dnocb * 18 16 3 * 8 76 +0x0031 dnodb * 18 16 3 * 8 76 +0x0032 dnofb * 18 16 3 * 8 76 +0x0033 dnohb * 18 16 3 * 8 76 +0x0034 cndmb * 18 16 3 * 7 44 +0x0035 cnprb * 18 16 3 * 5 45 +0x0036 cphdb * 18 16 3 * 0 46 +0x0037 dprob * 18 16 3 4 0 47 +0x0038 cpuzb * 18 16 3 * 0 48 +0x0039 csm1b * 18 16 3 * 9 49 +0x003a carmb * 18 20 4 * 2 52 +0x003b dsptb * 18 16 3 3 0 53 +0x003c dskwb * 18 16 3 3 4 54 +0x003d dhlfb * 18 16 3 4 0 72 +0x003e dhlmb * 18 16 3 4 0 73 +0x003f dhufb * 18 16 3 4 0 80 +0x0040 dhumb * 18 16 3 4 0 81 +0x0041 ctrzb * 18 20 4 * 6 55 +0x0042 ctgtb * 18 16 3 * 5 56 +0x0043 ctreb * 18 16 3 * 5 57 +0x0044 ctrsb * 18 16 3 * 5 58 +0x0045 ctrcb * 18 32 5 * 5 59 +0x0046 cvgyb * 18 16 3 * 5 60 +0x0047 dvhab * 18 16 3 1 2 61 +0x0048 dwreb * 18 16 3 1 0 62 +0x0049 cwrmb * 18 16 3 * 1 63 +0x004a cwurb * 18 16 3 * 5 64 +0x004b dzmfb * 18 16 3 4 5 83 +0x004c dzmmb * 18 16 3 4 5 65 +0x004d cfelb * 18 16 3 * 0 22 +0x004e csm2b * 18 16 3 * 9 50 +0x004f csm3b * 18 22 5 * 9 51 +0x0050 dthkb * 18 22 5 4 5 5 +0x0051 dnosb * 18 16 3 * 8 76 +0x0052 dnozb * 18 16 3 * 5 76 +0x0053 dtffb * 18 16 3 4 0 84 +0x0054 dtlrb * 18 16 3 4 1 77 +0x0055 dtfmb * 18 16 3 4 1 79 +0x0056 dbaub * 18 16 3 4 1 69 +0x0057 charb * 18 16 5 * 2 71 +0x0058 dmidb * 18 16 3 4 0 75 +0x0059 daafb * 18 16 3 4 0 70 +0x005a cgabb * 18 20 4 * 5 1 +0x005b crabb * 18 20 4 * 5 2 +0x005c dtwzb * 18 16 3 4 0 82 +0x005d dnomb * 18 16 3 * 0 19 +0x005e cuhdb * 18 16 3 * 3 * +0x005f dquib * 18 16 3 4 3 * +0x0060 ctrdb * 18 16 5 * 5 58 +0x0061 dnofb * 18 16 3 * 8 76 +0x0062 dvhstdb * 18 16 3 1 2 * +0x0063 dctmb * 18 16 3 4 0 * +0x0064 daafb * 18 16 3 4 0 80 +0x0066 dctfb * 18 16 3 4 0 * +0x022e carmb * 18 16 3 * 2 52 +0x080e cdeib * 18 16 3 * 6 14 +0x2000 GEARS1 GEARS2 18 60 13 * * * +0x3000 IGHEAD IGARM 18 15 13 * * 66 +0xf000 POSMAIN POSSHAD 18 60 13 * * 67 diff --git a/src/org/infinity/resource/cre/decoder/tables/avatars-pstee.2da b/src/org/infinity/resource/cre/decoder/tables/avatars-pstee.2da new file mode 100644 index 000000000..62d43422c --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/tables/avatars-pstee.2da @@ -0,0 +1,105 @@ +2DA V1.0 +* + RESREF RESREF2 TYPE ELLIPSE SPACE CLOWN ARMOR BESTIARY +0x0000 cabsb * 18 20 4 * 5 0 +0x0001 cadvb * 18 16 3 * 0 3 +0x0002 dannb * 18 16 3 1 0 4 +0x0003 cbaub * 18 16 3 * * 69 +0x0004 ccamb * 18 22 5 * 2 6 +0x0005 ccolb * 18 16 3 * 0 7 +0x0006 dcthb * 18 16 3 4 1 78 +0x0007 ccorb * 18 20 4 * 5 8 +0x0008 ccrnb * 18 12 3 * 5 9 +0x0009 ccgdb * 18 16 3 * 2 10 +0x000a dctfb * 18 16 3 4 0 85 +0x000b dctmb * 18 16 3 4 0 11 +0x000c cdabb * 18 16 3 * 0 12 +0x000d ddkkb * 18 16 3 1 1 13 +0x000e cdeib * 18 16 3 * 6 14 +0x000f cdhlb * 18 20 4 * 0 15 +0x0010 dhkmb * 18 20 4 1 2 16 +0x0011 clkmb * 18 16 5 * 2 17 +0x0012 cwizb * 18 16 3 * 0 18 +0x0013 ddtfb * 18 16 3 2 0 86 +0x0014 ddtmb * 18 16 3 2 0 19 +0x0015 cebbb * 18 16 3 * 2 20 +0x0016 cffgb * 18 16 3 * 0 21 +0x0017 cfhjb * 18 20 4 * 5 23 +0x0018 cgarb * 18 16 3 * 5 24 +0x0019 dghfb * 18 16 3 4 0 87 +0x001a dghmb * 18 16 3 4 0 25 +0x001b cgstb * 18 16 3 * 3 26 +0x001c cacab * 18 16 3 * 0 27 +0x001d dgytb * 18 16 3 4 1 28 +0x001e cglab * 18 30 5 * 5 29 +0x001f dgodb * 18 16 3 4 1 30 +0x0020 cgdgb * 18 16 3 * 2 31 +0x0021 cgrib * 18 16 3 * 5 32 +0x0022 cgrkb * 18 30 5 * 5 33 +0x0023 chrsb * 18 16 5 * 5 34 +0x0024 cigyb * 18 16 3 * 10 35 +0x0025 cjbrb * 18 16 3 * 5 36 +0x0026 cjftb * 18 16 3 * 5 37 +0x0027 clopb * 18 30 5 * 0 38 +0x0028 clemb * 18 20 3 * 5 39 +0x0029 clthb * 18 16 3 * 0 40 +0x002a dmhtb * 18 16 3 4 0 74 +0x002b cmklb * 18 16 3 * 2 41 +0x002c dmidb * 18 16 3 * 0 75 +0x002d cmdrb * 18 16 3 * 7 42 +0x002e cmrtb * 18 16 3 * 4 43 +0x002f dnoab * 18 16 3 * 8 76 +0x0030 dnocb * 18 16 3 * 8 76 +0x0031 dnodb * 18 16 3 * 8 76 +0x0032 dnofb * 18 16 3 * 8 76 +0x0033 dnohb * 18 16 3 * 8 76 +0x0034 cndmb * 18 16 3 * 7 44 +0x0035 cnprb * 18 16 3 * 5 45 +0x0036 cphdb * 18 16 3 * 0 46 +0x0037 dprob * 18 16 3 4 0 47 +0x0038 cpuzb * 18 16 3 * 0 48 +0x0039 csm1b * 18 16 3 * 9 49 +0x003a carmb * 18 20 4 * 2 52 +0x003b dsptb * 18 16 3 3 0 53 +0x003c dskwb * 18 16 3 3 4 54 +0x003d dhlfb * 18 16 3 4 0 72 +0x003e dhlmb * 18 16 3 4 0 73 +0x003f dhufb * 18 16 3 4 0 80 +0x0040 dhumb * 18 16 3 4 0 81 +0x0041 ctrzb * 18 20 4 * 6 55 +0x0042 ctgtb * 18 16 3 * 5 56 +0x0043 ctreb * 18 16 3 * 5 57 +0x0044 ctrsb * 18 16 3 * 5 58 +0x0045 ctrcb * 18 32 5 * 5 59 +0x0046 cvgyb * 18 16 3 * 5 60 +0x0047 dvhab * 18 16 3 1 2 61 +0x0048 dwreb * 18 16 3 1 0 62 +0x0049 cwrmb * 18 16 3 * 1 63 +0x004a cwurb * 18 16 3 * 5 64 +0x004b dzmfb * 18 16 3 4 5 83 +0x004c dzmmb * 18 16 3 4 5 65 +0x004d cfelb * 18 16 3 * 0 22 +0x004e csm2b * 18 16 3 * 9 50 +0x004f csm3b * 18 22 5 * 9 51 +0x0050 dthkb * 18 22 5 4 5 5 +0x0051 dnosb * 18 16 3 * 8 76 +0x0052 dnozb * 18 16 3 * 5 76 +0x0053 dtffb * 18 16 3 4 0 84 +0x0054 dtlrb * 18 16 3 4 1 77 +0x0055 dtfmb * 18 16 3 4 1 79 +0x0056 dbaub * 18 16 3 4 1 69 +0x0057 charb * 18 16 5 * 2 71 +0x0058 dmidb * 18 16 3 4 0 75 +0x0059 daafb * 18 16 3 4 0 70 +0x005a cgabb * 18 20 4 * 5 1 +0x005b crabb * 18 20 4 * 5 2 +0x005c dtwzb * 18 16 3 4 0 82 +0x005d dnomb * 18 16 3 * 0 19 +0x005e cuhdb * 18 16 3 * 3 * +0x005f dquib * 18 16 3 4 3 * +0x0060 ctrdb * 18 16 5 * 5 58 +0x0061 dnofb * 18 16 3 * 8 76 +0x0062 dvhstdb * 18 16 3 1 2 * +0x2000 GEARS1 GEARS2 18 60 13 * * * +0xf100 POSMAIN POSSHAD 18 60 13 * * 67 +0xf101 IGHEAD IGARM 18 15 13 * * 66 diff --git a/src/org/infinity/resource/cre/decoder/tables/infinityanimations-v5.ids b/src/org/infinity/resource/cre/decoder/tables/infinityanimations-v5.ids new file mode 100644 index 000000000..13e5ac87c --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/tables/infinityanimations-v5.ids @@ -0,0 +1,8724 @@ +IDS +0x1001 ÆAA EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1002 ÆAB EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1003 ÆAC EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1004 ÆAD EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1005 ÆAE EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1006 ÆAF EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1007 ÆAG EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1008 ÆAH EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1009 ÆAI EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x100a ÆAJ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x100b ÆAK EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x100c ÆAL EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x100d ÆAM EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x100e ÆAN EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x100f ÆAO EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1010 ÆAP EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1011 ÆAQ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1012 ÆAR EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1013 ÆAS EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1014 ÆAT EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1015 ÆAU EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1016 ÆAV EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1017 ÆAW EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1018 ÆAX EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1019 ÆAY EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x101a ÆAZ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x101b ÆBA EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x101c ÆBB EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x101d ÆBC EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x101e ÆBD EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x101f ÆBE EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1020 ÆBF EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1021 ÆBG EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1022 ÆBH EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1023 ÆBI EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1024 ÆBJ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1025 ÆBK EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1026 ÆBL EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1027 ÆBM EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1028 ÆBN EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1029 ÆBO EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x102a ÆBP EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x102b ÆBQ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x102c ÆBR EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x102d ÆBS EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x102e ÆBT EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x102f ÆBU EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1030 ÆBV EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1031 ÆBW EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1032 ÆBX EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1033 ÆBY EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1034 ÆBZ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1035 ÆCA EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1036 ÆCB EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1037 ÆCC EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1038 ÆCD EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1039 ÆCE EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x103a ÆCF EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x103b ÆCG EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x103c ÆCH EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x103d ÆCI EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x103e ÆCJ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x103f ÆCK EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1040 ÆCL EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1041 ÆCM EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1042 ÆCN EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1043 ÆCO EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1044 ÆCP EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1045 ÆCQ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1046 ÆCR EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1047 ÆCS EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1048 ÆCT EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1049 ÆCU EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x104a ÆCV EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x104b ÆCW EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x104c ÆCX EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x104d ÆCY EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x104e ÆCZ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x104f ÆDA EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1050 ÆDB EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1051 ÆDC EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1052 ÆDD EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1053 ÆDE EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1054 ÆDF EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1055 ÆDG EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1056 ÆDH EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1057 ÆDI EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1058 ÆDJ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1059 ÆDK EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x105a ÆDL EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x105b ÆDM EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x105c ÆDN EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x105d ÆDO EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x105e ÆDP EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x105f ÆDQ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1060 ÆDR EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1061 ÆDS EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1062 ÆDT EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1063 ÆDU EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1064 ÆDV EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1065 ÆDW EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1066 ÆDX EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1067 ÆDY EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1068 ÆDZ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1069 ÆEA EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x106a ÆEB EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x106b ÆEC EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x106c ÆED EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x106d ÆEE EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x106e ÆEF EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x106f ÆEG EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1070 ÆEH EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1071 ÆEI EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1072 ÆEJ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1073 ÆEK EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1074 ÆEL EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1075 ÆEM EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1076 ÆEN EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1077 ÆEO EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1078 ÆEP EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1079 ÆEQ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x107a ÆER EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x107b ÆES EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x107c ÆET EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x107d ÆEU EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x107e ÆEV EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x107f ÆEW EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1080 ÆEX EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1081 ÆEY EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1082 ÆEZ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1083 ÆFA EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1084 ÆFB EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1085 ÆFC EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1086 ÆFD EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1087 ÆFE EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1088 ÆFF EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1089 ÆFG EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x108a ÆFH EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x108b ÆFI EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x108c ÆFJ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x108d ÆFK EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x108e ÆFL EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x108f ÆFM EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1090 ÆFN EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1091 ÆFO EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1092 ÆFP EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1093 ÆFQ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1094 ÆFR EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1095 ÆFS EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1096 ÆFT EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1097 ÆFU EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1098 ÆFV EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1099 ÆFW EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x109a ÆFX EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x109b ÆFY EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x109c ÆFZ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x109d ÆGA EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x109e ÆGB EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x109f ÆGC EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10a0 ÆGD EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10a1 ÆGE EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10a2 ÆGF EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10a3 ÆGG EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10a4 ÆGH EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10a5 ÆGI EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10a6 ÆGJ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10a7 ÆGK EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10a8 ÆGL EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10a9 ÆGM EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10aa ÆGN EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10ab ÆGO EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10ac ÆGP EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10ad ÆGQ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10ae ÆGR EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10af ÆGS EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10b0 ÆGT EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10b1 ÆGU EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10b2 ÆGV EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10b3 ÆGW EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10b4 ÆGX EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10b5 ÆGY EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10b6 ÆGZ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10b7 ÆHA EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10b8 ÆHB EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10b9 ÆHC EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10ba ÆHD EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10bb ÆHE EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10bc ÆHF EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10bd ÆHG EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10be ÆHH EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10bf ÆHI EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10c0 ÆHJ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10c1 ÆHK EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10c2 ÆHL EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10c3 ÆHM EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10c4 ÆHN EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10c5 ÆHO EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10c6 ÆHP EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10c7 ÆHQ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10c8 ÆHR EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10c9 ÆHS EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10ca ÆHT EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10cb ÆHU EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10cc ÆHV EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10cd ÆHW EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10ce ÆHX EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10cf ÆHY EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10d0 ÆHZ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10d1 ÆIA EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10d2 ÆIB EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10d3 ÆIC EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10d4 ÆID EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10d5 ÆIE EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10d6 ÆIF EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10d7 ÆIG EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10d8 ÆIH EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10d9 ÆII EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10da ÆIJ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10db ÆIK EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10dc ÆIL EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10dd ÆIM EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10de ÆIN EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10df ÆIO EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10e0 ÆIP EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10e1 ÆIQ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10e2 ÆIR EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10e3 ÆIS EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10e4 ÆIT EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10e5 ÆIU EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10e6 ÆIV EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10e7 ÆIW EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10e8 ÆIX EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10e9 ÆIY EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10ea ÆIZ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10eb ÆJA EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10ec ÆJB EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10ed ÆJC EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10ee ÆJD EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10ef ÆJE EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10f0 ÆJF EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10f1 ÆJG EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10f2 ÆJH EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10f3 ÆJI EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10f4 ÆJJ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10f5 ÆJK EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10f6 ÆJL EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10f7 ÆJM EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10f8 ÆJN EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10f9 ÆJO EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10fa ÆJP EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10fb ÆJQ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10fc ÆJR EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10fd ÆJS EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10fe ÆJT EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10ff ÆJU EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1101 ÆJW EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1102 ÆJX EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1103 ÆJY EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1104 ÆJZ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1105 ÆKA EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1106 ÆKB EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1107 ÆKC EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1108 ÆKD EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1109 ÆKE EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x110a ÆKF EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x110b ÆKG EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x110c ÆKH EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x110d ÆKI EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x110e ÆKJ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x110f ÆKK EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1110 ÆKL EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1111 ÆKM EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1112 ÆKN EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1113 ÆKO EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1114 ÆKP EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1115 ÆKQ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1116 ÆKR EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1117 ÆKS EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1118 ÆKT EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1119 ÆKU EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x111a ÆKV EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x111b ÆKW EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x111c ÆKX EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x111d ÆKY EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x111e ÆKZ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x111f ÆLA EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1120 ÆLB EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1121 ÆLC EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1122 ÆLD EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1123 ÆLE EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1124 ÆLF EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1125 ÆLG EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1126 ÆLH EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1127 ÆLI EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1128 ÆLJ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1129 ÆLK EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x112a ÆLL EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x112b ÆLM EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x112c ÆLN EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x112d ÆLO EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x112e ÆLP EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x112f ÆLQ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1130 ÆLR EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1131 ÆLS EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1132 ÆLT EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1133 ÆLU EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1134 ÆLV EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1135 ÆLW EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1136 ÆLX EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1137 ÆLY EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1138 ÆLZ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1139 ÆMA EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x113a ÆMB EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x113b ÆMC EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x113c ÆMD EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x113d ÆME EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x113e ÆMF EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x113f ÆMG EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1140 ÆMH EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1141 ÆMI EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1142 ÆMJ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1143 ÆMK EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1144 ÆML EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1145 ÆMM EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1146 ÆMN EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1147 ÆMO EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1148 ÆMP EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1149 ÆMQ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x114a ÆMR EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x114b ÆMS EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x114c ÆMT EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x114d ÆMU EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x114e ÆMV EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x114f ÆMW EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1150 ÆMX EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1151 ÆMY EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1152 ÆMZ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1153 ÆNA EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1154 ÆNB EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1155 ÆNC EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1156 ÆND EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1157 ÆNE EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1158 ÆNF EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1159 ÆNG EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x115a ÆNH EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x115b ÆNI EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x115c ÆNJ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x115d ÆNK EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x115e ÆNL EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x115f ÆNM EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1160 ÆNN EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1161 ÆNO EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1162 ÆNP EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1163 ÆNQ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1164 ÆNR EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1165 ÆNS EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1166 ÆNT EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1167 ÆNU EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1168 ÆNV EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1169 ÆNW EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x116a ÆNX EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x116b ÆNY EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x116c ÆNZ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x116d ÆOA EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x116e ÆOB EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x116f ÆOC EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1170 ÆOD EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1171 ÆOE EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1172 ÆOF EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1173 ÆOG EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1174 ÆOH EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1175 ÆOI EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1176 ÆOJ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1177 ÆOK EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1178 ÆOL EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1179 ÆOM EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x117a ÆON EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x117b ÆOO EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x117c ÆOP EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x117d ÆOQ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x117e ÆOR EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x117f ÆOS EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1180 ÆOT EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1181 ÆOU EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1182 ÆOV EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1183 ÆOW EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1184 ÆOX EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1185 ÆOY EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1186 ÆOZ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1187 ÆPA EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1188 ÆPB EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1189 ÆPC EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x118a ÆPD EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x118b ÆPE EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x118c ÆPF EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x118d ÆPG EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x118e ÆPH EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x118f ÆPI EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1190 ÆPJ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1191 ÆPK EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1192 ÆPL EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1193 ÆPM EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1194 ÆPN EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1195 ÆPO EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1196 ÆPP EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1197 ÆPQ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1198 ÆPR EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1199 ÆPS EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x119a ÆPT EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x119b ÆPU EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x119c ÆPV EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x119d ÆPW EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x119e ÆPX EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x119f ÆPY EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11a0 ÆPZ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11a1 ÆQA EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11a2 ÆQB EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11a3 ÆQC EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11a4 ÆQD EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11a5 ÆQE EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11a6 ÆQF EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11a7 ÆQG EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11a8 ÆQH EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11a9 ÆQI EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11aa ÆQJ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11ab ÆQK EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11ac ÆQL EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11ad ÆQM EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11ae ÆQN EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11af ÆQO EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11b0 ÆQP EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11b1 ÆQQ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11b2 ÆQR EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11b3 ÆQS EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11b4 ÆQT EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11b5 ÆQU EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11b6 ÆQV EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11b7 ÆQW EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11b8 ÆQX EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11b9 ÆQY EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11ba ÆQZ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11bb ÆRA EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11bc ÆRB EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11bd ÆRC EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11be ÆRD EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11bf ÆRE EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11c0 ÆRF EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11c1 ÆRG EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11c2 ÆRH EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11c3 ÆRI EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11c4 ÆRJ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11c5 ÆRK EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11c6 ÆRL EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11c7 ÆRM EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11c8 ÆRN EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11c9 ÆRO EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11ca ÆRP EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11cb ÆRQ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11cc ÆRR EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11cd ÆRS EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11ce ÆRT EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11cf ÆRU EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11d0 ÆRV EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11d1 ÆRW EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11d2 ÆRX EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11d3 ÆRY EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11d4 ÆRZ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11d5 ÆSA EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11d6 ÆSB EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11d7 ÆSC EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11d8 ÆSD EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11d9 ÆSE EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11da ÆSF EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11db ÆSG EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11dc ÆSH EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11dd ÆSI EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11de ÆSJ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11df ÆSK EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11e0 ÆSL EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11e1 ÆSM EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11e2 ÆSN EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11e3 ÆSO EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11e4 ÆSP EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11e5 ÆSQ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11e6 ÆSR EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11e7 ÆSS EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11e8 ÆST EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11e9 ÆSU EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11ea ÆSV EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11eb ÆSW EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11ec ÆSX EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11ed ÆSY EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11ee ÆSZ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11ef ÆTA EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11f0 ÆTB EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11f1 ÆTC EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11f2 ÆTD EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11f3 ÆTE EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11f4 ÆTF EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11f5 ÆTG EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11f6 ÆTH EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11f7 ÆTI EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11f8 ÆTJ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11f9 ÆTK EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11fa ÆTL EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11fb ÆTM EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11fc ÆTN EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11fd ÆTO EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11fe ÆTP EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11ff ÆTQ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1209 XDR1 EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x120a XDR2 EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x120b XDR3 EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x120c XDR4 EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x120d XDR5 EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x120e XDR6 EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x120f XDR7 EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1210 XDR8 EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1211 XDR9 EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1212 XDRA EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1213 XDRB EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1214 XDRC EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1215 XDRD EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1216 XDRE EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1217 XDRF EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1218 XDRG EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1219 XDRH EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x121a XDRI EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x121b XDRJ EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x121c XDRK EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x121d XDRL EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x121e XDRM EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x121f XDRN EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1220 XDRO EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1221 XDRP EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1222 XDRQ EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1223 XDRR EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1224 XDRS EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1225 XDRT EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1226 XDRU EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1227 XDRV EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1228 XDRW EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1229 XDRX EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x122a XDRY EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x122b XDRZ EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x122c XDRµ EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x122d XDRÐ EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x122e XDRÞ EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x122f XDRØ EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1230 XDRÑ EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1231 XDRÆ EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1232 XDRŒ EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1233 XDRß EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1234 XDRÇ EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1301 ÞAA EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1302 ÞAB EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1303 ÞAC EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1304 ÞAD EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1305 ÞAE EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1306 ÞAF EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1307 ÞAG EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1308 ÞAH EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1309 ÞAI EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x130a ÞAJ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x130b ÞAK EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x130c ÞAL EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x130d ÞAM EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x130e ÞAN EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x130f ÞAO EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1310 ÞAP EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1311 ÞAQ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1312 ÞAR EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1313 ÞAS EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1314 ÞAT EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1315 ÞAU EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1316 ÞAV EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1317 ÞAW EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1318 ÞAX EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1319 ÞAY EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x131a ÞAZ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x131b ÞBA EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x131c ÞBB EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x131d ÞBC EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x131e ÞBD EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x131f ÞBE EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1320 ÞBF EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1321 ÞBG EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1322 ÞBH EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1323 ÞBI EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1324 ÞBJ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1325 ÞBK EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1326 ÞBL EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1327 ÞBM EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1328 ÞBN EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1329 ÞBO EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x132a ÞBP EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x132b ÞBQ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x132c ÞBR EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x132d ÞBS EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x132e ÞBT EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x132f ÞBU EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1330 ÞBV EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1331 ÞBW EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1332 ÞBX EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1333 ÞBY EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1334 ÞBZ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1335 ÞCA EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1336 ÞCB EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1337 ÞCC EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1338 ÞCD EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1339 ÞCE EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x133a ÞCF EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x133b ÞCG EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x133c ÞCH EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x133d ÞCI EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x133e ÞCJ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x133f ÞCK EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1340 ÞCL EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1341 ÞCM EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1342 ÞCN EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1343 ÞCO EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1344 ÞCP EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1345 ÞCQ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1346 ÞCR EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1347 ÞCS EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1348 ÞCT EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1349 ÞCU EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x134a ÞCV EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x134b ÞCW EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x134c ÞCX EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x134d ÞCY EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x134e ÞCZ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x134f ÞDA EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1350 ÞDB EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1351 ÞDC EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1352 ÞDD EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1353 ÞDE EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1354 ÞDF EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1355 ÞDG EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1356 ÞDH EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1357 ÞDI EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1358 ÞDJ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1359 ÞDK EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x135a ÞDL EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x135b ÞDM EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x135c ÞDN EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x135d ÞDO EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x135e ÞDP EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x135f ÞDQ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1360 ÞDR EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1361 ÞDS EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1362 ÞDT EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1363 ÞDU EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1364 ÞDV EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1365 ÞDW EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1366 ÞDX EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1367 ÞDY EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1368 ÞDZ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1369 ÞEA EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x136a ÞEB EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x136b ÞEC EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x136c ÞED EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x136d ÞEE EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x136e ÞEF EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x136f ÞEG EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1370 ÞEH EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1371 ÞEI EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1372 ÞEJ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1373 ÞEK EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1374 ÞEL EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1375 ÞEM EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1376 ÞEN EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1377 ÞEO EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1378 ÞEP EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1379 ÞEQ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x137a ÞER EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x137b ÞES EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x137c ÞET EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x137d ÞEU EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x137e ÞEV EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x137f ÞEW EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1380 ÞEX EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1381 ÞEY EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1382 ÞEZ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1383 ÞFA EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1384 ÞFB EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1385 ÞFC EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1386 ÞFD EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1387 ÞFE EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1388 ÞFF EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1389 ÞFG EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x138a ÞFH EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x138b ÞFI EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x138c ÞFJ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x138d ÞFK EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x138e ÞFL EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x138f ÞFM EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1390 ÞFN EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1391 ÞFO EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1392 ÞFP EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1393 ÞFQ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1394 ÞFR EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1395 ÞFS EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1396 ÞFT EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1397 ÞFU EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1398 ÞFV EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1399 ÞFW EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x139a ÞFX EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x139b ÞFY EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x139c ÞFZ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x139d ÞGA EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x139e ÞGB EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x139f ÞGC EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13a0 ÞGD EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13a1 ÞGE EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13a2 ÞGF EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13a3 ÞGG EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13a4 ÞGH EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13a5 ÞGI EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13a6 ÞGJ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13a7 ÞGK EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13a8 ÞGL EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13a9 ÞGM EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13aa ÞGN EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13ab ÞGO EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13ac ÞGP EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13ad ÞGQ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13ae ÞGR EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13af ÞGS EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13b0 ÞGT EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13b1 ÞGU EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13b2 ÞGV EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13b3 ÞGW EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13b4 ÞGX EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13b5 ÞGY EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13b6 ÞGZ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13b7 ÞHA EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13b8 ÞHB EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13b9 ÞHC EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13ba ÞHD EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13bb ÞHE EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13bc ÞHF EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13bd ÞHG EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13be ÞHH EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13bf ÞHI EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13c0 ÞHJ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13c1 ÞHK EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13c2 ÞHL EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13c3 ÞHM EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13c4 ÞHN EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13c5 ÞHO EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13c6 ÞHP EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13c7 ÞHQ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13c8 ÞHR EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13c9 ÞHS EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13ca ÞHT EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13cb ÞHU EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13cc ÞHV EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13cd ÞHW EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13ce ÞHX EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13cf ÞHY EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13d0 ÞHZ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13d1 ÞIA EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13d2 ÞIB EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13d3 ÞIC EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13d4 ÞID EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13d5 ÞIE EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13d6 ÞIF EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13d7 ÞIG EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13d8 ÞIH EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13d9 ÞII EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13da ÞIJ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13db ÞIK EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13dc ÞIL EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13dd ÞIM EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13de ÞIN EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13df ÞIO EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13e0 ÞIP EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13e1 ÞIQ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13e2 ÞIR EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13e3 ÞIS EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13e4 ÞIT EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13e5 ÞIU EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13e6 ÞIV EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13e7 ÞIW EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13e8 ÞIX EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13e9 ÞIY EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13ea ÞIZ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13eb ÞJA EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13ec ÞJB EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13ed ÞJC EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13ee ÞJD EMPTY HUGE A Lo BGII SPLIT 4 PART unpaletted +0x13ef ÞJE EMPTY HUGE A Lo BGII SPLIT 4 PART unpaletted +0x13f0 ÞJF EMPTY HUGE A Lo BGII SPLIT 4 PART unpaletted +0x13f1 ÞJG EMPTY HUGE A Lo BGII SPLIT 4 PART unpaletted +0x13f2 ÞJH EMPTY HUGE A Lo BGII SPLIT 4 PART unpaletted +0x13f3 ÞJI EMPTY HUGE A Lo BGII SPLIT 4 PART unpaletted +0x13f4 ÞJJ EMPTY HUGE A Lo BGII SPLIT 4 PART unpaletted +0x13f5 ÞJK EMPTY HUGE A Lo BGII SPLIT 4 PART unpaletted +0x13f6 ÞJL EMPTY HUGE A Lo BGII SPLIT 4 PART unpaletted +0x13f7 ÞJM EMPTY HUGE A Lo BGII SPLIT 4 PART unpaletted +0x13f8 ÞJN EMPTY HUGE A Lo BGII SPLIT 4 PART unpaletted +0x13f9 ÞJO EMPTY HUGE A Lo BGII SPLIT 4 PART unpaletted +0x13fa ÞJP EMPTY HUGE A Lo BGII SPLIT 4 PART unpaletted +0x13fb ÞJQ EMPTY HUGE A Lo BGII SPLIT 4 PART unpaletted +0x13fc ÞJR EMPTY HUGE A Lo BGII SPLIT 4 PART unpaletted +0x13fd ÞJS EMPTY HUGE A Lo BGII SPLIT 4 PART unpaletted +0x13fe ÞJT EMPTY HUGE A Lo BGII SPLIT 4 PART unpaletted +0x13ff ÞJU EMPTY HUGE A Lo BGII SPLIT 4 PART unpaletted +0x2001 ¥AA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2002 ¥AB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2003 ¥AC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2004 ¥AD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2005 ¥AE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2006 ¥AF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2007 ¥AG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2008 ¥AH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2009 ¥AI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x200a ¥AJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x200b ¥AK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x200c ¥AL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x200d ¥AM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x200e ¥AN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x200f ¥AO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2010 ¥AP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2011 ¥AQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2012 ¥AR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2013 ¥AS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2014 ¥AT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2015 ¥AU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2016 ¥AV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2017 ¥AW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2018 ¥AX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2019 ¥AY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x201a ¥AZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x201b ¥BA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x201c ¥BB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x201d ¥BC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x201e ¥BD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x201f ¥BE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2020 ¥BF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2021 ¥BG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2022 ¥BH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2023 ¥BI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2024 ¥BJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2025 ¥BK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2026 ¥BL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2027 ¥BM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2028 ¥BN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2029 ¥BO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x202a ¥BP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x202b ¥BQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x202c ¥BR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x202d ¥BS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x202e ¥BT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x202f ¥BU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2030 ¥BV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2031 ¥BW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2032 ¥BX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2033 ¥BY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2034 ¥BZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2035 ¥CA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2036 ¥CB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2037 ¥CC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2038 ¥CD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2039 ¥CE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x203a ¥CF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x203b ¥CG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x203c ¥CH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x203d ¥CI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x203e ¥CJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x203f ¥CK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2040 ¥CL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2041 ¥CM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2042 ¥CN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2043 ¥CO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2044 ¥CP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2045 ¥CQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2046 ¥CR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2047 ¥CS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2048 ¥CT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2049 ¥CU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x204a ¥CV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x204b ¥CW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x204c ¥CX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x204d ¥CY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x204e ¥CZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x204f ¥DA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2050 ¥DB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2051 ¥DC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2052 ¥DD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2053 ¥DE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2054 ¥DF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2055 ¥DG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2056 ¥DH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2057 ¥DI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2058 ¥DJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2059 ¥DK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x205a ¥DL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x205b ¥DM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x205c ¥DN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x205d ¥DO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x205e ¥DP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x205f ¥DQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2060 ¥DR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2061 ¥DS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2062 ¥DT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2063 ¥DU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2064 ¥DV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2065 ¥DW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2066 ¥DX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2067 ¥DY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2068 ¥DZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2069 ¥EA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x206a ¥EB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x206b ¥EC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x206c ¥ED EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x206d ¥EE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x206e ¥EF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x206f ¥EG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2070 ¥EH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2071 ¥EI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2072 ¥EJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2073 ¥EK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2074 ¥EL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2075 ¥EM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2076 ¥EN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2077 ¥EO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2078 ¥EP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2079 ¥EQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x207a ¥ER EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x207b ¥ES EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x207c ¥ET EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x207d ¥EU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x207e ¥EV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x207f ¥EW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2080 ¥EX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2081 ¥EY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2082 ¥EZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2083 ¥FA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2084 ¥FB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2085 ¥FC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2086 ¥FD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2087 ¥FE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2088 ¥FF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2089 ¥FG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x208a ¥FH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x208b ¥FI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x208c ¥FJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x208d ¥FK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x208e ¥FL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x208f ¥FM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2090 ¥FN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2091 ¥FO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2092 ¥FP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2093 ¥FQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2094 ¥FR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2095 ¥FS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2096 ¥FT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2097 ¥FU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2098 ¥FV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2099 ¥FW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x209a ¥FX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x209b ¥FY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x209c ¥FZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x209d ¥GA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x209e ¥GB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x209f ¥GC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20a0 ¥GD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20a1 ¥GE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20a2 ¥GF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20a3 ¥GG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20a4 ¥GH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20a5 ¥GI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20a6 ¥GJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20a7 ¥GK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20a8 ¥GL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20a9 ¥GM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20aa ¥GN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20ab ¥GO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20ac ¥GP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20ad ¥GQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20ae ¥GR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20af ¥GS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20b0 ¥GT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20b1 ¥GU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20b2 ¥GV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20b3 ¥GW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20b4 ¥GX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20b5 ¥GY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20b6 ¥GZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20b7 ¥HA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20b8 ¥HB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20b9 ¥HC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20ba ¥HD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20bb ¥HE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20bc ¥HF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20bd ¥HG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20be ¥HH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20bf ¥HI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20c0 ¥HJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20c1 ¥HK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20c2 ¥HL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20c3 ¥HM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20c4 ¥HN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20c5 ¥HO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20c6 ¥HP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20c7 ¥HQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20c8 ¥HR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20c9 ¥HS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20ca ¥HT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20cb ¥HU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20cc ¥HV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20cd ¥HW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20ce ¥HX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20cf ¥HY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20d0 ¥HZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20d1 ¥IA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20d2 ¥IB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20d3 ¥IC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20d4 ¥ID EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20d5 ¥IE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20d6 ¥IF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20d7 ¥IG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20d8 ¥IH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20d9 ¥II EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20da ¥IJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20db ¥IK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20dc ¥IL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20dd ¥IM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20de ¥IN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20df ¥IO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20e0 ¥IP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20e1 ¥IQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20e2 ¥IR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20e3 ¥IS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20e4 ¥IT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20e5 ¥IU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20e6 ¥IV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20e7 ¥IW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20e8 ¥IX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20e9 ¥IY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20ea ¥IZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20eb ¥JA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20ec ¥JB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20ed ¥JC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20ee ¥JD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20ef ¥JE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20f0 ¥JF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20f1 ¥JG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20f2 ¥JH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20f3 ¥JI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20f4 ¥JJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20f5 ¥JK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20f6 ¥JL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20f7 ¥JM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20f8 ¥JN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20f9 ¥JO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20fa ¥JP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20fb ¥JQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20fc ¥JR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20fd ¥JS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20fe ¥JT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20ff ¥JU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2101 ¥JW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2102 ¥JX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2103 ¥JY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2104 ¥JZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2105 ¥KA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2106 ¥KB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2107 ¥KC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2108 ¥KD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2109 ¥KE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x210a ¥KF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x210b ¥KG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x210c ¥KH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x210d ¥KI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x210e ¥KJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x210f ¥KK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2110 ¥KL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2111 ¥KM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2112 ¥KN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2113 ¥KO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2114 ¥KP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2115 ¥KQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2116 ¥KR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2117 ¥KS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2118 ¥KT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2119 ¥KU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x211a ¥KV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x211b ¥KW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x211c ¥KX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x211d ¥KY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x211e ¥KZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x211f ¥LA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2120 ¥LB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2121 ¥LC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2122 ¥LD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2123 ¥LE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2124 ¥LF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2125 ¥LG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2126 ¥LH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2127 ¥LI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2128 ¥LJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2129 ¥LK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x212a ¥LL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x212b ¥LM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x212c ¥LN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x212d ¥LO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x212e ¥LP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x212f ¥LQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2130 ¥LR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2131 ¥LS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2132 ¥LT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2133 ¥LU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2134 ¥LV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2135 ¥LW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2136 ¥LX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2137 ¥LY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2138 ¥LZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2139 ¥MA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x213a ¥MB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x213b ¥MC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x213c ¥MD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x213d ¥ME EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x213e ¥MF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x213f ¥MG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2140 ¥MH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2141 ¥MI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2142 ¥MJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2143 ¥MK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2144 ¥ML EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2145 ¥MM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2146 ¥MN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2147 ¥MO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2148 ¥MP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2149 ¥MQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x214a ¥MR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x214b ¥MS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x214c ¥MT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x214d ¥MU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x214e ¥MV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x214f ¥MW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2150 ¥MX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2151 ¥MY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2152 ¥MZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2153 ¥NA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2154 ¥NB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2155 ¥NC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2156 ¥ND EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2157 ¥NE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2158 ¥NF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2159 ¥NG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x215a ¥NH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x215b ¥NI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x215c ¥NJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x215d ¥NK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x215e ¥NL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x215f ¥NM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2160 ¥NN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2161 ¥NO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2162 ¥NP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2163 ¥NQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2164 ¥NR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2165 ¥NS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2166 ¥NT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2167 ¥NU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2168 ¥NV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2169 ¥NW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x216a ¥NX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x216b ¥NY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x216c ¥NZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x216d ¥OA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x216e ¥OB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x216f ¥OC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2170 ¥OD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2171 ¥OE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2172 ¥OF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2173 ¥OG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2174 ¥OH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2175 ¥OI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2176 ¥OJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2177 ¥OK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2178 ¥OL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2179 ¥OM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x217a ¥ON EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x217b ¥OO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x217c ¥OP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x217d ¥OQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x217e ¥OR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x217f ¥OS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2180 ¥OT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2181 ¥OU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2182 ¥OV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2183 ¥OW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2184 ¥OX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2185 ¥OY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2186 ¥OZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2187 ¥PA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2188 ¥PB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2189 ¥PC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x218a ¥PD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x218b ¥PE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x218c ¥PF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x218d ¥PG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x218e ¥PH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x218f ¥PI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2190 ¥PJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2191 ¥PK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2192 ¥PL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2193 ¥PM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2194 ¥PN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2195 ¥PO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2196 ¥PP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2197 ¥PQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2198 ¥PR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2199 ¥PS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x219a ¥PT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x219b ¥PU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x219c ¥PV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x219d ¥PW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x219e ¥PX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x219f ¥PY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21a0 ¥PZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21a1 ¥QA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21a2 ¥QB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21a3 ¥QC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21a4 ¥QD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21a5 ¥QE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21a6 ¥QF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21a7 ¥QG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21a8 ¥QH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21a9 ¥QI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21aa ¥QJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21ab ¥QK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21ac ¥QL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21ad ¥QM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21ae ¥QN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21af ¥QO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21b0 ¥QP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21b1 ¥QQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21b2 ¥QR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21b3 ¥QS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21b4 ¥QT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21b5 ¥QU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21b6 ¥QV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21b7 ¥QW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21b8 ¥QX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21b9 ¥QY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21ba ¥QZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21bb ¥RA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21bc ¥RB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21bd ¥RC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21be ¥RD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21bf ¥RE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21c0 ¥RF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21c1 ¥RG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21c2 ¥RH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21c3 ¥RI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21c4 ¥RJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21c5 ¥RK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21c6 ¥RL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21c7 ¥RM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21c8 ¥RN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21c9 ¥RO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21ca ¥RP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21cb ¥RQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21cc ¥RR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21cd ¥RS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21ce ¥RT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21cf ¥RU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21d0 ¥RV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21d1 ¥RW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21d2 ¥RX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21d3 ¥RY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21d4 ¥RZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21d5 ¥SA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21d6 ¥SB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21d7 ¥SC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21d8 ¥SD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21d9 ¥SE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21da ¥SF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21db ¥SG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21dc ¥SH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21dd ¥SI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21de ¥SJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21df ¥SK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21e0 ¥SL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21e1 ¥SM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21e2 ¥SN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21e3 ¥SO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21e4 ¥SP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21e5 ¥SQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21e6 ¥SR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21e7 ¥SS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21e8 ¥ST EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21e9 ¥SU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21ea ¥SV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21eb ¥SW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21ec ¥SX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21ed ¥SY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21ee ¥SZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21ef ¥TA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21f0 ¥TB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21f1 ¥TC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21f2 ¥TD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21f3 ¥TE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21f4 ¥TF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21f5 ¥TG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21f6 ¥TH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21f7 ¥TI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21f8 ¥TJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21f9 ¥TK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21fa ¥TL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21fb ¥TM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21fc ¥TN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21fd ¥TO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21fe ¥TP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21ff ¥TQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2201 ÐAA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2202 ÐAB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2203 ÐAC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2204 ÐAD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2205 ÐAE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2206 ÐAF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2207 ÐAG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2208 ÐAH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2209 ÐAI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x220a ÐAJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x220b ÐAK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x220c ÐAL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x220d ÐAM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x220e ÐAN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x220f ÐAO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2210 ÐAP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2211 ÐAQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2212 ÐAR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2213 ÐAS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2214 ÐAT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2215 ÐAU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2216 ÐAV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2217 ÐAW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2218 ÐAX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2219 ÐAY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x221a ÐAZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x221b ÐBA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x221c ÐBB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x221d ÐBC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x221e ÐBD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x221f ÐBE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2220 ÐBF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2221 ÐBG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2222 ÐBH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2223 ÐBI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2224 ÐBJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2225 ÐBK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2226 ÐBL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2227 ÐBM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2228 ÐBN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2229 ÐBO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x222a ÐBP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x222b ÐBQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x222c ÐBR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x222d ÐBS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x222e ÐBT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x222f ÐBU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2230 ÐBV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2231 ÐBW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2232 ÐBX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2233 ÐBY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2234 ÐBZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2235 ÐCA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2236 ÐCB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2237 ÐCC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2238 ÐCD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2239 ÐCE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x223a ÐCF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x223b ÐCG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x223c ÐCH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x223d ÐCI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x223e ÐCJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x223f ÐCK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2240 ÐCL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2241 ÐCM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2242 ÐCN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2243 ÐCO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2244 ÐCP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2245 ÐCQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2246 ÐCR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2247 ÐCS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2248 ÐCT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2249 ÐCU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x224a ÐCV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x224b ÐCW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x224c ÐCX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x224d ÐCY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x224e ÐCZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x224f ÐDA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2250 ÐDB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2251 ÐDC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2252 ÐDD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2253 ÐDE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2254 ÐDF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2255 ÐDG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2256 ÐDH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2257 ÐDI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2258 ÐDJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2259 ÐDK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x225a ÐDL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x225b ÐDM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x225c ÐDN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x225d ÐDO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x225e ÐDP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x225f ÐDQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2260 ÐDR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2261 ÐDS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2262 ÐDT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2263 ÐDU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2264 ÐDV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2265 ÐDW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2266 ÐDX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2267 ÐDY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2268 ÐDZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2269 ÐEA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x226a ÐEB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x226b ÐEC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x226c ÐED EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x226d ÐEE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x226e ÐEF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x226f ÐEG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2270 ÐEH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2271 ÐEI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2272 ÐEJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2273 ÐEK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2274 ÐEL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2275 ÐEM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2276 ÐEN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2277 ÐEO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2278 ÐEP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2279 ÐEQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x227a ÐER EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x227b ÐES EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x227c ÐET EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x227d ÐEU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x227e ÐEV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x227f ÐEW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2280 ÐEX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2281 ÐEY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2282 ÐEZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2283 ÐFA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2284 ÐFB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2285 ÐFC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2286 ÐFD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2287 ÐFE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2288 ÐFF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2289 ÐFG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x228a ÐFH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x228b ÐFI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x228c ÐFJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x228d ÐFK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x228e ÐFL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x228f ÐFM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2290 ÐFN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2291 ÐFO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2292 ÐFP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2293 ÐFQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2294 ÐFR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2295 ÐFS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2296 ÐFT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2297 ÐFU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2298 ÐFV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2299 ÐFW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x229a ÐFX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x229b ÐFY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x229c ÐFZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x229d ÐGA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x229e ÐGB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x229f ÐGC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22a0 ÐGD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22a1 ÐGE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22a2 ÐGF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22a3 ÐGG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22a4 ÐGH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22a5 ÐGI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22a6 ÐGJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22a7 ÐGK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22a8 ÐGL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22a9 ÐGM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22aa ÐGN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22ab ÐGO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22ac ÐGP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22ad ÐGQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22ae ÐGR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22af ÐGS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22b0 ÐGT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22b1 ÐGU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22b2 ÐGV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22b3 ÐGW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22b4 ÐGX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22b5 ÐGY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22b6 ÐGZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22b7 ÐHA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22b8 ÐHB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22b9 ÐHC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22ba ÐHD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22bb ÐHE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22bc ÐHF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22bd ÐHG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22be ÐHH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22bf ÐHI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22c0 ÐHJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22c1 ÐHK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22c2 ÐHL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22c3 ÐHM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22c4 ÐHN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22c5 ÐHO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22c6 ÐHP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22c7 ÐHQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22c8 ÐHR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22c9 ÐHS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22ca ÐHT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22cb ÐHU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22cc ÐHV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22cd ÐHW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22ce ÐHX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22cf ÐHY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22d0 ÐHZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22d1 ÐIA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22d2 ÐIB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22d3 ÐIC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22d4 ÐID EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22d5 ÐIE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22d6 ÐIF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22d7 ÐIG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22d8 ÐIH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22d9 ÐII EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22da ÐIJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22db ÐIK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22dc ÐIL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22dd ÐIM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22de ÐIN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22df ÐIO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22e0 ÐIP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22e1 ÐIQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22e2 ÐIR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22e3 ÐIS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22e4 ÐIT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22e5 ÐIU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22e6 ÐIV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22e7 ÐIW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22e8 ÐIX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22e9 ÐIY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22ea ÐIZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22eb ÐJA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22ec ÐJB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22ed ÐJC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22ee ÐJD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22ef ÐJE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22f0 ÐJF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22f1 ÐJG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22f2 ÐJH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22f3 ÐJI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22f4 ÐJJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22f5 ÐJK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22f6 ÐJL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22f7 ÐJM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22f8 ÐJN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22f9 ÐJO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22fa ÐJP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22fb ÐJQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22fc ÐJR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22fd ÐJS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22fe ÐJT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22ff ÐJU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x3001 ÆAA EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3002 ÆAB EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3003 ÆAC EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3004 ÆAD EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3005 ÆAE EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3006 ÆAF EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3007 ÆAG EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3008 ÆAH EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3009 ÆAI EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x300a ÆAJ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x300b ÆAK EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x300c ÆAL EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x300d ÆAM EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x300e ÆAN EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x300f ÆAO EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3010 ÆAP EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3011 ÆAQ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3012 ÆAR EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3013 ÆAS EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3014 ÆAT EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3015 ÆAU EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3016 ÆAV EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3017 ÆAW EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3018 ÆAX EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3019 ÆAY EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x301a ÆAZ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x301b ÆBA EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x301c ÆBB EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x301d ÆBC EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x301e ÆBD EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x301f ÆBE EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3020 ÆBF EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3021 ÆBG EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3022 ÆBH EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3023 ÆBI EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3024 ÆBJ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3025 ÆBK EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3026 ÆBL EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3027 ÆBM EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3028 ÆBN EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3029 ÆBO EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x302a ÆBP EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x302b ÆBQ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x302c ÆBR EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x302d ÆBS EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x302e ÆBT EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x302f ÆBU EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3030 ÆBV EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3031 ÆBW EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3032 ÆBX EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3033 ÆBY EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3034 ÆBZ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3035 ÆCA EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3036 ÆCB EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3037 ÆCC EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3038 ÆCD EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3039 ÆCE EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x303a ÆCF EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x303b ÆCG EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x303c ÆCH EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x303d ÆCI EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x303e ÆCJ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x303f ÆCK EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3040 ÆCL EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3041 ÆCM EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3042 ÆCN EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3043 ÆCO EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3044 ÆCP EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3045 ÆCQ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3046 ÆCR EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3047 ÆCS EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3048 ÆCT EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3049 ÆCU EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x304a ÆCV EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x304b ÆCW EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x304c ÆCX EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x304d ÆCY EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x304e ÆCZ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x304f ÆDA EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3050 ÆDB EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3051 ÆDC EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3052 ÆDD EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3053 ÆDE EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3054 ÆDF EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3055 ÆDG EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3056 ÆDH EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3057 ÆDI EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3058 ÆDJ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3059 ÆDK EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x305a ÆDL EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x305b ÆDM EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x305c ÆDN EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x305d ÆDO EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x305e ÆDP EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x305f ÆDQ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3060 ÆDR EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3061 ÆDS EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3062 ÆDT EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3063 ÆDU EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3064 ÆDV EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3065 ÆDW EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3066 ÆDX EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3067 ÆDY EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3068 ÆDZ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3069 ÆEA EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x306a ÆEB EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x306b ÆEC EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x306c ÆED EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x306d ÆEE EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x306e ÆEF EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x306f ÆEG EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3070 ÆEH EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3071 ÆEI EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3072 ÆEJ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3073 ÆEK EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3074 ÆEL EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3075 ÆEM EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3076 ÆEN EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3077 ÆEO EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3078 ÆEP EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3079 ÆEQ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x307a ÆER EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x307b ÆES EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x307c ÆET EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x307d ÆEU EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x307e ÆEV EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x307f ÆEW EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3080 ÆEX EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3081 ÆEY EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3082 ÆEZ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3083 ÆFA EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3084 ÆFB EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3085 ÆFC EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3086 ÆFD EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3087 ÆFE EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3088 ÆFF EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3089 ÆFG EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x308a ÆFH EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x308b ÆFI EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x308c ÆFJ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x308d ÆFK EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x308e ÆFL EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x308f ÆFM EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3090 ÆFN EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3091 ÆFO EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3092 ÆFP EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3093 ÆFQ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3094 ÆFR EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3095 ÆFS EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3096 ÆFT EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3097 ÆFU EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3098 ÆFV EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3099 ÆFW EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x309a ÆFX EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x309b ÆFY EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x309c ÆFZ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x309d ÆGA EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x309e ÆGB EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x309f ÆGC EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30a0 ÆGD EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30a1 ÆGE EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30a2 ÆGF EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30a3 ÆGG EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30a4 ÆGH EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30a5 ÆGI EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30a6 ÆGJ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30a7 ÆGK EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30a8 ÆGL EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30a9 ÆGM EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30aa ÆGN EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30ab ÆGO EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30ac ÆGP EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30ad ÆGQ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30ae ÆGR EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30af ÆGS EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30b0 ÆGT EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30b1 ÆGU EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30b2 ÆGV EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30b3 ÆGW EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30b4 ÆGX EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30b5 ÆGY EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30b6 ÆGZ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30b7 ÆHA EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30b8 ÆHB EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30b9 ÆHC EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30ba ÆHD EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30bb ÆHE EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30bc ÆHF EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30bd ÆHG EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30be ÆHH EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30bf ÆHI EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30c0 ÆHJ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30c1 ÆHK EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30c2 ÆHL EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30c3 ÆHM EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30c4 ÆHN EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30c5 ÆHO EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30c6 ÆHP EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30c7 ÆHQ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30c8 ÆHR EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30c9 ÆHS EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30ca ÆHT EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30cb ÆHU EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30cc ÆHV EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30cd ÆHW EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30ce ÆHX EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30cf ÆHY EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30d0 ÆHZ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30d1 ÆIA EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30d2 ÆIB EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30d3 ÆIC EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30d4 ÆID EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30d5 ÆIE EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30d6 ÆIF EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30d7 ÆIG EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30d8 ÆIH EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30d9 ÆII EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30da ÆIJ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30db ÆIK EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30dc ÆIL EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30dd ÆIM EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30de ÆIN EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30df ÆIO EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30e0 ÆIP EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30e1 ÆIQ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30e2 ÆIR EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30e3 ÆIS EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30e4 ÆIT EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30e5 ÆIU EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30e6 ÆIV EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30e7 ÆIW EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30e8 ÆIX EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30e9 ÆIY EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30ea ÆIZ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30eb ÆJA EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30ec ÆJB EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30ed ÆJC EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30ee ÆJD EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30ef ÆJE EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30f0 ÆJF EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30f1 ÆJG EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30f2 ÆJH EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30f3 ÆJI EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30f4 ÆJJ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30f5 ÆJK EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30f6 ÆJL EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30f7 ÆJM EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30f8 ÆJN EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30f9 ÆJO EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30fa ÆJP EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30fb ÆJQ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30fc ÆJR EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30fd ÆJS EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30fe ÆJT EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30ff ÆJU EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3100 ÆJV EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3101 ÆJW EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3102 ÆJX EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3103 ÆJY EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3104 ÆJZ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3105 ÆKA EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3106 ÆKB EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3107 ÆKC EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3108 ÆKD EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3109 ÆKE EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x310a ÆKF EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x310b ÆKG EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x310c ÆKH EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x310d ÆKI EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x310e ÆKJ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x310f ÆKK EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3110 ÆKL EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3111 ÆKM EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3112 ÆKN EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3113 ÆKO EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3114 ÆKP EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3115 ÆKQ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3116 ÆKR EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3117 ÆKS EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3118 ÆKT EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3119 ÆKU EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x311a ÆKV EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x311b ÆKW EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x311c ÆKX EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x311d ÆKY EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x311e ÆKZ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x311f ÆLA EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3120 ÆLB EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3121 ÆLC EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3122 ÆLD EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3123 ÆLE EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3124 ÆLF EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3125 ÆLG EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3126 ÆLH EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3127 ÆLI EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3128 ÆLJ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3129 ÆLK EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x312a ÆLL EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x312b ÆLM EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x312c ÆLN EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x312d ÆLO EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x312e ÆLP EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x312f ÆLQ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3130 ÆLR EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3131 ÆLS EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3132 ÆLT EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3133 ÆLU EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3134 ÆLV EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3135 ÆLW EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3136 ÆLX EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3137 ÆLY EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3138 ÆLZ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3139 ÆMA EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x313a ÆMB EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x313b ÆMC EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x313c ÆMD EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x313d ÆME EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x313e ÆMF EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x313f ÆMG EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3140 ÆMH EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3141 ÆMI EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3142 ÆMJ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3143 ÆMK EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3144 ÆML EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3145 ÆMM EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3146 ÆMN EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3147 ÆMO EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3148 ÆMP EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3149 ÆMQ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x314a ÆMR EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x314b ÆMS EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x314c ÆMT EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x314d ÆMU EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x314e ÆMV EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x314f ÆMW EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3150 ÆMX EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3151 ÆMY EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3152 ÆMZ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3153 ÆNA EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3154 ÆNB EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3155 ÆNC EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3156 ÆND EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3157 ÆNE EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3158 ÆNF EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3159 ÆNG EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x315a ÆNH EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x315b ÆNI EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x315c ÆNJ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x315d ÆNK EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x315e ÆNL EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x315f ÆNM EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3160 ÆNN EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3161 ÆNO EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3162 ÆNP EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3163 ÆNQ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3164 ÆNR EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3165 ÆNS EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3166 ÆNT EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3167 ÆNU EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3168 ÆNV EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3169 ÆNW EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x316a ÆNX EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x316b ÆNY EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x316c ÆNZ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x316d ÆOA EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x316e ÆOB EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x316f ÆOC EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3170 ÆOD EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3171 ÆOE EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3172 ÆOF EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3173 ÆOG EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3174 ÆOH EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3175 ÆOI EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3176 ÆOJ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3177 ÆOK EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3178 ÆOL EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3179 ÆOM EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x317a ÆON EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x317b ÆOO EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x317c ÆOP EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x317d ÆOQ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x317e ÆOR EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x317f ÆOS EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3180 ÆOT EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3181 ÆOU EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3182 ÆOV EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3183 ÆOW EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3184 ÆOX EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3185 ÆOY EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3186 ÆOZ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3187 ÆPA EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3188 ÆPB EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3189 ÆPC EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x318a ÆPD EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x318b ÆPE EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x318c ÆPF EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x318d ÆPG EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x318e ÆPH EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x318f ÆPI EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3190 ÆPJ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3191 ÆPK EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3192 ÆPL EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3193 ÆPM EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3194 ÆPN EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3195 ÆPO EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3196 ÆPP EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3197 ÆPQ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3198 ÆPR EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3199 ÆPS EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x319a ÆPT EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x319b ÆPU EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x319c ÆPV EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x319d ÆPW EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x319e ÆPX EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x319f ÆPY EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31a0 ÆPZ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31a1 ÆQA EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31a2 ÆQB EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31a3 ÆQC EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31a4 ÆQD EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31a5 ÆQE EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31a6 ÆQF EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31a7 ÆQG EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31a8 ÆQH EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31a9 ÆQI EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31aa ÆQJ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31ab ÆQK EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31ac ÆQL EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31ad ÆQM EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31ae ÆQN EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31af ÆQO EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31b0 ÆQP EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31b1 ÆQQ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31b2 ÆQR EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31b3 ÆQS EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31b4 ÆQT EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31b5 ÆQU EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31b6 ÆQV EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31b7 ÆQW EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31b8 ÆQX EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31b9 ÆQY EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31ba ÆQZ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31bb ÆRA EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31bc ÆRB EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31bd ÆRC EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31be ÆRD EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31bf ÆRE EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31c0 ÆRF EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31c1 ÆRG EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31c2 ÆRH EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31c3 ÆRI EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31c4 ÆRJ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31c5 ÆRK EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31c6 ÆRL EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31c7 ÆRM EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31c8 ÆRN EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31c9 ÆRO EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31ca ÆRP EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31cb ÆRQ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31cc ÆRR EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31cd ÆRS EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31ce ÆRT EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31cf ÆRU EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31d0 ÆRV EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31d1 ÆRW EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31d2 ÆRX EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31d3 ÆRY EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31d4 ÆRZ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31d5 ÆSA EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31d6 ÆSB EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31d7 ÆSC EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31d8 ÆSD EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31d9 ÆSE EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31da ÆSF EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31db ÆSG EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31dc ÆSH EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31dd ÆSI EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31de ÆSJ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31df ÆSK EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31e0 ÆSL EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31e1 ÆSM EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31e2 ÆSN EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31e3 ÆSO EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31e4 ÆSP EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31e5 ÆSQ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31e6 ÆSR EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31e7 ÆSS EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31e8 ÆST EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31e9 ÆSU EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31ea ÆSV EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31eb ÆSW EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31ec ÆSX EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31ed ÆSY EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31ee ÆSZ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31ef ÆTA EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31f0 ÆTB EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31f1 ÆTC EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31f2 ÆTD EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31f3 ÆTE EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31f4 ÆTF EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31f5 ÆTG EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31f6 ÆTH EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31f7 ÆTI EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31f8 ÆTJ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31f9 ÆTK EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31fa ÆTL EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31fb ÆTM EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31fc ÆTN EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31fd ÆTO EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31fe ÆTP EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31ff ÆTQ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x5000 µAA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5001 µAB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5002 µAC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5003 µAD EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5004 µAE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5005 µAF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5006 µAG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5007 µAH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5008 µAI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5009 µAJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x500a µAK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x500b µAL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x500c µAM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x500d µAN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x500e µAO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x500f µAP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5010 µAQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5011 µAR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5012 µAS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5013 µAT EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5014 µAU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5015 µAV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5016 µAW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5017 µAX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5018 µAY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5019 µAZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x501a µBA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x501b µBB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x501c µBC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x501d µBD EMPTY LARGE 5 Io IWD LARGE unpaletted +0x501e µBE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x501f µBF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5020 µBG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5021 µBH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5022 µBI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5023 µBJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5024 µBK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5025 µBL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5026 µBM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5027 µBN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5028 µBO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5029 µBP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x502a µBQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x502b µBR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x502c µBS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x502d µBT EMPTY LARGE 5 Io IWD LARGE unpaletted +0x502e µBU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x502f µBV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5030 µBW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5031 µBX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5032 µBY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5033 µBZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5034 µCA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5035 µCB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5036 µCC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5037 µCD EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5038 µCE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5039 µCF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x503a µCG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x503b µCH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x503c µCI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x503d µCJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x503e µCK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x503f µCL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5040 µCM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5041 µCN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5042 µCO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5043 µCP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5044 µCQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5045 µCR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5046 µCS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5047 µCT EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5048 µCU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5049 µCV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x504a µCW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x504b µCX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x504c µCY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x504d µCZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x504e µDA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x504f µDB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5050 µDC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5051 µDD EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5052 µDE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5053 µDF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5054 µDG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5055 µDH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5056 µDI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5057 µDJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5058 µDK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5059 µDL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x505a µDM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x505b µDN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x505c µDO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x505d µDP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x505e µDQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x505f µDR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5060 µDS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5061 µDT EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5062 µDU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5063 µDV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5064 µDW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5065 µDX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5066 µDY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5067 µDZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5068 µEA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5069 µEB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x506a µEC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x506b µED EMPTY LARGE 5 Io IWD LARGE unpaletted +0x506c µEE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x506d µEF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x506e µEG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x506f µEH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5070 µEI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5071 µEJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5072 µEK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5073 µEL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5074 µEM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5075 µEN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5076 µEO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5077 µEP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5078 µEQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5079 µER EMPTY LARGE 5 Io IWD LARGE unpaletted +0x507a µES EMPTY LARGE 5 Io IWD LARGE unpaletted +0x507b µET EMPTY LARGE 5 Io IWD LARGE unpaletted +0x507c µEU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x507d µEV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x507e µEW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x507f µEX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5080 µEY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5081 µEZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5082 µFA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5083 µFB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5084 µFC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5085 µFD EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5086 µFE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5087 µFF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5088 µFG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5089 µFH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x508a µFI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x508b µFJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x508c µFK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x508d µFL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x508e µFM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x508f µFN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5090 µFO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5091 µFP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5092 µFQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5093 µFR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5094 µFS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5095 µFT EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5096 µFU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5097 µFV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5098 µFW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5099 µFX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x509a µFY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x509b µFZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x509c µGA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x509d µGB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x509e µGC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x509f µGD EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50a0 µGE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50a1 µGF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50a2 µGG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50a3 µGH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50a4 µGI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50a5 µGJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50a6 µGK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50a7 µGL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50a8 µGM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50a9 µGN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50aa µGO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50ab µGP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50ac µGQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50ad µGR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50ae µGS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50af µGT EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50b0 µGU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50b1 µGV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50b2 µGW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50b3 µGX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50b4 µGY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50b5 µGZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50b6 µHA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50b7 µHB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50b8 µHC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50b9 µHD EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50ba µHE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50bb µHF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50bc µHG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50bd µHH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50be µHI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50bf µHJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50c0 µHK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50c1 µHL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50c2 µHM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50c3 µHN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50c4 µHO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50c5 µHP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50c6 µHQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50c7 µHR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50c8 µHS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50c9 µHT EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50ca µHU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50cb µHV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50cc µHW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50cd µHX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50ce µHY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50cf µHZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50d0 µIA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50d1 µIB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50d2 µIC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50d3 µID EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50d4 µIE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50d5 µIF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50d6 µIG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50d7 µIH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50d8 µII EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50d9 µIJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50da µIK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50db µIL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50dc µIM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50dd µIN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50de µIO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50df µIP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50e0 µIQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50e1 µIR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50e2 µIS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50e3 µIT EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50e4 µIU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50e5 µIV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50e6 µIW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50e7 µIX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50e8 µIY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50e9 µIZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50ea µJA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50eb µJB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50ec µJC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50ed µJD EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50ee µJE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50ef µJF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50f0 µJG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50f1 µJH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50f2 µJI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50f3 µJJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50f4 µJK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50f5 µJL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50f6 µJM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50f7 µJN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50f8 µJO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50f9 µJP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50fa µJQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50fb µJR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50fc µJS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50fd µJT EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50fe µJU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50ff µJV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5100 µJW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5101 µJX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5102 µJY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5103 µJZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5104 µKA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5105 µKB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5106 µKC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5107 µKD EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5108 µKE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5109 µKF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x510a µKG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x510b µKH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x510c µKI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x510d µKJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x510e µKK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x510f µKL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5110 µKM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5111 µKN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5112 µKO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5113 µKP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5114 µKQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5115 µKR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5116 µKS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5117 µKT EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5118 µKU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5119 µKV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x511a µKW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x511b µKX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x511c µKY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x511d µKZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x511e µLA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x511f µLB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5120 µLC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5121 µLD EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5122 µLE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5123 µLF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5124 µLG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5125 µLH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5126 µLI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5127 µLJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5128 µLK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5129 µLL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x512a µLM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x512b µLN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x512c µLO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x512d µLP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x512e µLQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x512f µLR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5130 µLS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5131 µLT EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5132 µLU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5133 µLV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5134 µLW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5135 µLX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5136 µLY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5137 µLZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5138 µMA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5139 µMB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x513a µMC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x513b µMD EMPTY LARGE 5 Io IWD LARGE unpaletted +0x513c µME EMPTY LARGE 5 Io IWD LARGE unpaletted +0x513d µMF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x513e µMG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x513f µMH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5140 µMI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5141 µMJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5142 µMK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5143 µML EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5144 µMM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5145 µMN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5146 µMO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5147 µMP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5148 µMQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5149 µMR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x514a µMS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x514b µMT EMPTY LARGE 5 Io IWD LARGE unpaletted +0x514c µMU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x514d µMV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x514e µMW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x514f µMX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5150 µMY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5151 µMZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5152 µNA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5153 µNB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5154 µNC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5155 µND EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5156 µNE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5157 µNF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5158 µNG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5159 µNH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x515a µNI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x515b µNJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x515c µNK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x515d µNL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x515e µNM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x515f µNN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5160 µNO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5161 µNP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5162 µNQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5163 µNR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5164 µNS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5165 µNT EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5166 µNU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5167 µNV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5168 µNW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5169 µNX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x516a µNY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x516b µNZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x516c µOA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x516d µOB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x516e µOC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x516f µOD EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5170 µOE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5171 µOF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5172 µOG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5173 µOH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5174 µOI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5175 µOJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5176 µOK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5177 µOL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5178 µOM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5179 µON EMPTY LARGE 5 Io IWD LARGE unpaletted +0x517a µOO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x517b µOP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x517c µOQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x517d µOR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x517e µOS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x517f µOT EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5180 µOU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5181 µOV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5182 µOW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5183 µOX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5184 µOY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5185 µOZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5186 µPA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5187 µPB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5188 µPC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5189 µPD EMPTY LARGE 5 Io IWD LARGE unpaletted +0x518a µPE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x518b µPF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x518c µPG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x518d µPH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x518e µPI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x518f µPJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5190 µPK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5191 µPL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5192 µPM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5193 µPN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5194 µPO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5195 µPP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5196 µPQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5197 µPR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5198 µPS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5199 µPT EMPTY LARGE 5 Io IWD LARGE unpaletted +0x519a µPU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x519b µPV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x519c µPW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x519d µPX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x519e µPY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x519f µPZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51a0 µQA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51a1 µQB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51a2 µQC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51a3 µQD EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51a4 µQE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51a5 µQF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51a6 µQG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51a7 µQH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51a8 µQI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51a9 µQJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51aa µQK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51ab µQL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51ac µQM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51ad µQN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51ae µQO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51af µQP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51b0 µQQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51b1 µQR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51b2 µQS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51b3 µQT EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51b4 µQU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51b5 µQV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51b6 µQW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51b7 µQX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51b8 µQY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51b9 µQZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51ba µRA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51bb µRB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51bc µRC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51bd µRD EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51be µRE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51bf µRF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51c0 µRG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51c1 µRH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51c2 µRI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51c3 µRJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51c4 µRK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51c5 µRL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51c6 µRM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51c7 µRN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51c8 µRO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51c9 µRP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51ca µRQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51cb µRR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51cc µRS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51cd µRT EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51ce µRU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51cf µRV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51d0 µRW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51d1 µRX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51d2 µRY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51d3 µRZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51d4 µSA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51d5 µSB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51d6 µSC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51d7 µSD EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51d8 µSE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51d9 µSF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51da µSG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51db µSH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51dc µSI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51dd µSJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51de µSK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51df µSL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51e0 µSM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51e1 µSN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51e2 µSO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51e3 µSP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51e4 µSQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51e5 µSR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51e6 µSS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51e7 µST EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51e8 µSU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51e9 µSV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51ea µSW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51eb µSX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51ec µSY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51ed µSZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51ee µTA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51ef µTB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51f0 µTC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51f1 µTD EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51f2 µTE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51f3 µTF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51f4 µTG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51f5 µTH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51f6 µTI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51f7 µTJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51f8 µTK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51f9 µTL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51fa µTM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51fb µTN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51fc µTO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51fd µTP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51fe µTQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51ff µTR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5200 µTS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5201 µTT EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5202 µTU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5203 µTV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5204 µTW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5205 µTX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5206 µTY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5207 µTZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5208 µUA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5209 µUB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x520a µUC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x520b µUD EMPTY LARGE 5 Io IWD LARGE unpaletted +0x520c µUE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x520d µUF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x520e µUG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x520f µUH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5210 µUI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5211 µUJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5212 µUK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5213 µUL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5214 µUM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5215 µUN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5216 µUO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5217 µUP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5218 µUQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5219 µUR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x521a µUS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x521b µUT EMPTY LARGE 5 Io IWD LARGE unpaletted +0x521c µUU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x521d µUV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x521e µUW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x521f µUX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5220 µUY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5221 µUZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5222 µVA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5223 µVB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5224 µVC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5225 µVD EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5226 µVE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5227 µVF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5228 µVG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5229 µVH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x522a µVI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x522b µVJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x522c µVK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x522d µVL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x522e µVM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x522f µVN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5230 µVO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5231 µVP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5232 µVQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5233 µVR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5234 µVS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5235 µVT EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5236 µVU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5237 µVV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5238 µVW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5239 µVX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x523a µVY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x523b µVZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x523d µAA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x523e µAB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x523f µAC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5240 µAD EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5241 µAE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5242 µAF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5243 µAG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5244 µAH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5245 µAI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5246 µAJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5247 µAK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5248 µAL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5249 µAM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x524a µAN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x524b µAO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x524c µAP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x524d µAQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x524e µAR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x524f µAS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5250 µAT EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5251 µAU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5252 µAV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5253 µAW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5254 µAX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5255 µAY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5256 µAZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5257 µBA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5258 µBB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5259 µBC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x525a µBD EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x525b µBE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x525c µBF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x525d µBG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x525e µBH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x525f µBI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5260 µBJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5261 µBK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5262 µBL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5263 µBM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5264 µBN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5265 µBO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5266 µBP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5267 µBQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5268 µBR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5269 µBS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x526a µBT EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x526b µBU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x526c µBV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x526d µBW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x526e µBX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x526f µBY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5270 µBZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5271 µCA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5272 µCB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5273 µCC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5274 µCD EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5275 µCE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5276 µCF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5277 µCG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5278 µCH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5279 µCI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x527a µCJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x527b µCK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x527c µCL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x527d µCM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x527e µCN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x527f µCO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5280 µCP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5281 µCQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5282 µCR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5283 µCS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5284 µCT EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5285 µCU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5286 µCV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5287 µCW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5288 µCX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5289 µCY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x528a µCZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x528b µDA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x528c µDB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x528d µDC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x528e µDD EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x528f µDE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5290 µDF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5291 µDG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5292 µDH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5293 µDI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5294 µDJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5295 µDK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5296 µDL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5297 µDM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5298 µDN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5299 µDO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x529a µDP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x529b µDQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x529c µDR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x529d µDS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x529e µDT EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x529f µDU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52a0 µDV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52a1 µDW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52a2 µDX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52a3 µDY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52a4 µDZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52a5 µEA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52a6 µEB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52a7 µEC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52a8 µED EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52a9 µEE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52aa µEF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52ab µEG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52ac µEH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52ad µEI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52ae µEJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52af µEK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52b0 µEL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52b1 µEM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52b2 µEN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52b3 µEO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52b4 µEP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52b5 µEQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52b6 µER EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52b7 µES EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52b8 µET EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52b9 µEU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52ba µEV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52bb µEW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52bc µEX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52bd µEY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52be µEZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52bf µFA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52c0 µFB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52c1 µFC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52c2 µFD EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52c3 µFE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52c4 µFF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52c5 µFG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52c6 µFH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52c7 µFI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52c8 µFJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52c9 µFK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52ca µFL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52cb µFM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52cc µFN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52cd µFO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52ce µFP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52cf µFQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52d0 µFR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52d1 µFS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52d2 µFT EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52d3 µFU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52d4 µFV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52d5 µFW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52d6 µFX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52d7 µFY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52d8 µFZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52d9 µGA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52da µGB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52db µGC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52dc µGD EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52dd µGE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52de µGF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52df µGG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52e0 µGH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52e1 µGI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52e2 µGJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52e3 µGK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52e4 µGL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52e5 µGM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52e6 µGN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52e7 µGO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52e8 µGP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52e9 µGQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52ea µGR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52eb µGS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52ec µGT EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52ed µGU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52ee µGV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52ef µGW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52f0 µGX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52f1 µGY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52f2 µGZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52f3 µHA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52f4 µHB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52f5 µHC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52f6 µHD EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52f7 µHE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52f8 µHF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52f9 µHG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52fa µHH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52fb µHI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52fc µHJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52fd µHK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52fe µHL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52ff µHM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5300 µHN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5301 µHO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5302 µHP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5303 µHQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5304 µHR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5305 µHS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5306 µHT EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5307 µHU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5308 µHV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5309 µHW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x530a µHX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x530b µHY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x530c µHZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x530d µIA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x530e µIB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x530f µIC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5310 µID EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5311 µIE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5312 µIF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5313 µIG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5314 µIH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5315 µII EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5316 µIJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5317 µIK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5318 µIL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5319 µIM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x531a µIN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x531b µIO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x531c µIP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x531d µIQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x531e µIR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x531f µIS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5320 µIT EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5321 µIU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5322 µIV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5323 µIW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5324 µIX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5325 µIY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5326 µIZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5327 µJA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5328 µJB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5329 µJC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x532a µJD EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x532b µJE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x532c µJF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x532d µJG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x532e µJH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x532f µJI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5330 µJJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5331 µJK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5332 µJL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5333 µJM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5334 µJN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5335 µJO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5336 µJP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5337 µJQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5338 µJR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5339 µJS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x533a µJT EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x533b µJU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x533c µJV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x533d µJW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x533e µJX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x533f µJY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5340 µJZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5341 µKA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5342 µKB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5343 µKC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5344 µKD EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5345 µKE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5346 µKF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5347 µKG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5348 µKH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5349 µKI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x534a µKJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x534b µKK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x534c µKL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x534d µKM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x534e µKN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x534f µKO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5350 µKP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5351 µKQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5352 µKR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5353 µKS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5354 µKT EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5355 µKU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5356 µKV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5357 µKW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5358 µKX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5359 µKY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x535a µKZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x535b µLA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x535c µLB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x535d µLC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x535e µLD EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x535f µLE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5360 µLF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5361 µLG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5362 µLH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5363 µLI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5364 µLJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5365 µLK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5366 µLL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5367 µLM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5368 µLN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5369 µLO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x536a µLP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x536b µLQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x536c µLR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x536d µLS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x536e µLT EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x536f µLU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5370 µLV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5371 µLW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5372 µLX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5373 µLY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5374 µLZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5375 µMA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5376 µMB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5377 µMC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5378 µMD EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5379 µME EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x537a µMF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x537b µMG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x537c µMH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x537d µMI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x537e µMJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x537f µMK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5380 µML EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5381 µMM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5382 µMN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5383 µMO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5384 µMP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5385 µMQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5386 µMR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5387 µMS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5388 µMT EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5389 µMU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x538a µMV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x538b µMW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x538c µMX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x538d µMY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x538e µMZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x538f µNA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5390 µNB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5391 µNC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5392 µND EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5393 µNE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5394 µNF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5395 µNG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5396 µNH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5397 µNI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5398 µNJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5399 µNK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x539a µNL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x539b µNM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x539c µNN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x539d µNO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x539e µNP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x539f µNQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53a0 µNR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53a1 µNS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53a2 µNT EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53a3 µNU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53a4 µNV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53a5 µNW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53a6 µNX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53a7 µNY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53a8 µNZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53a9 µOA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53aa µOB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53ab µOC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53ac µOD EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53ad µOE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53ae µOF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53af µOG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53b0 µOH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53b1 µOI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53b2 µOJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53b3 µOK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53b4 µOL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53b5 µOM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53b6 µON EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53b7 µOO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53b8 µOP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53b9 µOQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53ba µOR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53bb µOS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53bc µOT EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53bd µOU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53be µOV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53bf µOW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53c0 µOX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53c1 µOY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53c2 µOZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53c3 µPA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53c4 µPB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53c5 µPC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53c6 µPD EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53c7 µPE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53c8 µPF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53c9 µPG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53ca µPH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53cb µPI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53cc µPJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53cd µPK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53ce µPL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53cf µPM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53d0 µPN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53d1 µPO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53d2 µPP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53d3 µPQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53d4 µPR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53d5 µPS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53d6 µPT EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53d7 µPU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53d8 µPV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53d9 µPW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53da µPX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53db µPY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53dc µPZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53dd µQA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53de µQB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53df µQC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53e0 µQD EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53e1 µQE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53e2 µQF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53e3 µQG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53e4 µQH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53e5 µQI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53e6 µQJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53e7 µQK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53e8 µQL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53e9 µQM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53ea µQN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53eb µQO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53ec µQP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53ed µQQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53ee µQR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53ef µQS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53f0 µQT EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53f1 µQU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53f2 µQV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53f3 µQW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53f4 µQX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53f5 µQY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53f6 µQZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53f7 µRA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53f8 µRB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53f9 µRC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53fa µRD EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53fb µRE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53fc µRF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53fd µRG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53fe µRH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53ff µRI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5400 µRJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5401 µRK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5402 µRL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5403 µRM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5404 µRN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5405 µRO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5406 µRP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5407 µRQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5408 µRR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5409 µRS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x540a µRT EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x540b µRU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x540c µRV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x540d µRW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x540e µRX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x540f µRY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5410 µRZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5411 µSA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5412 µSB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5413 µSC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5414 µSD EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5415 µSE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5416 µSF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5417 µSG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5418 µSH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5419 µSI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x541a µSJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x541b µSK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x541c µSL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x541d µSM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x541e µSN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x541f µSO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5420 µSP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5421 µSQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5422 µSR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5423 µSS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5424 µST EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5425 µSU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5426 µSV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5427 µSW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5428 µSX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5429 µSY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x542a µSZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x542b µTA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x542c µTB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x542d µTC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x542e µTD EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x542f µTE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5430 µTF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5431 µTG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5432 µTH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5433 µTI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5434 µTJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5435 µTK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5436 µTL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5437 µTM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5438 µTN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5439 µTO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x543a µTP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x543b µTQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x543c µTR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x543d µTS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x543e µTT EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x543f µTU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5440 µTV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5441 µTW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5442 µTX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5443 µTY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5444 µTZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5445 µUA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5446 µUB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5447 µUC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5448 µUD EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5449 µUE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x544a µUF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x544b µUG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x544c µUH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x544d µUI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x544e µUJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x544f µUK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5450 µUL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5451 µUM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5452 µUN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5453 µUO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5454 µUP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5455 µUQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5456 µUR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5457 µUS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5458 µUT EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5459 µUU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x545a µUV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x545b µUW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x545c µUX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x545d µUY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x545e µUZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x545f µVA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5460 µVB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5461 µVC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5462 µVD EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5463 µVE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5464 µVF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5465 µVG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5466 µVH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5467 µVI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5468 µVJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5469 µVK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x546a µVL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x546b µVM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x546c µVN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x546d µVO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x546e µVP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x546f µVQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5470 µVR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5471 µVS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5472 µVT EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5473 µVU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5474 µVV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5475 µVW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5476 µVX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5477 µVY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5478 µVZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x547a £AA EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x547b £AB EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x547c £AC EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x547d £AD EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x547e £AE EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x547f £AF EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5480 £AG EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5481 £AH EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5482 £AI EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5483 £AJ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5484 £AK EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5485 £AL EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5486 £AM EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5487 £AN EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5488 £AO EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5489 £AP EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x548a £AQ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x548b £AR EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x548c £AS EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x548d £AT EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x548e £AU EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x548f £AV EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5490 £AW EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5491 £AX EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5492 £AY EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5493 £AZ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5494 £BA EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5495 £BB EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5496 £BC EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5497 £BD EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5498 £BE EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5499 £BF EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x549a £BG EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x549b £BH EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x549c £BI EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x549d £BJ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x549e £BK EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x549f £BL EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54a0 £BM EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54a1 £BN EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54a2 £BO EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54a3 £BP EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54a4 £BQ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54a5 £BR EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54a6 £BS EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54a7 £BT EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54a8 £BU EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54a9 £BV EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54aa £BW EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54ab £BX EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54ac £BY EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54ad £BZ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54ae £CA EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54af £CB EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54b0 £CC EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54b1 £CD EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54b2 £CE EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54b3 £CF EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54b4 £CG EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54b5 £CH EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54b6 £CI EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54b7 £CJ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54b8 £CK EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54b9 £CL EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54ba £CM EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54bb £CN EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54bc £CO EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54bd £CP EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54be £CQ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54bf £CR EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54c0 £CS EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54c1 £CT EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54c2 £CU EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54c3 £CV EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54c4 £CW EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54c5 £CX EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54c6 £CY EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54c7 £CZ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54c8 £DA EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54c9 £DB EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54ca £DC EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54cb £DD EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54cc £DE EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54cd £DF EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54ce £DG EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54cf £DH EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54d0 £DI EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54d1 £DJ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54d2 £DK EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54d3 £DL EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54d4 £DM EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54d5 £DN EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54d6 £DO EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54d7 £DP EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54d8 £DQ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54d9 £DR EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54da £DS EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54db £DT EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54dc £DU EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54dd £DV EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54de £DW EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54df £DX EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54e0 £DY EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54e1 £DZ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54e2 £EA EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54e3 £EB EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54e4 £EC EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54e5 £ED EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54e6 £EE EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54e7 £EF EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54e8 £EG EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54e9 £EH EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54ea £EI EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54eb £EJ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54ec £EK EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54ed £EL EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54ee £EM EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54ef £EN EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54f0 £EO EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54f1 £EP EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54f2 £EQ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54f3 £ER EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54f4 £ES EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54f5 £ET EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54f6 £EU EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54f7 £EV EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54f8 £EW EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54f9 £EX EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54fa £EY EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54fb £EZ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54fc £FA EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54fd £FB EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54fe £FC EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54ff £FD EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5500 £FE EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5501 £FF EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5502 £FG EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5503 £FH EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5504 £FI EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5505 £FJ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5506 £FK EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5507 £FL EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5508 £FM EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5509 £FN EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x550a £FO EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x550b £FP EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x550c £FQ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x550d £FR EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x550e £FS EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x550f £FT EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5510 £FU EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5511 £FV EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5512 £FW EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5513 £FX EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5514 £FY EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5515 £FZ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5516 £GA EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5517 £GB EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5518 £GC EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5519 £GD EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x551a £GE EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x551b £GF EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x551c £GG EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x551d £GH EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x551e £GI EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x551f £GJ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5520 £GK EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5521 £GL EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5522 £GM EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5523 £GN EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5524 £GO EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5525 £GP EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5526 £GQ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5527 £GR EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5528 £GS EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5529 £GT EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x552a £GU EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x552b £GV EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x552c £GW EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x552d £GX EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x552e £GY EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x552f £GZ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5530 £HA EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5531 £HB EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5532 £HC EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5533 £HD EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5534 £HE EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5535 £HF EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5536 £HG EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5537 £HH EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5538 £HI EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5539 £HJ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x553a £HK EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x553b £HL EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x553c £HM EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x553d £HN EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x553e £HO EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x553f £HP EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5540 £HQ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5541 £HR EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5542 £HS EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5543 £HT EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5544 £HU EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5545 £HV EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5546 £HW EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5547 £HX EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5548 £HY EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5549 £HZ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x554a £IA EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x554b £IB EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x554c £IC EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x554d £ID EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x554e £IE EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x554f £IF EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5550 £IG EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5551 £IH EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5552 £II EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5553 £IJ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5554 £IK EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5555 £IL EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5556 £IM EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5557 £IN EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5558 £IO EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5559 £IP EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x555a £IQ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x555b £IR EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x555c £IS EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x555d £IT EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x555e £IU EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x555f £IV EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5560 £IW EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5561 £IX EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5562 £IY EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5563 £IZ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5564 £JA EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5565 £JB EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5566 £JC EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5567 £JD EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5568 £JE EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5569 £JF EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x556a £JG EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x556b £JH EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x556c £JI EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x556d £JJ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x556e £JK EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x556f £JL EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5570 £JM EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5571 £JN EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5572 £JO EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5573 £JP EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5574 £JQ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5575 £JR EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5576 £JS EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5577 £JT EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5578 £JU EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5579 £JV EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x557a £JW EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x557b £JX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x557c £JY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x557d £JZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x557e £KA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x557f £KB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5580 £KC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5581 £KD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5582 £KE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5583 £KF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5584 £KG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5585 £KH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5586 £KI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5587 £KJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5588 £KK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5589 £KL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x558a £KM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x558b £KN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x558c £KO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x558d £KP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x558e £KQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x558f £KR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5590 £KS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5591 £KT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5592 £KU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5593 £KV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5594 £KW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5595 £KX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5596 £KY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5597 £KZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5598 £LA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5599 £LB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x559a £LC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x559b £LD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x559c £LE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x559d £LF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x559e £LG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x559f £LH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55a0 £LI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55a1 £LJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55a2 £LK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55a3 £LL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55a4 £LM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55a5 £LN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55a6 £LO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55a7 £LP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55a8 £LQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55a9 £LR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55aa £LS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55ab £LT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55ac £LU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55ad £LV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55ae £LW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55af £LX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55b0 £LY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55b1 £LZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55b2 £MA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55b3 £MB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55b4 £MC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55b5 £MD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55b6 £ME EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55b7 £MF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55b8 £MG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55b9 £MH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55ba £MI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55bb £MJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55bc £MK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55bd £ML EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55be £MM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55bf £MN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55c0 £MO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55c1 £MP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55c2 £MQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55c3 £MR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55c4 £MS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55c5 £MT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55c6 £MU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55c7 £MV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55c8 £MW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55c9 £MX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55ca £MY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55cb £MZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55cc £NA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55cd £NB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55ce £NC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55cf £ND EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55d0 £NE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55d1 £NF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55d2 £NG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55d3 £NH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55d4 £NI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55d5 £NJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55d6 £NK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55d7 £NL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55d8 £NM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55d9 £NN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55da £NO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55db £NP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55dc £NQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55dd £NR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55de £NS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55df £NT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55e0 £NU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55e1 £NV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55e2 £NW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55e3 £NX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55e4 £NY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55e5 £NZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55e6 £OA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55e7 £OB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55e8 £OC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55e9 £OD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55ea £OE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55eb £OF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55ec £OG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55ed £OH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55ee £OI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55ef £OJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55f0 £OK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55f1 £OL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55f2 £OM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55f3 £ON EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55f4 £OO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55f5 £OP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55f6 £OQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55f7 £OR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55f8 £OS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55f9 £OT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55fa £OU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55fb £OV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55fc £OW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55fd £OX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55fe £OY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55ff £OZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5600 £PA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5601 £PB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5602 £PC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5603 £PD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5604 £PE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5605 £PF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5606 £PG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5607 £PH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5608 £PI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5609 £PJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x560a £PK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x560b £PL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x560c £PM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x560d £PN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x560e £PO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x560f £PP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5610 £PQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5611 £PR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5612 £PS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5613 £PT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5614 £PU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5615 £PV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5616 £PW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5617 £PX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5618 £PY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5619 £PZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x561a £QA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x561b £QB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x561c £QC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x561d £QD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x561e £QE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x561f £QF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5620 £QG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5621 £QH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5622 £QI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5623 £QJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5624 £QK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5625 £QL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5626 £QM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5627 £QN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5628 £QO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5629 £QP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x562a £QQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x562b £QR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x562c £QS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x562d £QT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x562e £QU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x562f £QV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5630 £QW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5631 £QX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5632 £QY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5633 £QZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5634 £RA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5635 £RB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5636 £RC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5637 £RD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5638 £RE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5639 £RF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x563a £RG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x563b £RH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x563c £RI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x563d £RJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x563e £RK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x563f £RL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5640 £RM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5641 £RN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5642 £RO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5643 £RP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5644 £RQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5645 £RR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5646 £RS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5647 £RT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5648 £RU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5649 £RV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x564a £RW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x564b £RX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x564c £RY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x564d £RZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x564e £SA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x564f £SB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5650 £SC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5651 £SD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5652 £SE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5653 £SF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5654 £SG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5655 £SH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5656 £SI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5657 £SJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5658 £SK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5659 £SL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x565a £SM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x565b £SN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x565c £SO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x565d £SP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x565e £SQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x565f £SR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5660 £SS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5661 £ST EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5662 £SU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5663 £SV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5664 £SW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5665 £SX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5666 £SY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5667 £SZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5668 £TA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5669 £TB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x566a £TC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x566b £TD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x566c £TE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x566d £TF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x566e £TG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x566f £TH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5670 £TI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5671 £TJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5672 £TK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5673 £TL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5674 £TM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5675 £TN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5676 £TO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5677 £TP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5678 £TQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5679 £TR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x567a £TS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x567b £TT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x567c £TU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x567d £TV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x567e £TW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x567f £TX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5680 £TY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5681 £TZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5682 £UA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5683 £UB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5684 £UC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5685 £UD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5686 £UE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5687 £UF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5688 £UG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5689 £UH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x568a £UI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x568b £UJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x568c £UK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x568d £UL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x568e £UM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x568f £UN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5690 £UO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5691 £UP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5692 £UQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5693 £UR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5694 £US EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5695 £UT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5696 £UU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5697 £UV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5698 £UW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5699 £UX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x569a £UY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x569b £UZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x569c £VA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x569d £VB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x569e £VC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x569f £VD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56a0 £VE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56a1 £VF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56a2 £VG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56a3 £VH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56a4 £VI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56a5 £VJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56a6 £VK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56a7 £VL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56a8 £VM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56a9 £VN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56aa £VO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56ab £VP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56ac £VQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56ad £VR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56ae £VS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56af £VT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56b0 £VU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56b1 £VV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56b2 £VW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56b3 £VX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56b4 £VY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56b5 £VZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56b6 £WA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56b7 £AA EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56b8 £AB EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56b9 £AC EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56ba £AD EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56bb £AE EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56bc £AF EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56bd £AG EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56be £AH EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56bf £AI EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56c0 £AJ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56c1 £AK EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56c2 £AL EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56c3 £AM EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56c4 £AN EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56c5 £AO EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56c6 £AP EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56c7 £AQ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56c8 £AR EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56c9 £AS EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56ca £AT EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56cb £AU EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56cc £AV EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56cd £AW EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56ce £AX EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56cf £AY EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56d0 £AZ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56d1 £BA EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56d2 £BB EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56d3 £BC EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56d4 £BD EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56d5 £BE EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56d6 £BF EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56d7 £BG EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56d8 £BH EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56d9 £BI EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56da £BJ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56db £BK EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56dc £BL EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56dd £BM EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56de £BN EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56df £BO EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56e0 £BP EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56e1 £BQ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56e2 £BR EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56e3 £BS EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56e4 £BT EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56e5 £BU EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56e6 £BV EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56e7 £BW EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56e8 £BX EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56e9 £BY EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56ea £BZ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56eb £CA EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56ec £CB EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56ed £CC EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56ee £CD EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56ef £CE EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56f0 £CF EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56f1 £CG EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56f2 £CH EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56f3 £CI EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56f4 £CJ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56f5 £CK EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56f6 £CL EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56f7 £CM EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56f8 £CN EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56f9 £CO EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56fa £CP EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56fb £CQ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56fc £CR EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56fd £CS EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56fe £CT EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56ff £CU EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5700 £CV EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5701 £CW EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5702 £CX EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5703 £CY EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5704 £CZ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5705 £DA EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5706 £DB EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5707 £DC EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5708 £DD EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5709 £DE EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x570a £DF EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x570b £DG EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x570c £DH EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x570d £DI EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x570e £DJ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x570f £DK EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5710 £DL EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5711 £DM EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5712 £DN EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5713 £DO EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5714 £DP EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5715 £DQ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5716 £DR EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5717 £DS EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5718 £DT EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5719 £DU EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x571a £DV EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x571b £DW EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x571c £DX EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x571d £DY EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x571e £DZ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x571f £EA EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5720 £EB EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5721 £EC EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5722 £ED EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5723 £EE EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5724 £EF EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5725 £EG EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5726 £EH EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5727 £EI EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5728 £EJ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5729 £EK EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x572a £EL EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x572b £EM EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x572c £EN EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x572d £EO EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x572e £EP EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x572f £EQ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5730 £ER EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5731 £ES EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5732 £ET EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5733 £EU EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5734 £EV EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5735 £EW EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5736 £EX EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5737 £EY EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5738 £EZ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5739 £FA EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x573a £FB EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x573b £FC EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x573c £FD EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x573d £FE EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x573e £FF EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x573f £FG EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5740 £FH EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5741 £FI EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5742 £FJ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5743 £FK EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5744 £FL EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5745 £FM EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5746 £FN EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5747 £FO EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5748 £FP EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5749 £FQ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x574a £FR EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x574b £FS EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x574c £FT EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x574d £FU EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x574e £FV EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x574f £FW EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5750 £FX EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5751 £FY EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5752 £FZ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5753 £GA EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5754 £GB EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5755 £GC EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5756 £GD EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5757 £GE EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5758 £GF EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5759 £GG EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x575a £GH EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x575b £GI EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x575c £GJ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x575d £GK EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x575e £GL EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x575f £GM EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5760 £GN EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5761 £GO EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5762 £GP EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5763 £GQ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5764 £GR EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5765 £GS EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5766 £GT EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5767 £GU EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5768 £GV EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5769 £GW EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x576a £GX EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x576b £GY EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x576c £GZ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x576d £HA EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x576e £HB EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x576f £HC EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5770 £HD EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5771 £HE EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5772 £HF EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5773 £HG EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5774 £HH EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5775 £HI EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5776 £HJ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5777 £HK EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5778 £HL EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5779 £HM EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x577a £HN EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x577b £HO EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x577c £HP EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x577d £HQ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x577e £HR EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x577f £HS EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5780 £HT EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5781 £HU EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5782 £HV EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5783 £HW EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5784 £HX EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5785 £HY EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5786 £HZ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5787 £IA EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5788 £IB EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5789 £IC EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x578a £ID EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x578b £IE EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x578c £IF EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x578d £IG EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x578e £IH EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x578f £II EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5790 £IJ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5791 £IK EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5792 £IL EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5793 £IM EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5794 £IN EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5795 £IO EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5796 £IP EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5797 £IQ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5798 £IR EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5799 £IS EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x579a £IT EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x579b £IU EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x579c £IV EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x579d £IW EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x579e £IX EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x579f £IY EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57a0 £IZ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57a1 £JA EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57a2 £JB EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57a3 £JC EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57a4 £JD EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57a5 £JE EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57a6 £JF EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57a7 £JG EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57a8 £JH EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57a9 £JI EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57aa £JJ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57ab £JK EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57ac £JL EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57ad £JM EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57ae £JN EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57af £JO EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57b0 £JP EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57b1 £JQ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57b2 £JR EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57b3 £JS EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57b4 £JT EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57b5 £JU EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57b6 £JV EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57b7 £JW EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57b8 £JX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57b9 £JY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57ba £JZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57bb £KA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57bc £KB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57bd £KC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57be £KD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57bf £KE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57c0 £KF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57c1 £KG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57c2 £KH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57c3 £KI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57c4 £KJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57c5 £KK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57c6 £KL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57c7 £KM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57c8 £KN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57c9 £KO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57ca £KP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57cb £KQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57cc £KR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57cd £KS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57ce £KT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57cf £KU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57d0 £KV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57d1 £KW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57d2 £KX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57d3 £KY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57d4 £KZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57d5 £LA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57d6 £LB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57d7 £LC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57d8 £LD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57d9 £LE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57da £LF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57db £LG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57dc £LH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57dd £LI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57de £LJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57df £LK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57e0 £LL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57e1 £LM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57e2 £LN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57e3 £LO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57e4 £LP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57e5 £LQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57e6 £LR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57e7 £LS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57e8 £LT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57e9 £LU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57ea £LV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57eb £LW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57ec £LX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57ed £LY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57ee £LZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57ef £MA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57f0 £MB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57f1 £MC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57f2 £MD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57f3 £ME EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57f4 £MF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57f5 £MG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57f6 £MH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57f7 £MI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57f8 £MJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57f9 £MK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57fa £ML EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57fb £MM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57fc £MN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57fd £MO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57fe £MP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57ff £MQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5800 £MR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5801 £MS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5802 £MT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5803 £MU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5804 £MV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5805 £MW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5806 £MX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5807 £MY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5808 £MZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5809 £NA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x580a £NB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x580b £NC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x580c £ND EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x580d £NE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x580e £NF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x580f £NG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5810 £NH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5811 £NI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5812 £NJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5813 £NK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5814 £NL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5815 £NM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5816 £NN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5817 £NO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5818 £NP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5819 £NQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x581a £NR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x581b £NS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x581c £NT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x581d £NU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x581e £NV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x581f £NW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5820 £NX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5821 £NY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5822 £NZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5823 £OA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5824 £OB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5825 £OC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5826 £OD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5827 £OE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5828 £OF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5829 £OG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x582a £OH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x582b £OI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x582c £OJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x582d £OK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x582e £OL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x582f £OM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5830 £ON EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5831 £OO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5832 £OP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5833 £OQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5834 £OR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5835 £OS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5836 £OT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5837 £OU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5838 £OV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5839 £OW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x583a £OX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x583b £OY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x583c £OZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x583d £PA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x583e £PB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x583f £PC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5840 £PD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5841 £PE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5842 £PF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5843 £PG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5844 £PH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5845 £PI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5846 £PJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5847 £PK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5848 £PL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5849 £PM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x584a £PN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x584b £PO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x584c £PP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x584d £PQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x584e £PR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x584f £PS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5850 £PT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5851 £PU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5852 £PV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5853 £PW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5854 £PX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5855 £PY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5856 £PZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5857 £QA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5858 £QB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5859 £QC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x585a £QD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x585b £QE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x585c £QF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x585d £QG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x585e £QH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x585f £QI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5860 £QJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5861 £QK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5862 £QL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5863 £QM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5864 £QN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5865 £QO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5866 £QP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5867 £QQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5868 £QR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5869 £QS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x586a £QT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x586b £QU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x586c £QV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x586d £QW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x586e £QX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x586f £QY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5870 £QZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5871 £RA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5872 £RB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5873 £RC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5874 £RD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5875 £RE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5876 £RF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5877 £RG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5878 £RH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5879 £RI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x587a £RJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x587b £RK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x587c £RL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x587d £RM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x587e £RN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x587f £RO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5880 £RP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5881 £RQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5882 £RR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5883 £RS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5884 £RT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5885 £RU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5886 £RV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5887 £RW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5888 £RX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5889 £RY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x588a £RZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x588b £SA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x588c £SB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x588d £SC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x588e £SD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x588f £SE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5890 £SF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5891 £SG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5892 £SH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5893 £SI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5894 £SJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5895 £SK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5896 £SL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5897 £SM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5898 £SN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5899 £SO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x589a £SP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x589b £SQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x589c £SR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x589d £SS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x589e £ST EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x589f £SU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58a0 £SV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58a1 £SW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58a2 £SX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58a3 £SY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58a4 £SZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58a5 £TA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58a6 £TB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58a7 £TC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58a8 £TD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58a9 £TE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58aa £TF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58ab £TG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58ac £TH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58ad £TI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58ae £TJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58af £TK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58b0 £TL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58b1 £TM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58b2 £TN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58b3 £TO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58b4 £TP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58b5 £TQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58b6 £TR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58b7 £TS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58b8 £TT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58b9 £TU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58ba £TV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58bb £TW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58bc £TX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58bd £TY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58be £TZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58bf £UA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58c0 £UB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58c1 £UC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58c2 £UD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58c3 £UE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58c4 £UF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58c5 £UG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58c6 £UH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58c7 £UI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58c8 £UJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58c9 £UK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58ca £UL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58cb £UM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58cc £UN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58cd £UO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58ce £UP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58cf £UQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58d0 £UR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58d1 £US EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58d2 £UT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58d3 £UU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58d4 £UV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58d5 £UW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58d6 £UX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58d7 £UY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58d8 £UZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58d9 £VA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58da £VB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58db £VC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58dc £VD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58dd £VE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58de £VF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58df £VG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58e0 £VH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58e1 £VI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58e2 £VJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58e3 £VK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58e4 £VL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58e5 £VM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58e6 £VN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58e7 £VO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58e8 £VP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58e9 £VQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58ea £VR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58eb £VS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58ec £VT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58ed £VU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58ee £VV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58ef £VW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58f0 £VX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58f1 £VY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58f2 £VZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58f3 £WA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5b00 µAA EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b01 µAB EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b02 µAC EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b03 µAD EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b04 µAE EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b05 µAF EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b06 µAG EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b07 µAH EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b08 µAI EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b09 µAJ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b0a µAK EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b0b µAL EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b0c µAM EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b0d µAN EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b0e µAO EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b0f µAP EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b10 µAQ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b11 µAR EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b12 µAS EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b13 µAT EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b14 µAU EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b15 µAV EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b16 µAW EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b17 µAX EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b18 µAY EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b19 µAZ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b1a µBA EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b1b µBB EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b1c µBC EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b1d µBD EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b1e µBE EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b1f µBF EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b20 µBG EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b21 µBH EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b22 µBI EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b23 µBJ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b24 µBK EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b25 µBL EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b26 µBM EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b27 µBN EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b28 µBO EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b29 µBP EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b2a µBQ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b2b µBR EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b2c µBS EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b2d µBT EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b2e µBU EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b2f µBV EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b30 µBW EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b31 µBX EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b32 µBY EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b33 µBZ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b34 µCA EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b35 µCB EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b36 µCC EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b37 µCD EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b38 µCE EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b39 µCF EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b3a µCG EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b3b µCH EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b3c µCI EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b3d µCJ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b3e µCK EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b3f µCL EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b40 µCM EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b41 µCN EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b42 µCO EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b43 µCP EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b44 µCQ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b45 µCR EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b46 µCS EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b47 µCT EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b48 µCU EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b49 µCV EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b4a µCW EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b4b µCX EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b4c µCY EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b4d µCZ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b4e µDA EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b4f µDB EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b50 µDC EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b51 µDD EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b52 µDE EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b53 µDF EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b54 µDG EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b55 µDH EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b56 µDI EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b57 µDJ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b58 µDK EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b59 µDL EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b5a µDM EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b5b µDN EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b5c µDO EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b5d µDP EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b5e µDQ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b5f µDR EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b60 µDS EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b61 µDT EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b62 µDU EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b63 µDV EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b64 µDW EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b65 µDX EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b66 µDY EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b67 µDZ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b68 µEA EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b69 µEB EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b6a µEC EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b6b µED EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b6c µEE EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b6d µEF EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b6e µEG EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b6f µEH EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b70 µEI EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b71 µEJ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b72 µEK EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b73 µEL EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b74 µEM EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b75 µEN EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b76 µEO EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b77 µEP EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b78 µEQ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b79 µER EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b7a µES EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b7b µET EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b7c µEU EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b7d µEV EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b7e µEW EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b7f µEX EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b80 µEY EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b81 µEZ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b82 µFA EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b83 µFB EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b84 µFC EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b85 µFD EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b86 µFE EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b87 µFF EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b88 µFG EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b89 µFH EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b8a µFI EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b8b µFJ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b8c µFK EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b8d µFL EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b8e µFM EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b8f µFN EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b90 µFO EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b91 µFP EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b92 µFQ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b93 µFR EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b94 µFS EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b95 µFT EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b96 µFU EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b97 µFV EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b98 µFW EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b99 µFX EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b9a µFY EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b9b µFZ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b9c µGA EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b9d µGB EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b9e µGC EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b9f µGD EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5ba0 µGE EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5ba1 µGF EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5ba2 µGG EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5ba3 µGH EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5ba4 µGI EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5ba5 µGJ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5ba6 µGK EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5ba7 µGL EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5ba8 µGM EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5ba9 µGN EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5baa µGO EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bab µGP EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bac µGQ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bad µGR EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bae µGS EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5baf µGT EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bb0 µGU EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bb1 µGV EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bb2 µGW EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bb3 µGX EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bb4 µGY EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bb5 µGZ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bb6 µHA EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bb7 µHB EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bb8 µHC EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bb9 µHD EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bba µHE EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bbb µHF EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bbc µHG EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bbd µHH EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bbe µHI EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bbf µHJ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bc0 µHK EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bc1 µHL EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bc2 µHM EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bc3 µHN EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bc4 µHO EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bc5 µHP EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bc6 µHQ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bc7 µHR EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bc8 µHS EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bc9 µHT EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bca µHU EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bcb µHV EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bcc µHW EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bcd µHX EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bce µHY EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bcf µHZ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bd0 µIA EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bd1 µIB EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bd2 µIC EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bd3 µID EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bd4 µIE EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bd5 µIF EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bd6 µIG EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bd7 µIH EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bd8 µII EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bd9 µIJ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bda µIK EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bdb µIL EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bdc µIM EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bdd µIN EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bde µIO EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bdf µIP EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5be0 µIQ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5be1 µIR EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5be2 µIS EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5be3 µIT EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5be4 µIU EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5be5 µIV EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5be6 µIW EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5be7 µIX EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5be8 µIY EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5be9 µIZ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bea µJA EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5beb µJB EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bec µJC EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bed µJD EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bee µJE EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bef µJF EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bf0 µJG EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bf1 µJH EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bf2 µJI EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bf3 µJJ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bf4 µJK EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bf5 µJL EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bf6 µJM EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bf7 µJN EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bf8 µJO EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bf9 µJP EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bfa µJQ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bfb µJR EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bfc µJS EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bfd µJT EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bfe µJU EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bff µJV EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5c00 µJW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c01 µJX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c02 µJY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c03 µJZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c04 µKA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c05 µKB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c06 µKC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c07 µKD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c08 µKE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c09 µKF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c0a µKG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c0b µKH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c0c µKI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c0d µKJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c0e µKK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c0f µKL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c10 µKM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c11 µKN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c12 µKO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c13 µKP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c14 µKQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c15 µKR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c16 µKS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c17 µKT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c18 µKU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c19 µKV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c1a µKW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c1b µKX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c1c µKY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c1d µKZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c1e µLA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c1f µLB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c20 µLC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c21 µLD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c22 µLE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c23 µLF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c24 µLG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c25 µLH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c26 µLI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c27 µLJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c28 µLK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c29 µLL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c2a µLM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c2b µLN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c2c µLO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c2d µLP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c2e µLQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c2f µLR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c30 µLS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c31 µLT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c32 µLU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c33 µLV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c34 µLW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c35 µLX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c36 µLY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c37 µLZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c38 µMA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c39 µMB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c3a µMC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c3b µMD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c3c µME EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c3d µMF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c3e µMG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c3f µMH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c40 µMI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c41 µMJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c42 µMK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c43 µML EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c44 µMM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c45 µMN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c46 µMO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c47 µMP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c48 µMQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c49 µMR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c4a µMS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c4b µMT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c4c µMU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c4d µMV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c4e µMW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c4f µMX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c50 µMY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c51 µMZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c52 µNA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c53 µNB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c54 µNC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c55 µND EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c56 µNE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c57 µNF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c58 µNG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c59 µNH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c5a µNI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c5b µNJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c5c µNK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c5d µNL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c5e µNM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c5f µNN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c60 µNO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c61 µNP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c62 µNQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c63 µNR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c64 µNS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c65 µNT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c66 µNU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c67 µNV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c68 µNW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c69 µNX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c6a µNY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c6b µNZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c6c µOA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c6d µOB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c6e µOC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c6f µOD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c70 µOE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c71 µOF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c72 µOG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c73 µOH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c74 µOI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c75 µOJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c76 µOK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c77 µOL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c78 µOM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c79 µON EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c7a µOO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c7b µOP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c7c µOQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c7d µOR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c7e µOS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c7f µOT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c80 µOU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c81 µOV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c82 µOW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c83 µOX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c84 µOY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c85 µOZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c86 µPA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c87 µPB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c88 µPC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c89 µPD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c8a µPE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c8b µPF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c8c µPG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c8d µPH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c8e µPI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c8f µPJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c90 µPK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c91 µPL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c92 µPM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c93 µPN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c94 µPO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c95 µPP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c96 µPQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c97 µPR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c98 µPS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c99 µPT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c9a µPU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c9b µPV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c9c µPW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c9d µPX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c9e µPY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c9f µPZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ca0 µQA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ca1 µQB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ca2 µQC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ca3 µQD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ca4 µQE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ca5 µQF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ca6 µQG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ca7 µQH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ca8 µQI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ca9 µQJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5caa µQK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cab µQL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cac µQM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cad µQN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cae µQO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5caf µQP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cb0 µQQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cb1 µQR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cb2 µQS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cb3 µQT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cb4 µQU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cb5 µQV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cb6 µQW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cb7 µQX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cb8 µQY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cb9 µQZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cba µRA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cbb µRB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cbc µRC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cbd µRD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cbe µRE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cbf µRF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cc0 µRG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cc1 µRH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cc2 µRI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cc3 µRJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cc4 µRK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cc5 µRL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cc6 µRM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cc7 µRN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cc8 µRO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cc9 µRP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cca µRQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ccb µRR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ccc µRS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ccd µRT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cce µRU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ccf µRV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cd0 µRW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cd1 µRX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cd2 µRY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cd3 µRZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cd4 µSA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cd5 µSB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cd6 µSC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cd7 µSD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cd8 µSE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cd9 µSF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cda µSG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cdb µSH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cdc µSI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cdd µSJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cde µSK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cdf µSL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ce0 µSM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ce1 µSN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ce2 µSO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ce3 µSP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ce4 µSQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ce5 µSR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ce6 µSS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ce7 µST EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ce8 µSU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ce9 µSV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cea µSW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ceb µSX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cec µSY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ced µSZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cee µTA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cef µTB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cf0 µTC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cf1 µTD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cf2 µTE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cf3 µTF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cf4 µTG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cf5 µTH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cf6 µTI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cf7 µTJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cf8 µTK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cf9 µTL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cfa µTM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cfb µTN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cfc µTO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cfd µTP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cfe µTQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cff µTR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d00 µTS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d01 µTT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d02 µTU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d03 µTV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d04 µTW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d05 µTX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d06 µTY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d07 µTZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d08 µUA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d09 µUB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d0a µUC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d0b µUD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d0c µUE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d0d µUF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d0e µUG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d0f µUH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d10 µUI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d11 µUJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d12 µUK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d13 µUL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d14 µUM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d15 µUN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d16 µUO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d17 µUP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d18 µUQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d19 µUR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d1a µUS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d1b µUT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d1c µUU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d1d µUV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d1e µUW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d1f µUX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d20 µUY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d21 µUZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d22 µVA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d23 µVB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d24 µVC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d25 µVD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d26 µVE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d27 µVF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d28 µVG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d29 µVH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d2a µVI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d2b µVJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d2c µVK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d2d µVL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d2e µVM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d2f µVN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d30 µVO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d31 µVP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d32 µVQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d33 µVR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d34 µVS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d35 µVT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d36 µVU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d37 µVV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d38 µVW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d39 µVX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d3a µVY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d3b µVZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d3d µAA EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d3e µAB EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d3f µAC EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d40 µAD EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d41 µAE EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d42 µAF EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d43 µAG EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d44 µAH EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d45 µAI EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d46 µAJ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d47 µAK EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d48 µAL EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d49 µAM EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d4a µAN EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d4b µAO EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d4c µAP EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d4d µAQ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d4e µAR EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d4f µAS EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d50 µAT EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d51 µAU EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d52 µAV EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d53 µAW EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d54 µAX EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d55 µAY EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d56 µAZ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d57 µBA EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d58 µBB EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d59 µBC EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d5a µBD EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d5b µBE EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d5c µBF EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d5d µBG EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d5e µBH EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d5f µBI EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d60 µBJ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d61 µBK EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d62 µBL EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d63 µBM EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d64 µBN EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d65 µBO EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d66 µBP EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d67 µBQ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d68 µBR EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d69 µBS EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d6a µBT EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d6b µBU EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d6c µBV EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d6d µBW EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d6e µBX EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d6f µBY EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d70 µBZ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d71 µCA EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d72 µCB EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d73 µCC EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d74 µCD EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d75 µCE EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d76 µCF EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d77 µCG EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d78 µCH EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d79 µCI EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d7a µCJ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d7b µCK EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d7c µCL EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d7d µCM EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d7e µCN EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d7f µCO EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d80 µCP EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d81 µCQ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d82 µCR EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d83 µCS EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d84 µCT EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d85 µCU EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d86 µCV EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d87 µCW EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d88 µCX EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d89 µCY EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d8a µCZ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d8b µDA EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d8c µDB EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d8d µDC EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d8e µDD EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d8f µDE EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d90 µDF EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d91 µDG EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d92 µDH EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d93 µDI EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d94 µDJ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d95 µDK EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d96 µDL EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d97 µDM EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d98 µDN EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d99 µDO EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d9a µDP EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d9b µDQ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d9c µDR EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d9d µDS EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d9e µDT EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d9f µDU EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5da0 µDV EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5da1 µDW EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5da2 µDX EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5da3 µDY EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5da4 µDZ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5da5 µEA EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5da6 µEB EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5da7 µEC EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5da8 µED EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5da9 µEE EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5daa µEF EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dab µEG EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dac µEH EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dad µEI EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dae µEJ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5daf µEK EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5db0 µEL EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5db1 µEM EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5db2 µEN EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5db3 µEO EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5db4 µEP EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5db5 µEQ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5db6 µER EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5db7 µES EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5db8 µET EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5db9 µEU EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dba µEV EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dbb µEW EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dbc µEX EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dbd µEY EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dbe µEZ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dbf µFA EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dc0 µFB EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dc1 µFC EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dc2 µFD EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dc3 µFE EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dc4 µFF EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dc5 µFG EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dc6 µFH EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dc7 µFI EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dc8 µFJ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dc9 µFK EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dca µFL EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dcb µFM EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dcc µFN EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dcd µFO EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dce µFP EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dcf µFQ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dd0 µFR EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dd1 µFS EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dd2 µFT EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dd3 µFU EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dd4 µFV EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dd5 µFW EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dd6 µFX EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dd7 µFY EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dd8 µFZ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dd9 µGA EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dda µGB EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5ddb µGC EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5ddc µGD EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5ddd µGE EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dde µGF EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5ddf µGG EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5de0 µGH EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5de1 µGI EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5de2 µGJ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5de3 µGK EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5de4 µGL EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5de5 µGM EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5de6 µGN EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5de7 µGO EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5de8 µGP EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5de9 µGQ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dea µGR EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5deb µGS EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dec µGT EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5ded µGU EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dee µGV EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5def µGW EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5df0 µGX EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5df1 µGY EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5df2 µGZ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5df3 µHA EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5df4 µHB EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5df5 µHC EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5df6 µHD EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5df7 µHE EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5df8 µHF EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5df9 µHG EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dfa µHH EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dfb µHI EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dfc µHJ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dfd µHK EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dfe µHL EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dff µHM EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e00 µHN EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e01 µHO EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e02 µHP EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e03 µHQ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e04 µHR EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e05 µHS EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e06 µHT EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e07 µHU EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e08 µHV EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e09 µHW EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e0a µHX EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e0b µHY EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e0c µHZ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e0d µIA EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e0e µIB EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e0f µIC EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e10 µID EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e11 µIE EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e12 µIF EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e13 µIG EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e14 µIH EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e15 µII EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e16 µIJ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e17 µIK EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e18 µIL EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e19 µIM EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e1a µIN EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e1b µIO EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e1c µIP EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e1d µIQ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e1e µIR EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e1f µIS EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e20 µIT EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e21 µIU EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e22 µIV EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e23 µIW EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e24 µIX EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e25 µIY EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e26 µIZ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e27 µJA EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e28 µJB EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e29 µJC EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e2a µJD EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e2b µJE EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e2c µJF EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e2d µJG EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e2e µJH EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e2f µJI EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e30 µJJ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e31 µJK EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e32 µJL EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e33 µJM EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e34 µJN EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e35 µJO EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e36 µJP EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e37 µJQ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e38 µJR EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e39 µJS EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e3a µJT EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e3b µJU EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e3c µJV EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e3d µJW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e3e µJX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e3f µJY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e40 µJZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e41 µKA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e42 µKB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e43 µKC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e44 µKD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e45 µKE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e46 µKF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e47 µKG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e48 µKH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e49 µKI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e4a µKJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e4b µKK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e4c µKL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e4d µKM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e4e µKN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e4f µKO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e50 µKP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e51 µKQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e52 µKR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e53 µKS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e54 µKT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e55 µKU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e56 µKV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e57 µKW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e58 µKX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e59 µKY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e5a µKZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e5b µLA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e5c µLB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e5d µLC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e5e µLD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e5f µLE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e60 µLF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e61 µLG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e62 µLH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e63 µLI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e64 µLJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e65 µLK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e66 µLL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e67 µLM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e68 µLN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e69 µLO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e6a µLP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e6b µLQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e6c µLR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e6d µLS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e6e µLT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e6f µLU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e70 µLV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e71 µLW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e72 µLX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e73 µLY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e74 µLZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e75 µMA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e76 µMB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e77 µMC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e78 µMD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e79 µME EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e7a µMF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e7b µMG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e7c µMH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e7d µMI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e7e µMJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e7f µMK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e80 µML EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e81 µMM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e82 µMN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e83 µMO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e84 µMP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e85 µMQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e86 µMR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e87 µMS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e88 µMT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e89 µMU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e8a µMV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e8b µMW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e8c µMX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e8d µMY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e8e µMZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e8f µNA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e90 µNB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e91 µNC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e92 µND EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e93 µNE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e94 µNF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e95 µNG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e96 µNH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e97 µNI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e98 µNJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e99 µNK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e9a µNL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e9b µNM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e9c µNN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e9d µNO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e9e µNP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e9f µNQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ea0 µNR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ea1 µNS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ea2 µNT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ea3 µNU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ea4 µNV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ea5 µNW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ea6 µNX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ea7 µNY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ea8 µNZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ea9 µOA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eaa µOB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eab µOC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eac µOD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ead µOE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eae µOF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eaf µOG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eb0 µOH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eb1 µOI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eb2 µOJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eb3 µOK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eb4 µOL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eb5 µOM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eb6 µON EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eb7 µOO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eb8 µOP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eb9 µOQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eba µOR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ebb µOS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ebc µOT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ebd µOU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ebe µOV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ebf µOW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ec0 µOX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ec1 µOY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ec2 µOZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ec3 µPA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ec4 µPB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ec5 µPC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ec6 µPD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ec7 µPE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ec8 µPF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ec9 µPG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eca µPH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ecb µPI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ecc µPJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ecd µPK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ece µPL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ecf µPM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ed0 µPN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ed1 µPO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ed2 µPP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ed3 µPQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ed4 µPR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ed5 µPS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ed6 µPT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ed7 µPU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ed8 µPV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ed9 µPW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eda µPX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5edb µPY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5edc µPZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5edd µQA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ede µQB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5edf µQC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ee0 µQD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ee1 µQE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ee2 µQF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ee3 µQG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ee4 µQH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ee5 µQI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ee6 µQJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ee7 µQK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ee8 µQL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ee9 µQM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eea µQN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eeb µQO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eec µQP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eed µQQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eee µQR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eef µQS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ef0 µQT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ef1 µQU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ef2 µQV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ef3 µQW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ef4 µQX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ef5 µQY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ef6 µQZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ef7 µRA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ef8 µRB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ef9 µRC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5efa µRD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5efb µRE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5efc µRF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5efd µRG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5efe µRH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eff µRI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f00 µRJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f01 µRK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f02 µRL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f03 µRM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f04 µRN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f05 µRO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f06 µRP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f07 µRQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f08 µRR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f09 µRS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f0a µRT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f0b µRU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f0c µRV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f0d µRW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f0e µRX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f0f µRY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f10 µRZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f11 µSA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f12 µSB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f13 µSC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f14 µSD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f15 µSE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f16 µSF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f17 µSG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f18 µSH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f19 µSI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f1a µSJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f1b µSK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f1c µSL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f1d µSM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f1e µSN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f1f µSO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f20 µSP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f21 µSQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f22 µSR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f23 µSS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f24 µST EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f25 µSU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f26 µSV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f27 µSW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f28 µSX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f29 µSY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f2a µSZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f2b µTA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f2c µTB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f2d µTC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f2e µTD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f2f µTE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f30 µTF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f31 µTG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f32 µTH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f33 µTI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f34 µTJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f35 µTK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f36 µTL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f37 µTM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f38 µTN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f39 µTO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f3a µTP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f3b µTQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f3c µTR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f3d µTS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f3e µTT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f3f µTU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f40 µTV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f41 µTW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f42 µTX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f43 µTY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f44 µTZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f45 µUA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f46 µUB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f47 µUC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f48 µUD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f49 µUE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f4a µUF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f4b µUG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f4c µUH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f4d µUI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f4e µUJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f4f µUK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f50 µUL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f51 µUM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f52 µUN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f53 µUO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f54 µUP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f55 µUQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f56 µUR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f57 µUS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f58 µUT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f59 µUU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f5a µUV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f5b µUW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f5c µUX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f5d µUY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f5e µUZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f5f µVA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f60 µVB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f61 µVC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f62 µVD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f63 µVE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f64 µVF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f65 µVG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f66 µVH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f67 µVI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f68 µVJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f69 µVK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f6a µVL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f6b µVM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f6c µVN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f6d µVO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f6e µVP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f6f µVQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f70 µVR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f71 µVS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f72 µVT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f73 µVU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f74 µVV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f75 µVW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f76 µVX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f77 µVY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f78 µVZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f7a ØAA EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f7b ØAB EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f7c ØAC EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f7d ØAD EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f7e ØAE EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f7f ØAF EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f80 ØAG EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f81 ØAH EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f82 ØAI EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f83 ØAJ EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f84 ØAK EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f85 ØAL EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f86 ØAM EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f87 ØAN EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f88 ØAO EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f89 ØAP EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f8a ØAQ EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f8b ØAR EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f8c ØAS EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f8d ØAT EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f8e ØAU EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f8f ØAV EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f90 ØAW EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f91 ØAX EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f92 ØAY EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f93 ØAZ EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f94 ØBA EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f95 ØBB EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f96 ØBC EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f97 ØBD EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f98 ØBE EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f99 ØBF EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f9a ØBG EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f9b ØBH EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5f9c ØBI EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5f9d ØBJ EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5f9e ØBK EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5f9f ØBL EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fa0 ØBM EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fa1 ØBN EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fa2 ØBO EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fa3 ØBP EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fa4 ØBQ EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fa5 ØBR EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fa6 ØBS EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fa7 ØBT EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fa8 ØBU EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fa9 ØBV EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5faa ØBW EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fab ØBX EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fac ØBY EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fad ØBZ EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fae ØCA EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5faf ØCB EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fb0 ØCC EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fb1 ØCD EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fb2 ØCE EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fb3 ØCF EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fb4 ØCG EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fb5 ØCH EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fb6 ØCI EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fb7 ØCJ EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fb8 ØCK EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fb9 ØCL EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fba ØCM EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fbb ØCN EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fbc ØCO EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fbd ØCP EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fbe ØCQ EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fbf ØCR EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fc0 ØCS EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fc1 ØCT EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fc2 ØCU EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fc3 ØCV EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fc4 ØCW EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fc5 ØCX EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fc6 ØCY EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fc7 ØCZ EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fc8 ØDA EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fc9 ØDB EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fca ØDC EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fcb ØDD EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fcc ØDE EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fcd ØDF EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fce ØDG EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fcf ØDH EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fd0 ØDI EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fd1 ØDJ EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fd2 ØDK EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fd3 ØDL EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fd4 ØDM EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fd5 ØDN EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fd6 ØDO EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fd7 ØDP EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fd8 ØDQ EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fd9 ØDR EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fda ØDS EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fdb ØDT EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fdc ØDU EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fdd ØDV EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5fde ØDW EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5fdf ØDX EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5fe0 ØDY EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5fe1 ØDZ EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5fe2 ØEA EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5fe3 ØEB EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5fe4 ØEC EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5fe5 ØED EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5fe6 ØEE EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5fe7 ØEF EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5fe8 ØEG EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5fe9 ØEH EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5fea ØEI EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5feb ØEJ EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5fec ØEK EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5fed ØEL EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5fee ØEM EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5fef ØEN EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5ff0 ØEO EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5ff1 ØEP EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5ff2 ØEQ EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5ff3 ØER EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5ff4 ØES EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5ff5 ØET EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5ff6 ØEU EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5ff7 ØEV EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5ff8 ØEW EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5ff9 ØEX EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5ffa ØEY EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5ffb ØEZ EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5ffc ØFA EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5ffd ØFB EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5ffe ØFC EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5fff ØFD EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x6600 ¢AµA EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6601 ¢AµB EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6602 ¢AµC EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6603 ¢AµD EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6604 ¢AµE EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6605 ¢AµF EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6606 ¢AµG EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6607 ¢AµH EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6608 ¢AµI EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6609 ¢AµJ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x660a ¢AµK EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x660b ¢AµL EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x660c ¢AµM EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x660d ¢AµN EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x660e ¢AµO EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x660f ¢AµP EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6610 ¢AµQ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6611 ¢AµR EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6612 ¢AµS EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6613 ¢AµT EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6614 ¢AµU EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6615 ¢AµV EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6616 ¢AµW EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6617 ¢AµX EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6618 ¢AµY EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6619 ¢AµZ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x661a ¢BµA EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x661b ¢BµB EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x661c ¢BµC EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x661d ¢BµD EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x661e ¢BµE EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x661f ¢BµF EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6620 ¢BµG EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6621 ¢BµH EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6622 ¢BµI EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6623 ¢BµJ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6624 ¢BµK EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6625 ¢BµL EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6626 ¢BµM EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6627 ¢BµN EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6628 ¢BµO EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6629 ¢BµP EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x662a ¢BµQ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x662b ¢BµR EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x662c ¢BµS EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x662d ¢BµT EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x662e ¢BµU EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x662f ¢BµV EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6630 ¢BµW EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6631 ¢BµX EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6632 ¢BµY EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6633 ¢BµZ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6634 ¢CµA EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6635 ¢CµB EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6636 ¢CµC EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6637 ¢CµD EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6638 ¢CµE EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6639 ¢CµF EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x663a ¢CµG EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x663b ¢CµH EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x663c ¢CµI EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x663d ¢CµJ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x663e ¢CµK EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x663f ¢CµL EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6640 ¢CµM EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6641 ¢CµN EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6642 ¢CµO EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6643 ¢CµP EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6644 ¢CµQ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6645 ¢CµR EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6646 ¢CµS EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6647 ¢CµT EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6648 ¢CµU EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6649 ¢CµV EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x664a ¢CµW EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x664b ¢CµX EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x664c ¢CµY EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x664d ¢CµZ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x664e ¢DµA EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x664f ¢DµB EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6650 ¢DµC EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6651 ¢DµD EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6652 ¢DµE EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6653 ¢DµF EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6654 ¢DµG EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6655 ¢DµH EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6656 ¢DµI EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6657 ¢DµJ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6658 ¢DµK EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6659 ¢DµL EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x665a ¢DµM EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x665b ¢DµN EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x665c ¢DµO EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x665d ¢DµP EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x665e ¢DµQ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x665f ¢DµR EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6660 ¢DµS EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6661 ¢DµT EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6662 ¢DµU EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6663 ¢DµV EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6664 ¢DµW EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6665 ¢DµX EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6666 ¢DµY EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6667 ¢DµZ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6668 ¢EµA EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6669 ¢EµB EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x666a ¢EµC EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x666b ¢EµD EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x666c ¢EµE EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x666d ¢EµF EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x666e ¢EµG EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x666f ¢EµH EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6670 ¢EµI EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6671 ¢EµJ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6672 ¢EµK EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6673 ¢EµL EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6674 ¢EµM EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6675 ¢EµN EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6676 ¢EµO EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6677 ¢EµP EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6678 ¢EµQ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6679 ¢EµR EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x667a ¢EµS EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x667b ¢EµT EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x667c ¢EµU EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x667d ¢EµV EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x667e ¢EµW EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x667f ¢EµX EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6680 ¢EµY EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6681 ¢EµZ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6682 ¢FµA EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6683 ¢FµB EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6684 ¢FµC EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6685 ¢FµD EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6686 ¢FµE EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6687 ¢FµF EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6688 ¢FµG EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6689 ¢FµH EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x668a ¢FµI EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x668b ¢FµJ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x668c ¢FµK EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x668d ¢FµL EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x668e ¢FµM EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x668f ¢FµN EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6690 ¢FµO EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6691 ¢FµP EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6692 ¢FµQ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6693 ¢FµR EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6694 ¢FµS EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6695 ¢FµT EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6696 ¢FµU EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6697 ¢FµV EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6698 ¢FµW EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6699 ¢FµX EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x669a ¢FµY EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x669b ¢FµZ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x669c ¢GµA EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x669d ¢GµB EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x669e ¢GµC EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x669f ¢GµD EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66a0 ¢GµE EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66a1 ¢GµF EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66a2 ¢GµG EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66a3 ¢GµH EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66a4 ¢GµI EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66a5 ¢GµJ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66a6 ¢GµK EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66a7 ¢GµL EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66a8 ¢GµM EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66a9 ¢GµN EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66aa ¢GµO EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66ab ¢GµP EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66ac ¢GµQ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66ad ¢GµR EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66ae ¢GµS EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66af ¢GµT EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66b0 ¢GµU EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66b1 ¢GµV EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66b2 ¢GµW EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66b3 ¢GµX EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66b4 ¢GµY EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66b5 ¢GµZ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66b6 ¢HµA EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66b7 ¢HµB EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66b8 ¢HµC EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66b9 ¢HµD EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66ba ¢HµE EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66bb ¢HµF EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66bc ¢HµG EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66bd ¢HµH EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66be ¢HµI EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66bf ¢HµJ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66c0 ¢HµK EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66c1 ¢HµL EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66c2 ¢HµM EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66c3 ¢HµN EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66c4 ¢HµO EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66c5 ¢HµP EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66c6 ¢HµQ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66c7 ¢HµR EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66c8 ¢HµS EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66c9 ¢HµT EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66ca ¢HµU EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66cb ¢HµV EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66cc ¢HµW EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66cd ¢HµX EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66ce ¢HµY EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66cf ¢HµZ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66d0 ¢IµA EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66d1 ¢IµB EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66d2 ¢IµC EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66d3 ¢IµD EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66d4 ¢IµE EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66d5 ¢IµF EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66d6 ¢IµG EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66d7 ¢IµH EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66d8 ¢IµI EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66d9 ¢IµJ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66da ¢IµK EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66db ¢IµL EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66dc ¢IµM EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66dd ¢IµN EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66de ¢IµO EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66df ¢IµP EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66e0 ¢IµQ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66e1 ¢IµR EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66e2 ¢IµS EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66e3 ¢IµT EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66e4 ¢IµU EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66e5 ¢IµV EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66e6 ¢IµW EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66e7 ¢IµX EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66e8 ¢IµY EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66e9 ¢IµZ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66ea ¢JµA EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66eb ¢JµB EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66ec ¢JµC EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66ed ¢JµD EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66ee ¢JµE EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66ef ¢JµF EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66f0 ¢JµG EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66f1 ¢JµH EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66f2 ¢JµI EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66f3 ¢JµJ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66f4 ¢JµK EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66f5 ¢JµL EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66f6 ¢JµM EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66f7 ¢JµN EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66f8 ¢JµO EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66f9 ¢JµP EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66fa ¢JµQ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66fb ¢JµR EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66fc ¢JµS EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66fd ¢JµT EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66fe ¢JµU EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66ff ¢JµV EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6700 ¢JµW EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6701 ¢JµX EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6702 ¢JµY EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6703 ¢JµZ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6704 ¢KµA EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6705 ¢KµB EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6706 ¢KµC EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6707 ¢KµD EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6708 ¢KµE EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6709 ¢KµF EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x670a ¢KµG EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x670b ¢KµH EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x670c ¢KµI EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x670d ¢KµJ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x670e ¢KµK EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x670f ¢KµL EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6710 ¢KµM EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6711 ¢KµN EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6712 ¢KµO EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6713 ¢KµP EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6714 ¢KµQ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6715 ¢KµR EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6716 ¢KµS EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6717 ¢KµT EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6718 ¢KµU EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6719 ¢KµV EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x671a ¢KµW EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x671b ¢KµX EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x671c ¢KµY EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x671d ¢KµZ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x671e ¢LµA EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x671f ¢LµB EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6720 ¢LµC EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6721 ¢LµD EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6722 ¢LµE EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6723 ¢LµF EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6724 ¢LµG EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6725 ¢LµH EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6726 ¢LµI EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6727 ¢LµJ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6728 ¢LµK EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6729 ¢LµL EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x672a ¢LµM EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x672b ¢LµN EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x672c ¢LµO EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x672d ¢LµP EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x672e ¢LµQ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x672f ¢LµR EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6730 ¢LµS EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6731 ¢LµT EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6732 ¢LµU EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6733 ¢LµV EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6734 ¢LµW EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6735 ¢LµX EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6736 ¢LµY EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6737 ¢LµZ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6738 ¢MµA EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6739 ¢MµB EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x673a ¢MµC EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x673b ¢MµD EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x673c ¢MµE EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x673d ¢MµF EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x673e ¢MµG EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x673f ¢MµH EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6740 ¢MµI EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6741 ¢MµJ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6742 ¢MµK EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6743 ¢MµL EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6744 ¢MµM EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6745 ¢MµN EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6746 ¢MµO EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6747 ¢MµP EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6748 ¢MµQ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6749 ¢MµR EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x674a ¢MµS EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x674b ¢MµT EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x674c ¢MµU EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x674d ¢MµV EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x674e ¢MµW EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x674f ¢MµX EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6750 ¢MµY EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6751 ¢MµZ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6752 ¢NµA EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6753 ¢NµB EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6754 ¢NµC EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6755 ¢NµD EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6756 ¢NµE EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6757 ¢NµF EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6758 ¢NµG EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6759 ¢NµH EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x675a ¢NµI EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x675b ¢NµJ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x675c ¢NµK EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x675d ¢NµL EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x675e ¢NµM EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x675f ¢NµN EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6760 ¢NµO EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6761 ¢NµP EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6762 ¢NµQ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6763 ¢NµR EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6764 ¢NµS EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6765 ¢NµT EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6766 ¢NµU EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6767 ¢NµV EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6768 ¢NµW EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6769 ¢NµX EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x676a ¢NµY EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x676b ¢NµZ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x676c ¢OµA EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x676d ¢OµB EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x676e ¢OµC EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x676f ¢OµD EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6770 ¢OµE EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6771 ¢OµF EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6772 ¢OµG EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6773 ¢OµH EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6774 ¢OµI EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6775 ¢OµJ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6776 ¢OµK EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6777 ¢OµL EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6778 ¢OµM EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6779 ¢OµN EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x677a ¢OµO EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x677b ¢OµP EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x677c ¢OµQ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x677d ¢OµR EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x677e ¢OµS EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x677f ¢OµT EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6780 ¢OµU EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6781 ¢OµV EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6782 ¢OµW EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6783 ¢OµX EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6784 ¢OµY EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6785 ¢OµZ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6786 ¢PµA EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6787 ¢PµB EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6788 ¢PµC EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6789 ¢PµD EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x678a ¢PµE EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x678b ¢PµF EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x678c ¢PµG EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x678d ¢PµH EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x678e ¢PµI EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x678f ¢PµJ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6790 ¢PµK EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6791 ¢PµL EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6792 ¢PµM EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6793 ¢PµN EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6794 ¢PµO EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6795 ¢PµP EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6796 ¢PµQ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6797 ¢PµR EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6798 ¢PµS EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6799 ¢PµT EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x679a ¢PµU EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x679b ¢PµV EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x679c ¢PµW EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x679d ¢PµX EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x679e ¢PµY EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x679f ¢PµZ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67a0 ¢QµA EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67a1 ¢QµB EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67a2 ¢QµC EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67a3 ¢QµD EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67a4 ¢QµE EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67a5 ¢QµF EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67a6 ¢QµG EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67a7 ¢QµH EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67a8 ¢QµI EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67a9 ¢QµJ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67aa ¢QµK EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67ab ¢QµL EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67ac ¢QµM EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67ad ¢QµN EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67ae ¢QµO EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67af ¢QµP EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67b0 ¢QµQ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67b1 ¢QµR EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67b2 ¢QµS EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67b3 ¢QµT EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67b4 ¢QµU EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67b5 ¢QµV EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67b6 ¢QµW EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67b7 ¢QµX EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67b8 ¢QµY EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67b9 ¢QµZ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67ba ¢RµA EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67bb ¢RµB EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67bc ¢RµC EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67bd ¢RµD EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67be ¢RµE EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67bf ¢RµF EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67c0 ¢RµG EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67c1 ¢RµH EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67c2 ¢RµI EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67c3 ¢RµJ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67c4 ¢RµK EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67c5 ¢RµL EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67c6 ¢RµM EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67c7 ¢RµN EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67c8 ¢RµO EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67c9 ¢RµP EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67ca ¢RµQ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67cb ¢RµR EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67cc ¢RµS EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67cd ¢RµT EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67ce ¢RµU EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67cf ¢RµV EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67d0 ¢RµW EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67d1 ¢RµX EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67d2 ¢RµY EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67d3 ¢RµZ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67d4 ¢SµA EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67d5 ¢SµB EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67d6 ¢SµC EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67d7 ¢SµD EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67d8 ¢SµE EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67d9 ¢SµF EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67da ¢SµG EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67db ¢SµH EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67dc ¢SµI EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67dd ¢SµJ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67de ¢SµK EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67df ¢SµL EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67e0 ¢SµM EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67e1 ¢SµN EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67e2 ¢SµO EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67e3 ¢SµP EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67e4 ¢SµQ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67e5 ¢SµR EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67e6 ¢SµS EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67e7 ¢SµT EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67e8 ¢SµU EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67e9 ¢SµV EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67ea ¢SµW EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67eb ¢SµX EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67ec ¢SµY EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67ed ¢SµZ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67ee ¢TµA EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67ef ¢TµB EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67f0 ¢TµC EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67f1 ¢TµD EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67f2 ¢TµE EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67f3 ¢TµF EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67f4 ¢TµG EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67f5 ¢TµH EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67f6 ¢TµI EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67f7 ¢TµJ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67f8 ¢TµK EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67f9 ¢TµL EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67fa ¢TµM EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67fb ¢TµN EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67fc ¢TµO EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67fd ¢TµP EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67fe ¢TµQ EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67ff ¢TµR EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6800 ¢TµS EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6801 ¢TµT EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6802 ¢TµU EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6803 ¢TµV EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6804 ¢TµW EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6805 ¢TµX EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6806 ¢TµY EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6807 ¢TµZ EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6808 ¢UµA EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6809 ¢UµB EMPTY LARGE 5 Co CHARACTER BGII paletted +0x680a ¢UµC EMPTY LARGE 5 Co CHARACTER BGII paletted +0x680b ¢UµD EMPTY LARGE 5 Co CHARACTER BGII paletted +0x680c ¢UµE EMPTY LARGE 5 Co CHARACTER BGII paletted +0x680d ¢UµF EMPTY LARGE 5 Co CHARACTER BGII paletted +0x680e ¢UµG EMPTY LARGE 5 Co CHARACTER BGII paletted +0x680f ¢UµH EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6810 ¢UµI EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6811 ¢UµJ EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6812 ¢UµK EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6813 ¢UµL EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6814 ¢UµM EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6815 ¢UµN EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6816 ¢UµO EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6817 ¢UµP EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6818 ¢UµQ EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6819 ¢UµR EMPTY LARGE 5 Co CHARACTER BGII paletted +0x681a ¢UµS EMPTY LARGE 5 Co CHARACTER BGII paletted +0x681b ¢UµT EMPTY LARGE 5 Co CHARACTER BGII paletted +0x681c ¢UµU EMPTY LARGE 5 Co CHARACTER BGII paletted +0x681d ¢UµV EMPTY LARGE 5 Co CHARACTER BGII paletted +0x681e ¢UµW EMPTY LARGE 5 Co CHARACTER BGII paletted +0x681f ¢UµX EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6820 ¢UµY EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6821 ¢UµZ EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6822 ¢VµA EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6823 ¢VµB EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6824 ¢VµC EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6825 ¢VµD EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6826 ¢VµE EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6827 ¢VµF EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6828 ¢VµG EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6829 ¢VµH EMPTY LARGE 5 Co CHARACTER BGII paletted +0x682a ¢VµI EMPTY LARGE 5 Co CHARACTER BGII paletted +0x682b ¢VµJ EMPTY LARGE 5 Co CHARACTER BGII paletted +0x682c ¢VµK EMPTY LARGE 5 Co CHARACTER BGII paletted +0x682d ¢VµL EMPTY LARGE 5 Co CHARACTER BGII paletted +0x682e ¢VµM EMPTY LARGE 5 Co CHARACTER BGII paletted +0x682f ¢VµN EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6830 ¢VµO EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6831 ¢VµP EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6832 ¢VµQ EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6833 ¢VµR EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6834 ¢VµS EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6835 ¢VµT EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6836 ¢VµU EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6837 ¢VµV EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6838 ¢VµW EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6839 ¢VµX EMPTY LARGE 5 Co CHARACTER BGII paletted +0x683a ¢VµY EMPTY LARGE 5 Co CHARACTER BGII paletted +0x683b ¢VµZ EMPTY LARGE 5 Co CHARACTER BGII paletted +0x683c ¢WµA EMPTY LARGE 5 Co CHARACTER BGII paletted +0x683d ¢A¢A EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x683e ¢A¢B EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x683f ¢A¢C EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x6840 ¢A¢D EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x6841 ¢A¢E EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6842 ¢A¢F EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6843 ¢A¢G EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6844 ¢A¢H EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6845 ¢A¢I EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6846 ¢A¢J EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6847 ¢A¢K EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6848 ¢A¢L EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6849 ¢A¢M EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x684a ¢A¢N EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x684b ¢A¢O EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x684c ¢A¢P EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x684d ¢A¢Q EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x684e ¢A¢R EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x684f ¢A¢S EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x6850 ¢A¢T EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x6851 ¢A¢U EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6852 ¢A¢V EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6853 ¢A¢W EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6854 ¢A¢X EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6855 ¢A¢Y EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6856 ¢A¢Z EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6857 ¢B¢A EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6858 ¢B¢B EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6859 ¢B¢C EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x685a ¢B¢D EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x685b ¢B¢E EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x685c ¢B¢F EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x685d ¢B¢G EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x685e ¢B¢H EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x685f ¢B¢I EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x6860 ¢B¢J EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x6861 ¢B¢K EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6862 ¢B¢L EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6863 ¢B¢M EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6864 ¢B¢N EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6865 ¢B¢O EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6866 ¢B¢P EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6867 ¢B¢Q EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6868 ¢B¢R EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6869 ¢B¢S EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x686a ¢B¢T EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x686b ¢B¢U EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x686c ¢B¢V EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x686d ¢B¢W EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x686e ¢B¢X EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x686f ¢B¢Y EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x6870 ¢B¢Z EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x6871 ¢C¢A EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6872 ¢C¢B EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6873 ¢C¢C EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6874 ¢C¢D EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6875 ¢C¢E EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6876 ¢C¢F EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6877 ¢C¢G EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6878 ¢C¢H EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6879 ¢C¢I EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x687a ¢C¢J EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x687b ¢C¢K EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x687c ¢C¢L EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x687d ¢C¢M EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x687e ¢C¢N EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x687f ¢C¢O EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x6880 ¢C¢P EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x6881 ¢C¢Q EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6882 ¢C¢R EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6883 ¢C¢S EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6884 ¢C¢T EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6885 ¢C¢U EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6886 ¢C¢V EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6887 ¢C¢W EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6888 ¢C¢X EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6889 ¢C¢Y EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x688a ¢C¢Z EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x688b ¢D¢A EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x688c ¢D¢B EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x688d ¢D¢C EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x688e ¢D¢D EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x688f ¢D¢E EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x6890 ¢D¢F EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x6891 ¢D¢G EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6892 ¢D¢H EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6893 ¢D¢I EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6894 ¢D¢J EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6895 ¢D¢K EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6896 ¢D¢L EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6897 ¢D¢M EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6898 ¢D¢N EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6899 ¢D¢O EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x689a ¢D¢P EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x689b ¢D¢Q EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x689c ¢D¢R EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x689d ¢D¢S EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x689e ¢D¢T EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x689f ¢D¢U EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x68a0 ¢D¢V EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x68a1 ¢D¢W EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x68a2 ¢D¢X EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x68a3 ¢D¢Y EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x68a4 ¢D¢Z EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x68a5 ¢E¢A EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x68a6 ¢E¢B EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x68a7 ¢E¢C EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x68a8 ¢E¢D EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x68a9 ¢E¢E EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x68aa ¢E¢F EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x68ab ¢E¢G EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x68ac ¢E¢H EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x68ad ¢E¢I EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x68ae ¢E¢J EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x68af ¢E¢K EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x68b0 ¢E¢L EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x68b1 ¢E¢M EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x68b2 ¢E¢N EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x68b3 ¢E¢O EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x68b4 ¢E¢P EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x68b5 ¢E¢Q EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x68b6 ¢E¢R EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x68b7 ¢E¢S EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x68b8 ¢E¢T EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x68b9 ¢E¢U EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x68ba ¢E¢V EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x68bb ¢E¢W EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x68bc ¢E¢X EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x68bd ¢E¢Y EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x68be ¢E¢Z EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x68bf ¢F¢A EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x68c0 ¢F¢B EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x68c1 ¢F¢C EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x68c2 ¢F¢D EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x68c3 ¢AµA EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68c4 ¢AµB EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68c5 ¢AµC EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68c6 ¢AµD EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68c7 ¢AµE EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68c8 ¢AµF EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68c9 ¢AµG EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68ca ¢AµH EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68cb ¢AµI EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68cc ¢AµJ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68cd ¢AµK EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68ce ¢AµL EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68cf ¢AµM EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68d0 ¢AµN EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68d1 ¢AµO EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68d2 ¢AµP EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68d3 ¢AµQ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68d4 ¢AµR EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68d5 ¢AµS EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68d6 ¢AµT EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68d7 ¢AµU EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68d8 ¢AµV EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68d9 ¢AµW EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68da ¢AµX EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68db ¢AµY EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68dc ¢AµZ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68dd ¢BµA EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68de ¢BµB EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68df ¢BµC EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68e0 ¢BµD EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68e1 ¢BµE EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68e2 ¢BµF EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68e3 ¢BµG EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68e4 ¢BµH EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68e5 ¢BµI EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68e6 ¢BµJ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68e7 ¢BµK EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68e8 ¢BµL EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68e9 ¢BµM EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68ea ¢BµN EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68eb ¢BµO EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68ec ¢BµP EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68ed ¢BµQ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68ee ¢BµR EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68ef ¢BµS EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68f0 ¢BµT EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68f1 ¢BµU EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68f2 ¢BµV EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68f3 ¢BµW EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68f4 ¢BµX EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68f5 ¢BµY EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68f6 ¢BµZ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68f7 ¢CµA EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68f8 ¢CµB EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68f9 ¢CµC EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68fa ¢CµD EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68fb ¢CµE EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68fc ¢CµF EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68fd ¢CµG EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68fe ¢CµH EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68ff ¢CµI EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6900 ¢CµJ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6901 ¢CµK EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6902 ¢CµL EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6903 ¢CµM EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6904 ¢CµN EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6905 ¢CµO EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6906 ¢CµP EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6907 ¢CµQ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6908 ¢CµR EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6909 ¢CµS EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x690a ¢CµT EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x690b ¢CµU EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x690c ¢CµV EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x690d ¢CµW EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x690e ¢CµX EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x690f ¢CµY EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6910 ¢CµZ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6911 ¢DµA EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6912 ¢DµB EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6913 ¢DµC EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6914 ¢DµD EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6915 ¢DµE EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6916 ¢DµF EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6917 ¢DµG EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6918 ¢DµH EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6919 ¢DµI EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x691a ¢DµJ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x691b ¢DµK EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x691c ¢DµL EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x691d ¢DµM EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x691e ¢DµN EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x691f ¢DµO EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6920 ¢DµP EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6921 ¢DµQ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6922 ¢DµR EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6923 ¢DµS EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6924 ¢DµT EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6925 ¢DµU EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6926 ¢DµV EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6927 ¢DµW EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6928 ¢DµX EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6929 ¢DµY EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x692a ¢DµZ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x692b ¢EµA EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x692c ¢EµB EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x692d ¢EµC EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x692e ¢EµD EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x692f ¢EµE EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6930 ¢EµF EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6931 ¢EµG EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6932 ¢EµH EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6933 ¢EµI EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6934 ¢EµJ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6935 ¢EµK EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6936 ¢EµL EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6937 ¢EµM EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6938 ¢EµN EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6939 ¢EµO EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x693a ¢EµP EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x693b ¢EµQ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x693c ¢EµR EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x693d ¢EµS EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x693e ¢EµT EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x693f ¢EµU EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6940 ¢EµV EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6941 ¢EµW EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6942 ¢EµX EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6943 ¢EµY EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6944 ¢EµZ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6945 ¢FµA EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6946 ¢FµB EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6947 ¢FµC EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6948 ¢FµD EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6949 ¢FµE EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x694a ¢FµF EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x694b ¢FµG EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x694c ¢FµH EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x694d ¢FµI EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x694e ¢FµJ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x694f ¢FµK EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6950 ¢FµL EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6951 ¢FµM EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6952 ¢FµN EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6953 ¢FµO EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6954 ¢FµP EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6955 ¢FµQ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6956 ¢FµR EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6957 ¢FµS EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6958 ¢FµT EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6959 ¢FµU EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x695a ¢FµV EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x695b ¢FµW EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x695c ¢FµX EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x695d ¢FµY EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x695e ¢FµZ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x695f ¢GµA EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6960 ¢GµB EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6961 ¢GµC EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6962 ¢GµD EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6963 ¢GµE EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6964 ¢GµF EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6965 ¢GµG EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6966 ¢GµH EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6967 ¢GµI EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6968 ¢GµJ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6969 ¢GµK EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x696a ¢GµL EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x696b ¢GµM EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x696c ¢GµN EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x696d ¢GµO EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x696e ¢GµP EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x696f ¢GµQ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6970 ¢GµR EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6971 ¢GµS EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6972 ¢GµT EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6973 ¢GµU EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6974 ¢GµV EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6975 ¢GµW EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6976 ¢GµX EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6977 ¢GµY EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6978 ¢GµZ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6979 ¢HµA EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x697a ¢HµB EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x697b ¢HµC EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x697c ¢HµD EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x697d ¢HµE EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x697e ¢HµF EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x697f ¢HµG EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6980 ¢HµH EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6981 ¢HµI EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6982 ¢HµJ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6983 ¢HµK EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6984 ¢HµL EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6985 ¢HµM EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6986 ¢HµN EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6987 ¢HµO EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6988 ¢HµP EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6989 ¢HµQ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x698a ¢HµR EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x698b ¢HµS EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x698c ¢HµT EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x698d ¢HµU EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x698e ¢HµV EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x698f ¢HµW EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6990 ¢HµX EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6991 ¢HµY EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6992 ¢HµZ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6993 ¢IµA EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6994 ¢IµB EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6995 ¢IµC EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6996 ¢IµD EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6997 ¢IµE EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6998 ¢IµF EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6999 ¢IµG EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x699a ¢IµH EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x699b ¢IµI EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x699c ¢IµJ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x699d ¢IµK EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x699e ¢IµL EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x699f ¢IµM EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69a0 ¢IµN EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69a1 ¢IµO EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69a2 ¢IµP EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69a3 ¢IµQ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69a4 ¢IµR EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69a5 ¢IµS EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69a6 ¢IµT EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69a7 ¢IµU EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69a8 ¢IµV EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69a9 ¢IµW EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69aa ¢IµX EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69ab ¢IµY EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69ac ¢IµZ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69ad ¢JµA EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69ae ¢JµB EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69af ¢JµC EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69b0 ¢JµD EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69b1 ¢JµE EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69b2 ¢JµF EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69b3 ¢JµG EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69b4 ¢JµH EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69b5 ¢JµI EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69b6 ¢JµJ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69b7 ¢JµK EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69b8 ¢JµL EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69b9 ¢JµM EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69ba ¢JµN EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69bb ¢JµO EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69bc ¢JµP EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69bd ¢JµQ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69be ¢JµR EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69bf ¢JµS EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69c0 ¢JµT EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69c1 ¢JµU EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69c2 ¢JµV EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69c3 ¢JµW EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69c4 ¢JµX EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69c5 ¢JµY EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69c6 ¢JµZ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69c7 ¢KµA EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69c8 ¢KµB EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69c9 ¢KµC EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69ca ¢KµD EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69cb ¢KµE EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69cc ¢KµF EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69cd ¢KµG EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69ce ¢KµH EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69cf ¢KµI EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69d0 ¢KµJ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69d1 ¢KµK EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69d2 ¢KµL EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69d3 ¢KµM EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69d4 ¢KµN EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69d5 ¢KµO EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69d6 ¢KµP EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69d7 ¢KµQ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69d8 ¢KµR EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69d9 ¢KµS EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69da ¢KµT EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69db ¢KµU EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69dc ¢KµV EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69dd ¢KµW EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69de ¢KµX EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69df ¢KµY EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69e0 ¢KµZ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69e1 ¢LµA EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69e2 ¢LµB EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69e3 ¢LµC EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69e4 ¢LµD EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69e5 ¢LµE EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69e6 ¢LµF EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69e7 ¢LµG EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69e8 ¢LµH EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69e9 ¢LµI EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69ea ¢LµJ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69eb ¢LµK EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69ec ¢LµL EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69ed ¢LµM EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69ee ¢LµN EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69ef ¢LµO EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69f0 ¢LµP EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69f1 ¢LµQ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69f2 ¢LµR EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69f3 ¢LµS EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69f4 ¢LµT EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69f5 ¢LµU EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69f6 ¢LµV EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69f7 ¢LµW EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69f8 ¢LµX EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69f9 ¢LµY EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69fa ¢LµZ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69fb ¢MµA EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69fc ¢MµB EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69fd ¢MµC EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69fe ¢MµD EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69ff ¢MµE EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a00 ¢MµF EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a01 ¢MµG EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a02 ¢MµH EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a03 ¢MµI EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a04 ¢MµJ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a05 ¢MµK EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a06 ¢MµL EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a07 ¢MµM EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a08 ¢MµN EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a09 ¢MµO EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a0a ¢MµP EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a0b ¢MµQ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a0c ¢MµR EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a0d ¢MµS EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a0e ¢MµT EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a0f ¢MµU EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a10 ¢MµV EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a11 ¢MµW EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a12 ¢MµX EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a13 ¢MµY EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a14 ¢MµZ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a15 ¢NµA EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a16 ¢NµB EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a17 ¢NµC EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a18 ¢NµD EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a19 ¢NµE EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a1a ¢NµF EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a1b ¢NµG EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a1c ¢NµH EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a1d ¢NµI EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a1e ¢NµJ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a1f ¢NµK EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a20 ¢NµL EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a21 ¢NµM EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a22 ¢NµN EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a23 ¢NµO EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a24 ¢NµP EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a25 ¢NµQ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a26 ¢NµR EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a27 ¢NµS EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a28 ¢NµT EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a29 ¢NµU EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a2a ¢NµV EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a2b ¢NµW EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a2c ¢NµX EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a2d ¢NµY EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a2e ¢NµZ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a2f ¢OµA EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a30 ¢OµB EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a31 ¢OµC EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a32 ¢OµD EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a33 ¢OµE EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a34 ¢OµF EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a35 ¢OµG EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a36 ¢OµH EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a37 ¢OµI EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a38 ¢OµJ EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a39 ¢OµK EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a3a ¢OµL EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a3b ¢OµM EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a3c ¢OµN EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a3d ¢OµO EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a3e ¢OµP EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a3f ¢OµQ EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a40 ¢OµR EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a41 ¢OµS EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a42 ¢OµT EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a43 ¢OµU EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a44 ¢OµV EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a45 ¢OµW EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a46 ¢OµX EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a47 ¢OµY EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a48 ¢OµZ EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a49 ¢PµA EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a4a ¢PµB EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a4b ¢PµC EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a4c ¢PµD EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a4d ¢PµE EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a4e ¢PµF EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a4f ¢PµG EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a50 ¢PµH EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a51 ¢PµI EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a52 ¢PµJ EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a53 ¢PµK EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a54 ¢PµL EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a55 ¢PµM EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a56 ¢PµN EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a57 ¢PµO EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a58 ¢PµP EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a59 ¢PµQ EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a5a ¢PµR EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a5b ¢PµS EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a5c ¢PµT EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a5d ¢PµU EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a5e ¢PµV EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a5f ¢PµW EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a60 ¢PµX EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a61 ¢PµY EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a62 ¢PµZ EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a63 ¢QµA EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a64 ¢QµB EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a65 ¢QµC EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a66 ¢QµD EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a67 ¢QµE EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a68 ¢QµF EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a69 ¢QµG EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a6a ¢QµH EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a6b ¢QµI EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a6c ¢QµJ EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a6d ¢QµK EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a6e ¢QµL EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a6f ¢QµM EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a70 ¢QµN EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a71 ¢QµO EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a72 ¢QµP EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a73 ¢QµQ EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a74 ¢QµR EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a75 ¢QµS EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a76 ¢QµT EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a77 ¢QµU EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a78 ¢QµV EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a79 ¢QµW EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a7a ¢QµX EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a7b ¢QµY EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a7c ¢QµZ EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a7d ¢RµA EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a7e ¢RµB EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a7f ¢RµC EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a80 ¢RµD EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a81 ¢RµE EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a82 ¢RµF EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a83 ¢RµG EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a84 ¢RµH EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a85 ¢RµI EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a86 ¢RµJ EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a87 ¢RµK EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a88 ¢RµL EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a89 ¢RµM EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a8a ¢RµN EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a8b ¢RµO EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a8c ¢RµP EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a8d ¢RµQ EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a8e ¢RµR EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a8f ¢RµS EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a90 ¢RµT EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a91 ¢RµU EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a92 ¢RµV EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a93 ¢RµW EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a94 ¢RµX EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a95 ¢RµY EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a96 ¢RµZ EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a97 ¢SµA EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a98 ¢SµB EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a99 ¢SµC EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a9a ¢SµD EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a9b ¢SµE EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a9c ¢SµF EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a9d ¢SµG EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a9e ¢SµH EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a9f ¢SµI EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aa0 ¢SµJ EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aa1 ¢SµK EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aa2 ¢SµL EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aa3 ¢SµM EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aa4 ¢SµN EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aa5 ¢SµO EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aa6 ¢SµP EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aa7 ¢SµQ EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aa8 ¢SµR EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aa9 ¢SµS EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aaa ¢SµT EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aab ¢SµU EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aac ¢SµV EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aad ¢SµW EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aae ¢SµX EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aaf ¢SµY EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ab0 ¢SµZ EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ab1 ¢TµA EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ab2 ¢TµB EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ab3 ¢TµC EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ab4 ¢TµD EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ab5 ¢TµE EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ab6 ¢TµF EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ab7 ¢TµG EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ab8 ¢TµH EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ab9 ¢TµI EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aba ¢TµJ EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6abb ¢TµK EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6abc ¢TµL EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6abd ¢TµM EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6abe ¢TµN EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6abf ¢TµO EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ac0 ¢TµP EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ac1 ¢TµQ EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ac2 ¢TµR EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ac3 ¢TµS EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ac4 ¢TµT EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ac5 ¢TµU EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ac6 ¢TµV EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ac7 ¢TµW EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ac8 ¢TµX EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ac9 ¢TµY EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aca ¢TµZ EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6acb ¢UµA EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6acc ¢UµB EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6acd ¢UµC EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ace ¢UµD EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6acf ¢UµE EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ad0 ¢UµF EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ad1 ¢UµG EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ad2 ¢UµH EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ad3 ¢UµI EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ad4 ¢UµJ EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ad5 ¢UµK EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ad6 ¢UµL EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ad7 ¢UµM EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ad8 ¢UµN EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ad9 ¢UµO EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ada ¢UµP EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6adb ¢UµQ EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6adc ¢UµR EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6add ¢UµS EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ade ¢UµT EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6adf ¢UµU EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ae0 ¢UµV EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ae1 ¢UµW EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ae2 ¢UµX EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ae3 ¢UµY EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ae4 ¢UµZ EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ae5 ¢VµA EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ae6 ¢VµB EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ae7 ¢VµC EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ae8 ¢VµD EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ae9 ¢VµE EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aea ¢VµF EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aeb ¢VµG EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aec ¢VµH EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aed ¢VµI EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aee ¢VµJ EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aef ¢VµK EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6af0 ¢VµL EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6af1 ¢VµM EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6af2 ¢VµN EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6af3 ¢VµO EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6af4 ¢VµP EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6af5 ¢VµQ EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6af6 ¢VµR EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6af7 ¢VµS EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6af8 ¢VµT EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6af9 ¢VµU EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6afa ¢VµV EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6afb ¢VµW EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6afc ¢VµX EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6afd ¢VµY EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6afe ¢VµZ EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aff ¢WµA EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6b00 ¢AµA EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b01 ¢AµB EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b02 ¢AµC EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b03 ¢AµD EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b04 ¢AµE EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b05 ¢AµF EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b06 ¢AµG EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b07 ¢AµH EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b08 ¢AµI EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b09 ¢AµJ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b0a ¢AµK EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b0b ¢AµL EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b0c ¢AµM EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b0d ¢AµN EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b0e ¢AµO EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b0f ¢AµP EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b10 ¢AµQ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b11 ¢AµR EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b12 ¢AµS EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b13 ¢AµT EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b14 ¢AµU EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b15 ¢AµV EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b16 ¢AµW EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b17 ¢AµX EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b18 ¢AµY EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b19 ¢AµZ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b1a ¢BµA EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b1b ¢BµB EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b1c ¢BµC EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b1d ¢BµD EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b1e ¢BµE EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b1f ¢BµF EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b20 ¢BµG EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b21 ¢BµH EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b22 ¢BµI EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b23 ¢BµJ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b24 ¢BµK EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b25 ¢BµL EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b26 ¢BµM EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b27 ¢BµN EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b28 ¢BµO EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b29 ¢BµP EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b2a ¢BµQ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b2b ¢BµR EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b2c ¢BµS EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b2d ¢BµT EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b2e ¢BµU EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b2f ¢BµV EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b30 ¢BµW EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b31 ¢BµX EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b32 ¢BµY EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b33 ¢BµZ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b34 ¢CµA EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b35 ¢CµB EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b36 ¢CµC EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b37 ¢CµD EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b38 ¢CµE EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b39 ¢CµF EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b3a ¢CµG EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b3b ¢CµH EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b3c ¢CµI EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b3d ¢CµJ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b3e ¢CµK EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b3f ¢CµL EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b40 ¢CµM EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b41 ¢CµN EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b42 ¢CµO EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b43 ¢CµP EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b44 ¢CµQ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b45 ¢CµR EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b46 ¢CµS EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b47 ¢CµT EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b48 ¢CµU EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b49 ¢CµV EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b4a ¢CµW EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b4b ¢CµX EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b4c ¢CµY EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b4d ¢CµZ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b4e ¢DµA EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b4f ¢DµB EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b50 ¢DµC EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b51 ¢DµD EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b52 ¢DµE EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b53 ¢DµF EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b54 ¢DµG EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b55 ¢DµH EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b56 ¢DµI EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b57 ¢DµJ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b58 ¢DµK EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b59 ¢DµL EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b5a ¢DµM EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b5b ¢DµN EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b5c ¢DµO EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b5d ¢DµP EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b5e ¢DµQ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b5f ¢DµR EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b60 ¢DµS EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b61 ¢DµT EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b62 ¢DµU EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b63 ¢DµV EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b64 ¢DµW EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b65 ¢DµX EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b66 ¢DµY EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b67 ¢DµZ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b68 ¢EµA EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b69 ¢EµB EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b6a ¢EµC EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b6b ¢EµD EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b6c ¢EµE EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b6d ¢EµF EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b6e ¢EµG EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b6f ¢EµH EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b70 ¢EµI EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b71 ¢EµJ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b72 ¢EµK EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b73 ¢EµL EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b74 ¢EµM EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b75 ¢EµN EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b76 ¢EµO EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b77 ¢EµP EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b78 ¢EµQ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b79 ¢EµR EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b7a ¢EµS EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b7b ¢EµT EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b7c ¢EµU EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b7d ¢EµV EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b7e ¢EµW EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b7f ¢EµX EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b80 ¢EµY EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b81 ¢EµZ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b82 ¢FµA EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b83 ¢FµB EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b84 ¢FµC EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b85 ¢FµD EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b86 ¢FµE EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b87 ¢FµF EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b88 ¢FµG EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b89 ¢FµH EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b8a ¢FµI EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b8b ¢FµJ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b8c ¢FµK EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b8d ¢FµL EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b8e ¢FµM EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b8f ¢FµN EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b90 ¢FµO EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b91 ¢FµP EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b92 ¢FµQ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b93 ¢FµR EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b94 ¢FµS EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b95 ¢FµT EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b96 ¢FµU EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b97 ¢FµV EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b98 ¢FµW EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b99 ¢FµX EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b9a ¢FµY EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b9b ¢FµZ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b9c ¢GµA EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b9d ¢GµB EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b9e ¢GµC EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b9f ¢GµD EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ba0 ¢GµE EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ba1 ¢GµF EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ba2 ¢GµG EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ba3 ¢GµH EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ba4 ¢GµI EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ba5 ¢GµJ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ba6 ¢GµK EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ba7 ¢GµL EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ba8 ¢GµM EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ba9 ¢GµN EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6baa ¢GµO EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bab ¢GµP EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bac ¢GµQ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bad ¢GµR EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bae ¢GµS EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6baf ¢GµT EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bb0 ¢GµU EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bb1 ¢GµV EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bb2 ¢GµW EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bb3 ¢GµX EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bb4 ¢GµY EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bb5 ¢GµZ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bb6 ¢HµA EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bb7 ¢HµB EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bb8 ¢HµC EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bb9 ¢HµD EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bba ¢HµE EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bbb ¢HµF EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bbc ¢HµG EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bbd ¢HµH EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bbe ¢HµI EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bbf ¢HµJ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bc0 ¢HµK EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bc1 ¢HµL EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bc2 ¢HµM EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bc3 ¢HµN EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bc4 ¢HµO EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bc5 ¢HµP EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bc6 ¢HµQ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bc7 ¢HµR EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bc8 ¢HµS EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bc9 ¢HµT EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bca ¢HµU EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bcb ¢HµV EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bcc ¢HµW EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bcd ¢HµX EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bce ¢HµY EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bcf ¢HµZ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bd0 ¢IµA EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bd1 ¢IµB EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bd2 ¢IµC EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bd3 ¢IµD EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bd4 ¢IµE EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bd5 ¢IµF EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bd6 ¢IµG EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bd7 ¢IµH EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bd8 ¢IµI EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bd9 ¢IµJ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bda ¢IµK EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bdb ¢IµL EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bdc ¢IµM EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bdd ¢IµN EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bde ¢IµO EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bdf ¢IµP EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6be0 ¢IµQ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6be1 ¢IµR EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6be2 ¢IµS EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6be3 ¢IµT EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6be4 ¢IµU EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6be5 ¢IµV EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6be6 ¢IµW EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6be7 ¢IµX EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6be8 ¢IµY EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6be9 ¢IµZ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bea ¢JµA EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6beb ¢JµB EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bec ¢JµC EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bed ¢JµD EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bee ¢JµE EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bef ¢JµF EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bf0 ¢JµG EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bf1 ¢JµH EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bf2 ¢JµI EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bf3 ¢JµJ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bf4 ¢JµK EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bf5 ¢JµL EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bf6 ¢JµM EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bf7 ¢JµN EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bf8 ¢JµO EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bf9 ¢JµP EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bfa ¢JµQ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bfb ¢JµR EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bfc ¢JµS EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bfd ¢JµT EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bfe ¢JµU EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bff ¢JµV EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c00 ¢JµW EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c01 ¢JµX EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c02 ¢JµY EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c03 ¢JµZ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c04 ¢KµA EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c05 ¢KµB EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c06 ¢KµC EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c07 ¢KµD EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c08 ¢KµE EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c09 ¢KµF EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c0a ¢KµG EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c0b ¢KµH EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c0c ¢KµI EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c0d ¢KµJ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c0e ¢KµK EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c0f ¢KµL EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c10 ¢KµM EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c11 ¢KµN EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c12 ¢KµO EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c13 ¢KµP EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c14 ¢KµQ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c15 ¢KµR EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c16 ¢KµS EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c17 ¢KµT EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c18 ¢KµU EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c19 ¢KµV EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c1a ¢KµW EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c1b ¢KµX EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c1c ¢KµY EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c1d ¢KµZ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c1e ¢LµA EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c1f ¢LµB EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c20 ¢LµC EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c21 ¢LµD EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c22 ¢LµE EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c23 ¢LµF EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c24 ¢LµG EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c25 ¢LµH EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c26 ¢LµI EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c27 ¢LµJ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c28 ¢LµK EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c29 ¢LµL EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c2a ¢LµM EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c2b ¢LµN EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c2c ¢LµO EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c2d ¢LµP EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c2e ¢LµQ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c2f ¢LµR EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c30 ¢LµS EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c31 ¢LµT EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c32 ¢LµU EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c33 ¢LµV EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c34 ¢LµW EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c35 ¢LµX EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c36 ¢LµY EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c37 ¢LµZ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c38 ¢MµA EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c39 ¢MµB EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c3a ¢MµC EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c3b ¢MµD EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c3c ¢MµE EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c3d ¢MµF EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c3e ¢MµG EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c3f ¢MµH EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c40 ¢MµI EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c41 ¢MµJ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c42 ¢MµK EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c43 ¢MµL EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c44 ¢MµM EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c45 ¢MµN EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c46 ¢MµO EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c47 ¢MµP EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c48 ¢MµQ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c49 ¢MµR EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c4a ¢MµS EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c4b ¢MµT EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c4c ¢MµU EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c4d ¢MµV EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c4e ¢MµW EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c4f ¢MµX EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c50 ¢MµY EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c51 ¢MµZ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c52 ¢NµA EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c53 ¢NµB EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c54 ¢NµC EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c55 ¢NµD EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c56 ¢NµE EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c57 ¢NµF EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c58 ¢NµG EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c59 ¢NµH EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c5a ¢NµI EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c5b ¢NµJ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c5c ¢NµK EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c5d ¢NµL EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c5e ¢NµM EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c5f ¢NµN EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c60 ¢NµO EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c61 ¢NµP EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c62 ¢NµQ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c63 ¢NµR EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c64 ¢NµS EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c65 ¢NµT EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c66 ¢NµU EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c67 ¢NµV EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c68 ¢NµW EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c69 ¢NµX EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c6a ¢NµY EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c6b ¢NµZ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c6c ¢OµA EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c6d ¢OµB EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c6e ¢OµC EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c6f ¢OµD EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c70 ¢OµE EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c71 ¢OµF EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c72 ¢OµG EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c73 ¢OµH EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c74 ¢OµI EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c75 ¢OµJ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c76 ¢OµK EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c77 ¢OµL EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c78 ¢OµM EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c79 ¢OµN EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c7a ¢OµO EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c7b ¢OµP EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c7c ¢OµQ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c7d ¢OµR EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c7e ¢OµS EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c7f ¢OµT EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c80 ¢OµU EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c81 ¢OµV EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c82 ¢OµW EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c83 ¢OµX EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c84 ¢OµY EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c85 ¢OµZ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c86 ¢PµA EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c87 ¢PµB EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c88 ¢PµC EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c89 ¢PµD EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c8a ¢PµE EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c8b ¢PµF EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c8c ¢PµG EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c8d ¢PµH EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c8e ¢PµI EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c8f ¢PµJ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c90 ¢PµK EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c91 ¢PµL EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c92 ¢PµM EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c93 ¢PµN EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c94 ¢PµO EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c95 ¢PµP EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c96 ¢PµQ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c97 ¢PµR EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c98 ¢PµS EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c99 ¢PµT EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c9a ¢PµU EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c9b ¢PµV EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c9c ¢PµW EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c9d ¢PµX EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c9e ¢PµY EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c9f ¢PµZ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ca0 ¢QµA EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ca1 ¢QµB EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ca2 ¢QµC EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ca3 ¢QµD EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ca4 ¢QµE EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ca5 ¢QµF EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ca6 ¢QµG EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ca7 ¢QµH EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ca8 ¢QµI EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ca9 ¢QµJ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6caa ¢QµK EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cab ¢QµL EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cac ¢QµM EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cad ¢QµN EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cae ¢QµO EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6caf ¢QµP EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cb0 ¢QµQ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cb1 ¢QµR EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cb2 ¢QµS EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cb3 ¢QµT EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cb4 ¢QµU EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cb5 ¢QµV EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cb6 ¢QµW EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cb7 ¢QµX EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cb8 ¢QµY EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cb9 ¢QµZ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cba ¢RµA EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cbb ¢RµB EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cbc ¢RµC EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cbd ¢RµD EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cbe ¢RµE EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cbf ¢RµF EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cc0 ¢RµG EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cc1 ¢RµH EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cc2 ¢RµI EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cc3 ¢RµJ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cc4 ¢RµK EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cc5 ¢RµL EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cc6 ¢RµM EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cc7 ¢RµN EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cc8 ¢RµO EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cc9 ¢RµP EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cca ¢RµQ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ccb ¢RµR EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ccc ¢RµS EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ccd ¢RµT EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cce ¢RµU EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ccf ¢RµV EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cd0 ¢RµW EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cd1 ¢RµX EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cd2 ¢RµY EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cd3 ¢RµZ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cd4 ¢SµA EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cd5 ¢SµB EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cd6 ¢SµC EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cd7 ¢SµD EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cd8 ¢SµE EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cd9 ¢SµF EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cda ¢SµG EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cdb ¢SµH EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cdc ¢SµI EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cdd ¢SµJ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cde ¢SµK EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cdf ¢SµL EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ce0 ¢SµM EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ce1 ¢SµN EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ce2 ¢SµO EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ce3 ¢SµP EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ce4 ¢SµQ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ce5 ¢SµR EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ce6 ¢SµS EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ce7 ¢SµT EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ce8 ¢SµU EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ce9 ¢SµV EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cea ¢SµW EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ceb ¢SµX EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cec ¢SµY EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ced ¢SµZ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cee ¢TµA EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cef ¢TµB EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cf0 ¢TµC EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cf1 ¢TµD EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cf2 ¢TµE EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cf3 ¢TµF EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cf4 ¢TµG EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cf5 ¢TµH EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cf6 ¢TµI EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cf7 ¢TµJ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cf8 ¢TµK EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cf9 ¢TµL EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cfa ¢TµM EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cfb ¢TµN EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cfc ¢TµO EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cfd ¢TµP EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cfe ¢TµQ EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cff ¢TµR EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6d00 ¢TµS EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6d01 ¢TµT EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d02 ¢TµU EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d03 ¢TµV EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d04 ¢TµW EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d05 ¢TµX EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d06 ¢TµY EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d07 ¢TµZ EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d08 ¢UµA EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d09 ¢UµB EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d0a ¢UµC EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d0b ¢UµD EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d0c ¢UµE EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d0d ¢UµF EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d0e ¢UµG EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d0f ¢UµH EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d10 ¢UµI EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d11 ¢UµJ EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d12 ¢UµK EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d13 ¢UµL EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d14 ¢UµM EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d15 ¢UµN EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d16 ¢UµO EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d17 ¢UµP EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d18 ¢UµQ EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d19 ¢UµR EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d1a ¢UµS EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d1b ¢UµT EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d1c ¢UµU EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d1d ¢UµV EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d1e ¢UµW EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d1f ¢UµX EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d20 ¢UµY EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d21 ¢UµZ EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d22 ¢VµA EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d23 ¢VµB EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d24 ¢VµC EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d25 ¢VµD EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d26 ¢VµE EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d27 ¢VµF EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d28 ¢VµG EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d29 ¢VµH EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d2a ¢VµI EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d2b ¢VµJ EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d2c ¢VµK EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d2d ¢VµL EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d2e ¢VµM EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d2f ¢VµN EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d30 ¢VµO EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d31 ¢VµP EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d32 ¢VµQ EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d33 ¢VµR EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d34 ¢VµS EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d35 ¢VµT EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d36 ¢VµU EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d37 ¢VµV EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d38 ¢VµW EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d39 ¢VµX EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d3a ¢VµY EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d3b ¢VµZ EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d3c ¢WµA EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d3d ¢A¢A EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d3e ¢A¢B EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d3f ¢A¢C EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d40 ¢A¢D EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d41 ¢A¢E EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d42 ¢A¢F EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d43 ¢A¢G EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d44 ¢A¢H EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d45 ¢A¢I EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d46 ¢A¢J EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d47 ¢A¢K EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d48 ¢A¢L EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d49 ¢A¢M EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d4a ¢A¢N EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d4b ¢A¢O EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d4c ¢A¢P EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d4d ¢A¢Q EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d4e ¢A¢R EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d4f ¢A¢S EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d50 ¢A¢T EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d51 ¢A¢U EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d52 ¢A¢V EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d53 ¢A¢W EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d54 ¢A¢X EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d55 ¢A¢Y EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d56 ¢A¢Z EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d57 ¢B¢A EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d58 ¢B¢B EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d59 ¢B¢C EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d5a ¢B¢D EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d5b ¢B¢E EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d5c ¢B¢F EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d5d ¢B¢G EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d5e ¢B¢H EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d5f ¢B¢I EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d60 ¢B¢J EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d61 ¢B¢K EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d62 ¢B¢L EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d63 ¢B¢M EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d64 ¢B¢N EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d65 ¢B¢O EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d66 ¢B¢P EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d67 ¢B¢Q EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d68 ¢B¢R EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d69 ¢B¢S EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d6a ¢B¢T EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d6b ¢B¢U EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d6c ¢B¢V EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d6d ¢B¢W EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d6e ¢B¢X EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d6f ¢B¢Y EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d70 ¢B¢Z EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d71 ¢C¢A EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d72 ¢C¢B EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d73 ¢C¢C EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d74 ¢C¢D EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d75 ¢C¢E EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d76 ¢C¢F EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d77 ¢C¢G EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d78 ¢C¢H EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d79 ¢C¢I EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d7a ¢C¢J EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d7b ¢C¢K EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d7c ¢C¢L EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d7d ¢C¢M EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d7e ¢C¢N EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d7f ¢C¢O EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d80 ¢C¢P EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d81 ¢C¢Q EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d82 ¢C¢R EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d83 ¢C¢S EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d84 ¢C¢T EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d85 ¢C¢U EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d86 ¢C¢V EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d87 ¢C¢W EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d88 ¢C¢X EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d89 ¢C¢Y EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d8a ¢C¢Z EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d8b ¢D¢A EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d8c ¢D¢B EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d8d ¢D¢C EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d8e ¢D¢D EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d8f ¢D¢E EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d90 ¢D¢F EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d91 ¢D¢G EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d92 ¢D¢H EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d93 ¢D¢I EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d94 ¢D¢J EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d95 ¢D¢K EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d96 ¢D¢L EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d97 ¢D¢M EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d98 ¢D¢N EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d99 ¢D¢O EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d9a ¢D¢P EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d9b ¢D¢Q EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d9c ¢D¢R EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d9d ¢D¢S EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d9e ¢D¢T EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d9f ¢D¢U EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6da0 ¢D¢V EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6da1 ¢D¢W EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6da2 ¢D¢X EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6da3 ¢D¢Y EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6da4 ¢D¢Z EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6da5 ¢E¢A EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6da6 ¢E¢B EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6da7 ¢E¢C EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6da8 ¢E¢D EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6da9 ¢E¢E EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6daa ¢E¢F EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6dab ¢E¢G EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6dac ¢E¢H EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6dad ¢E¢I EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6dae ¢E¢J EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6daf ¢E¢K EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6db0 ¢E¢L EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6db1 ¢E¢M EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6db2 ¢E¢N EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6db3 ¢E¢O EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6db4 ¢E¢P EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6db5 ¢E¢Q EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6db6 ¢E¢R EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6db7 ¢E¢S EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6db8 ¢E¢T EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6db9 ¢E¢U EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6dba ¢E¢V EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6dbb ¢E¢W EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6dbc ¢E¢X EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6dbd ¢E¢Y EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6dbe ¢E¢Z EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6dbf ¢F¢A EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6dc0 ¢F¢B EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6dc1 ¢F¢C EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6dc2 ¢F¢D EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6dc3 ¢AµA EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dc4 ¢AµB EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dc5 ¢AµC EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dc6 ¢AµD EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dc7 ¢AµE EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dc8 ¢AµF EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dc9 ¢AµG EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dca ¢AµH EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dcb ¢AµI EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dcc ¢AµJ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dcd ¢AµK EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dce ¢AµL EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dcf ¢AµM EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dd0 ¢AµN EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dd1 ¢AµO EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dd2 ¢AµP EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dd3 ¢AµQ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dd4 ¢AµR EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dd5 ¢AµS EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dd6 ¢AµT EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dd7 ¢AµU EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dd8 ¢AµV EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dd9 ¢AµW EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dda ¢AµX EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ddb ¢AµY EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ddc ¢AµZ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ddd ¢BµA EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dde ¢BµB EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ddf ¢BµC EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6de0 ¢BµD EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6de1 ¢BµE EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6de2 ¢BµF EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6de3 ¢BµG EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6de4 ¢BµH EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6de5 ¢BµI EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6de6 ¢BµJ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6de7 ¢BµK EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6de8 ¢BµL EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6de9 ¢BµM EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dea ¢BµN EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6deb ¢BµO EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dec ¢BµP EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ded ¢BµQ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dee ¢BµR EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6def ¢BµS EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6df0 ¢BµT EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6df1 ¢BµU EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6df2 ¢BµV EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6df3 ¢BµW EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6df4 ¢BµX EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6df5 ¢BµY EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6df6 ¢BµZ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6df7 ¢CµA EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6df8 ¢CµB EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6df9 ¢CµC EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dfa ¢CµD EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dfb ¢CµE EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dfc ¢CµF EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dfd ¢CµG EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dfe ¢CµH EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dff ¢CµI EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e00 ¢CµJ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e01 ¢CµK EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e02 ¢CµL EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e03 ¢CµM EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e04 ¢CµN EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e05 ¢CµO EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e06 ¢CµP EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e07 ¢CµQ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e08 ¢CµR EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e09 ¢CµS EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e0a ¢CµT EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e0b ¢CµU EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e0c ¢CµV EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e0d ¢CµW EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e0e ¢CµX EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e0f ¢CµY EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e10 ¢CµZ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e11 ¢DµA EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e12 ¢DµB EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e13 ¢DµC EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e14 ¢DµD EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e15 ¢DµE EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e16 ¢DµF EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e17 ¢DµG EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e18 ¢DµH EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e19 ¢DµI EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e1a ¢DµJ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e1b ¢DµK EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e1c ¢DµL EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e1d ¢DµM EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e1e ¢DµN EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e1f ¢DµO EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e20 ¢DµP EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e21 ¢DµQ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e22 ¢DµR EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e23 ¢DµS EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e24 ¢DµT EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e25 ¢DµU EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e26 ¢DµV EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e27 ¢DµW EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e28 ¢DµX EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e29 ¢DµY EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e2a ¢DµZ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e2b ¢EµA EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e2c ¢EµB EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e2d ¢EµC EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e2e ¢EµD EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e2f ¢EµE EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e30 ¢EµF EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e31 ¢EµG EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e32 ¢EµH EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e33 ¢EµI EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e34 ¢EµJ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e35 ¢EµK EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e36 ¢EµL EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e37 ¢EµM EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e38 ¢EµN EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e39 ¢EµO EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e3a ¢EµP EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e3b ¢EµQ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e3c ¢EµR EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e3d ¢EµS EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e3e ¢EµT EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e3f ¢EµU EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e40 ¢EµV EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e41 ¢EµW EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e42 ¢EµX EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e43 ¢EµY EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e44 ¢EµZ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e45 ¢FµA EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e46 ¢FµB EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e47 ¢FµC EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e48 ¢FµD EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e49 ¢FµE EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e4a ¢FµF EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e4b ¢FµG EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e4c ¢FµH EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e4d ¢FµI EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e4e ¢FµJ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e4f ¢FµK EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e50 ¢FµL EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e51 ¢FµM EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e52 ¢FµN EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e53 ¢FµO EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e54 ¢FµP EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e55 ¢FµQ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e56 ¢FµR EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e57 ¢FµS EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e58 ¢FµT EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e59 ¢FµU EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e5a ¢FµV EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e5b ¢FµW EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e5c ¢FµX EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e5d ¢FµY EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e5e ¢FµZ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e5f ¢GµA EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e60 ¢GµB EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e61 ¢GµC EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e62 ¢GµD EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e63 ¢GµE EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e64 ¢GµF EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e65 ¢GµG EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e66 ¢GµH EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e67 ¢GµI EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e68 ¢GµJ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e69 ¢GµK EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e6a ¢GµL EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e6b ¢GµM EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e6c ¢GµN EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e6d ¢GµO EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e6e ¢GµP EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e6f ¢GµQ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e70 ¢GµR EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e71 ¢GµS EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e72 ¢GµT EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e73 ¢GµU EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e74 ¢GµV EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e75 ¢GµW EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e76 ¢GµX EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e77 ¢GµY EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e78 ¢GµZ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e79 ¢HµA EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e7a ¢HµB EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e7b ¢HµC EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e7c ¢HµD EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e7d ¢HµE EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e7e ¢HµF EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e7f ¢HµG EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e80 ¢HµH EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e81 ¢HµI EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e82 ¢HµJ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e83 ¢HµK EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e84 ¢HµL EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e85 ¢HµM EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e86 ¢HµN EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e87 ¢HµO EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e88 ¢HµP EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e89 ¢HµQ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e8a ¢HµR EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e8b ¢HµS EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e8c ¢HµT EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e8d ¢HµU EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e8e ¢HµV EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e8f ¢HµW EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e90 ¢HµX EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e91 ¢HµY EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e92 ¢HµZ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e93 ¢IµA EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e94 ¢IµB EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e95 ¢IµC EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e96 ¢IµD EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e97 ¢IµE EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e98 ¢IµF EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e99 ¢IµG EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e9a ¢IµH EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e9b ¢IµI EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e9c ¢IµJ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e9d ¢IµK EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e9e ¢IµL EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e9f ¢IµM EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ea0 ¢IµN EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ea1 ¢IµO EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ea2 ¢IµP EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ea3 ¢IµQ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ea4 ¢IµR EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ea5 ¢IµS EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ea6 ¢IµT EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ea7 ¢IµU EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ea8 ¢IµV EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ea9 ¢IµW EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eaa ¢IµX EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eab ¢IµY EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eac ¢IµZ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ead ¢JµA EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eae ¢JµB EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eaf ¢JµC EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eb0 ¢JµD EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eb1 ¢JµE EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eb2 ¢JµF EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eb3 ¢JµG EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eb4 ¢JµH EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eb5 ¢JµI EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eb6 ¢JµJ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eb7 ¢JµK EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eb8 ¢JµL EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eb9 ¢JµM EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eba ¢JµN EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ebb ¢JµO EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ebc ¢JµP EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ebd ¢JµQ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ebe ¢JµR EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ebf ¢JµS EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ec0 ¢JµT EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ec1 ¢JµU EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ec2 ¢JµV EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ec3 ¢JµW EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ec4 ¢JµX EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ec5 ¢JµY EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ec6 ¢JµZ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ec7 ¢KµA EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ec8 ¢KµB EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ec9 ¢KµC EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eca ¢KµD EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ecb ¢KµE EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ecc ¢KµF EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ecd ¢KµG EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ece ¢KµH EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ecf ¢KµI EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ed0 ¢KµJ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ed1 ¢KµK EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ed2 ¢KµL EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ed3 ¢KµM EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ed4 ¢KµN EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ed5 ¢KµO EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ed6 ¢KµP EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ed7 ¢KµQ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ed8 ¢KµR EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ed9 ¢KµS EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eda ¢KµT EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6edb ¢KµU EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6edc ¢KµV EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6edd ¢KµW EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ede ¢KµX EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6edf ¢KµY EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ee0 ¢KµZ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ee1 ¢LµA EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ee2 ¢LµB EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ee3 ¢LµC EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ee4 ¢LµD EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ee5 ¢LµE EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ee6 ¢LµF EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ee7 ¢LµG EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ee8 ¢LµH EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ee9 ¢LµI EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eea ¢LµJ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eeb ¢LµK EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eec ¢LµL EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eed ¢LµM EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eee ¢LµN EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eef ¢LµO EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ef0 ¢LµP EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ef1 ¢LµQ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ef2 ¢LµR EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ef3 ¢LµS EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ef4 ¢LµT EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ef5 ¢LµU EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ef6 ¢LµV EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ef7 ¢LµW EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ef8 ¢LµX EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ef9 ¢LµY EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6efa ¢LµZ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6efb ¢MµA EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6efc ¢MµB EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6efd ¢MµC EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6efe ¢MµD EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eff ¢MµE EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f00 ¢MµF EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f01 ¢MµG EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f02 ¢MµH EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f03 ¢MµI EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f04 ¢MµJ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f05 ¢MµK EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f06 ¢MµL EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f07 ¢MµM EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f08 ¢MµN EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f09 ¢MµO EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f0a ¢MµP EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f0b ¢MµQ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f0c ¢MµR EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f0d ¢MµS EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f0e ¢MµT EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f0f ¢MµU EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f10 ¢MµV EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f11 ¢MµW EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f12 ¢MµX EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f13 ¢MµY EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f14 ¢MµZ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f15 ¢NµA EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f16 ¢NµB EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f17 ¢NµC EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f18 ¢NµD EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f19 ¢NµE EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f1a ¢NµF EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f1b ¢NµG EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f1c ¢NµH EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f1d ¢NµI EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f1e ¢NµJ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f1f ¢NµK EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f20 ¢NµL EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f21 ¢NµM EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f22 ¢NµN EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f23 ¢NµO EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f24 ¢NµP EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f25 ¢NµQ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f26 ¢NµR EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f27 ¢NµS EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f28 ¢NµT EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f29 ¢NµU EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f2a ¢NµV EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f2b ¢NµW EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f2c ¢NµX EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f2d ¢NµY EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f2e ¢NµZ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f2f ¢OµA EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f30 ¢OµB EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f31 ¢OµC EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f32 ¢OµD EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f33 ¢OµE EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f34 ¢OµF EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f35 ¢OµG EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f36 ¢OµH EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f37 ¢OµI EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f38 ¢OµJ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f39 ¢OµK EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f3a ¢OµL EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f3b ¢OµM EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f3c ¢OµN EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f3d ¢OµO EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f3e ¢OµP EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f3f ¢OµQ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f40 ¢OµR EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f41 ¢OµS EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f42 ¢OµT EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f43 ¢OµU EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f44 ¢OµV EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f45 ¢OµW EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f46 ¢OµX EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f47 ¢OµY EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f48 ¢OµZ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f49 ¢PµA EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f4a ¢PµB EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f4b ¢PµC EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f4c ¢PµD EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f4d ¢PµE EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f4e ¢PµF EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f4f ¢PµG EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f50 ¢PµH EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f51 ¢PµI EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f52 ¢PµJ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f53 ¢PµK EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f54 ¢PµL EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f55 ¢PµM EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f56 ¢PµN EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f57 ¢PµO EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f58 ¢PµP EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f59 ¢PµQ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f5a ¢PµR EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f5b ¢PµS EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f5c ¢PµT EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f5d ¢PµU EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f5e ¢PµV EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f5f ¢PµW EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f60 ¢PµX EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f61 ¢PµY EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f62 ¢PµZ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f63 ¢QµA EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f64 ¢QµB EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f65 ¢QµC EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f66 ¢QµD EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f67 ¢QµE EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f68 ¢QµF EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f69 ¢QµG EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f6a ¢QµH EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f6b ¢QµI EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f6c ¢QµJ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f6d ¢QµK EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f6e ¢QµL EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f6f ¢QµM EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f70 ¢QµN EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f71 ¢QµO EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f72 ¢QµP EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f73 ¢QµQ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f74 ¢QµR EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f75 ¢QµS EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f76 ¢QµT EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f77 ¢QµU EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f78 ¢QµV EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f79 ¢QµW EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f7a ¢QµX EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f7b ¢QµY EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f7c ¢QµZ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f7d ¢RµA EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f7e ¢RµB EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f7f ¢RµC EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f80 ¢RµD EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f81 ¢RµE EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f82 ¢RµF EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f83 ¢RµG EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f84 ¢RµH EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f85 ¢RµI EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f86 ¢RµJ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f87 ¢RµK EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f88 ¢RµL EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f89 ¢RµM EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f8a ¢RµN EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f8b ¢RµO EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f8c ¢RµP EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f8d ¢RµQ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f8e ¢RµR EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f8f ¢RµS EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f90 ¢RµT EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f91 ¢RµU EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f92 ¢RµV EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f93 ¢RµW EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f94 ¢RµX EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f95 ¢RµY EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f96 ¢RµZ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f97 ¢SµA EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f98 ¢SµB EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f99 ¢SµC EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f9a ¢SµD EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f9b ¢SµE EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f9c ¢SµF EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f9d ¢SµG EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f9e ¢SµH EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f9f ¢SµI EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fa0 ¢SµJ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fa1 ¢SµK EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fa2 ¢SµL EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fa3 ¢SµM EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fa4 ¢SµN EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fa5 ¢SµO EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fa6 ¢SµP EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fa7 ¢SµQ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fa8 ¢SµR EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fa9 ¢SµS EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6faa ¢SµT EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fab ¢SµU EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fac ¢SµV EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fad ¢SµW EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fae ¢SµX EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6faf ¢SµY EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fb0 ¢SµZ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fb1 ¢TµA EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fb2 ¢TµB EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fb3 ¢TµC EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fb4 ¢TµD EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fb5 ¢TµE EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fb6 ¢TµF EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fb7 ¢TµG EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fb8 ¢TµH EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fb9 ¢TµI EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fba ¢TµJ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fbb ¢TµK EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fbc ¢TµL EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fbd ¢TµM EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fbe ¢TµN EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fbf ¢TµO EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fc0 ¢TµP EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fc1 ¢TµQ EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fc2 ¢TµR EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fc3 ¢TµS EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fc4 ¢TµT EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fc5 ¢TµU EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fc6 ¢TµV EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fc7 ¢TµW EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fc8 ¢TµX EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fc9 ¢TµY EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fca ¢TµZ EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fcb ¢UµA EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fcc ¢UµB EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fcd ¢UµC EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fce ¢UµD EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fcf ¢UµE EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fd0 ¢UµF EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fd1 ¢UµG EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fd2 ¢UµH EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fd3 ¢UµI EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fd4 ¢UµJ EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fd5 ¢UµK EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fd6 ¢UµL EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fd7 ¢UµM EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fd8 ¢UµN EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fd9 ¢UµO EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fda ¢UµP EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fdb ¢UµQ EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fdc ¢UµR EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fdd ¢UµS EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fde ¢UµT EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fdf ¢UµU EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fe0 ¢UµV EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fe1 ¢UµW EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fe2 ¢UµX EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fe3 ¢UµY EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fe4 ¢UµZ EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fe5 ¢VµA EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fe6 ¢VµB EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fe7 ¢VµC EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fe8 ¢VµD EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fe9 ¢VµE EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fea ¢VµF EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6feb ¢VµG EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fec ¢VµH EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fed ¢VµI EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fee ¢VµJ EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fef ¢VµK EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6ff0 ¢VµL EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6ff1 ¢VµM EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6ff2 ¢VµN EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6ff3 ¢VµO EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6ff4 ¢VµP EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6ff5 ¢VµQ EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6ff6 ¢VµR EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6ff7 ¢VµS EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6ff8 ¢VµT EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6ff9 ¢VµU EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6ffa ¢VµV EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6ffb ¢VµW EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6ffc ¢VµX EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6ffd ¢VµY EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6ffe ¢VµZ EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fff ¢WµA EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0xa001 ¥AA EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa002 ¥AB EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa003 ¥AC EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa004 ¥AD EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa005 ¥AE EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa006 ¥AF EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa007 ¥AG EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa008 ¥AH EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa009 ¥AI EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa00a ¥AJ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa00b ¥AK EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa00c ¥AL EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa00d ¥AM EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa00e ¥AN EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa00f ¥AO EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa010 ¥AP EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa011 ¥AQ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa012 ¥AR EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa013 ¥AS EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa014 ¥AT EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa015 ¥AU EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa016 ¥AV EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa017 ¥AW EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa018 ¥AX EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa019 ¥AY EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa01a ¥AZ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa01b ¥BA EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa01c ¥BB EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa01d ¥BC EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa01e ¥BD EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa01f ¥BE EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa020 ¥BF EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa021 ¥BG EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa022 ¥BH EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa023 ¥BI EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa024 ¥BJ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa025 ¥BK EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa026 ¥BL EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa027 ¥BM EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa028 ¥BN EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa029 ¥BO EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa02a ¥BP EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa02b ¥BQ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa02c ¥BR EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa02d ¥BS EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa02e ¥BT EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa02f ¥BU EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa030 ¥BV EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa031 ¥BW EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa032 ¥BX EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa033 ¥BY EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa034 ¥BZ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa035 ¥CA EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa036 ¥CB EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa037 ¥CC EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa038 ¥CD EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa039 ¥CE EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa03a ¥CF EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa03b ¥CG EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa03c ¥CH EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa03d ¥CI EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa03e ¥CJ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa03f ¥CK EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa040 ¥CL EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa041 ¥CM EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa042 ¥CN EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa043 ¥CO EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa044 ¥CP EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa045 ¥CQ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa046 ¥CR EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa047 ¥CS EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa048 ¥CT EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa049 ¥CU EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa04a ¥CV EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa04b ¥CW EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa04c ¥CX EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa04d ¥CY EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa04e ¥CZ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa04f ¥DA EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa050 ¥DB EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa051 ¥DC EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa052 ¥DD EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa053 ¥DE EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa054 ¥DF EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa055 ¥DG EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa056 ¥DH EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa057 ¥DI EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa058 ¥DJ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa059 ¥DK EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa05a ¥DL EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa05b ¥DM EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa05c ¥DN EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa05d ¥DO EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa05e ¥DP EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa05f ¥DQ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa060 ¥DR EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa061 ¥DS EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa062 ¥DT EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa063 ¥DU EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa064 ¥DV EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa065 ¥DW EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa066 ¥DX EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa067 ¥DY EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa068 ¥DZ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa069 ¥EA EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa06a ¥EB EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa06b ¥EC EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa06c ¥ED EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa06d ¥EE EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa06e ¥EF EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa06f ¥EG EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa070 ¥EH EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa071 ¥EI EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa072 ¥EJ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa073 ¥EK EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa074 ¥EL EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa075 ¥EM EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa076 ¥EN EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa077 ¥EO EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa078 ¥EP EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa079 ¥EQ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa07a ¥ER EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa07b ¥ES EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa07c ¥ET EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa07d ¥EU EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa07e ¥EV EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa07f ¥EW EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa080 ¥EX EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa081 ¥EY EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa082 ¥EZ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa083 ¥FA EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa084 ¥FB EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa085 ¥FC EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa086 ¥FD EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa087 ¥FE EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa088 ¥FF EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa089 ¥FG EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa08a ¥FH EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa08b ¥FI EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa08c ¥FJ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa08d ¥FK EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa08e ¥FL EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa08f ¥FM EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa090 ¥FN EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa091 ¥FO EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa092 ¥FP EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa093 ¥FQ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa094 ¥FR EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa095 ¥FS EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa096 ¥FT EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa097 ¥FU EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa098 ¥FV EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa099 ¥FW EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa09a ¥FX EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa09b ¥FY EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa09c ¥FZ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa09d ¥GA EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa09e ¥GB EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa09f ¥GC EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0a0 ¥GD EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0a1 ¥GE EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0a2 ¥GF EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0a3 ¥GG EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0a4 ¥GH EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0a5 ¥GI EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0a6 ¥GJ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0a7 ¥GK EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0a8 ¥GL EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0a9 ¥GM EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0aa ¥GN EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0ab ¥GO EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0ac ¥GP EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0ad ¥GQ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0ae ¥GR EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0af ¥GS EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0b0 ¥GT EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0b1 ¥GU EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0b2 ¥GV EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0b3 ¥GW EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0b4 ¥GX EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0b5 ¥GY EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0b6 ¥GZ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0b7 ¥HA EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0b8 ¥HB EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0b9 ¥HC EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0ba ¥HD EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0bb ¥HE EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0bc ¥HF EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0bd ¥HG EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0be ¥HH EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0bf ¥HI EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0c0 ¥HJ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0c1 ¥HK EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0c2 ¥HL EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0c3 ¥HM EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0c4 ¥HN EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0c5 ¥HO EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0c6 ¥HP EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0c7 ¥HQ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0c8 ¥HR EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0c9 ¥HS EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0ca ¥HT EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0cb ¥HU EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0cc ¥HV EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0cd ¥HW EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0ce ¥HX EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0cf ¥HY EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0d0 ¥HZ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0d1 ¥IA EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0d2 ¥IB EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0d3 ¥IC EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0d4 ¥ID EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0d5 ¥IE EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0d6 ¥IF EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0d7 ¥IG EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0d8 ¥IH EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0d9 ¥II EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0da ¥IJ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0db ¥IK EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0dc ¥IL EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0dd ¥IM EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0de ¥IN EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0df ¥IO EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0e0 ¥IP EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0e1 ¥IQ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0e2 ¥IR EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0e3 ¥IS EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0e4 ¥IT EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0e5 ¥IU EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0e6 ¥IV EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0e7 ¥IW EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0e8 ¥IX EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0e9 ¥IY EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0ea ¥IZ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0eb ¥JA EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0ec ¥JB EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0ed ¥JC EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0ee ¥JD EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0ef ¥JE EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0f0 ¥JF EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0f1 ¥JG EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0f2 ¥JH EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0f3 ¥JI EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0f4 ¥JJ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0f5 ¥JK EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0f6 ¥JL EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0f7 ¥JM EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0f8 ¥JN EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0f9 ¥JO EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0fa ¥JP EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0fb ¥JQ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0fc ¥JR EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0fd ¥JS EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0fe ¥JT EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0ff ¥JU EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa101 ¥JW EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa102 ¥JX EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa103 ¥JY EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa104 ¥JZ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa105 ¥KA EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa106 ¥KB EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa107 ¥KC EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa108 ¥KD EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa109 ¥KE EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa10a ¥KF EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa10b ¥KG EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa10c ¥KH EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa10d ¥KI EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa10e ¥KJ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa10f ¥KK EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa110 ¥KL EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa111 ¥KM EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa112 ¥KN EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa113 ¥KO EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa114 ¥KP EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa115 ¥KQ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa116 ¥KR EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa117 ¥KS EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa118 ¥KT EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa119 ¥KU EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa11a ¥KV EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa11b ¥KW EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa11c ¥KX EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa11d ¥KY EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa11e ¥KZ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa11f ¥LA EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa120 ¥LB EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa121 ¥LC EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa122 ¥LD EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa123 ¥LE EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa124 ¥LF EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa125 ¥LG EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa126 ¥LH EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa127 ¥LI EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa128 ¥LJ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa129 ¥LK EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa12a ¥LL EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa12b ¥LM EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa12c ¥LN EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa12d ¥LO EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa12e ¥LP EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa12f ¥LQ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa130 ¥LR EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa131 ¥LS EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa132 ¥LT EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa133 ¥LU EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa134 ¥LV EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa135 ¥LW EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa136 ¥LX EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa137 ¥LY EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa138 ¥LZ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa139 ¥MA EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa13a ¥MB EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa13b ¥MC EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa13c ¥MD EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa13d ¥ME EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa13e ¥MF EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa13f ¥MG EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa140 ¥MH EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa141 ¥MI EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa142 ¥MJ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa143 ¥MK EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa144 ¥ML EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa145 ¥MM EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa146 ¥MN EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa147 ¥MO EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa148 ¥MP EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa149 ¥MQ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa14a ¥MR EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa14b ¥MS EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa14c ¥MT EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa14d ¥MU EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa14e ¥MV EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa14f ¥MW EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa150 ¥MX EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa151 ¥MY EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa152 ¥MZ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa153 ¥NA EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa154 ¥NB EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa155 ¥NC EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa156 ¥ND EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa157 ¥NE EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa158 ¥NF EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa159 ¥NG EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa15a ¥NH EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa15b ¥NI EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa15c ¥NJ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa15d ¥NK EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa15e ¥NL EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa15f ¥NM EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa160 ¥NN EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa161 ¥NO EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa162 ¥NP EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa163 ¥NQ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa164 ¥NR EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa165 ¥NS EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa166 ¥NT EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa167 ¥NU EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa168 ¥NV EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa169 ¥NW EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa16a ¥NX EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa16b ¥NY EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa16c ¥NZ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa16d ¥OA EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa16e ¥OB EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa16f ¥OC EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa170 ¥OD EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa171 ¥OE EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa172 ¥OF EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa173 ¥OG EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa174 ¥OH EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa175 ¥OI EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa176 ¥OJ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa177 ¥OK EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa178 ¥OL EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa179 ¥OM EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa17a ¥ON EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa17b ¥OO EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa17c ¥OP EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa17d ¥OQ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa17e ¥OR EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa17f ¥OS EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa180 ¥OT EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa181 ¥OU EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa182 ¥OV EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa183 ¥OW EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa184 ¥OX EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa185 ¥OY EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa186 ¥OZ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa187 ¥PA EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa188 ¥PB EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa189 ¥PC EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa18a ¥PD EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa18b ¥PE EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa18c ¥PF EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa18d ¥PG EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa18e ¥PH EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa18f ¥PI EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa190 ¥PJ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa191 ¥PK EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa192 ¥PL EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa193 ¥PM EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa194 ¥PN EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa195 ¥PO EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa196 ¥PP EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa197 ¥PQ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa198 ¥PR EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa199 ¥PS EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa19a ¥PT EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa19b ¥PU EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa19c ¥PV EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa19d ¥PW EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa19e ¥PX EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa19f ¥PY EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1a0 ¥PZ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1a1 ¥QA EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1a2 ¥QB EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1a3 ¥QC EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1a4 ¥QD EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1a5 ¥QE EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1a6 ¥QF EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1a7 ¥QG EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1a8 ¥QH EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1a9 ¥QI EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1aa ¥QJ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1ab ¥QK EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1ac ¥QL EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1ad ¥QM EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1ae ¥QN EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1af ¥QO EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1b0 ¥QP EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1b1 ¥QQ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1b2 ¥QR EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1b3 ¥QS EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1b4 ¥QT EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1b5 ¥QU EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1b6 ¥QV EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1b7 ¥QW EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1b8 ¥QX EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1b9 ¥QY EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1ba ¥QZ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1bb ¥RA EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1bc ¥RB EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1bd ¥RC EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1be ¥RD EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1bf ¥RE EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1c0 ¥RF EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1c1 ¥RG EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1c2 ¥RH EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1c3 ¥RI EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1c4 ¥RJ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1c5 ¥RK EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1c6 ¥RL EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1c7 ¥RM EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1c8 ¥RN EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1c9 ¥RO EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1ca ¥RP EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1cb ¥RQ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1cc ¥RR EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1cd ¥RS EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1ce ¥RT EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1cf ¥RU EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1d0 ¥RV EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1d1 ¥RW EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1d2 ¥RX EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1d3 ¥RY EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1d4 ¥RZ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1d5 ¥SA EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1d6 ¥SB EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1d7 ¥SC EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1d8 ¥SD EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1d9 ¥SE EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1da ¥SF EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1db ¥SG EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1dc ¥SH EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1dd ¥SI EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1de ¥SJ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1df ¥SK EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1e0 ¥SL EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1e1 ¥SM EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1e2 ¥SN EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1e3 ¥SO EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1e4 ¥SP EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1e5 ¥SQ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1e6 ¥SR EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1e7 ¥SS EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1e8 ¥ST EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1e9 ¥SU EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1ea ¥SV EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1eb ¥SW EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1ec ¥SX EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1ed ¥SY EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1ee ¥SZ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1ef ¥TA EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1f0 ¥TB EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1f1 ¥TC EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1f2 ¥TD EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1f3 ¥TE EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1f4 ¥TF EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1f5 ¥TG EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1f6 ¥TH EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1f7 ¥TI EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1f8 ¥TJ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1f9 ¥TK EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1fa ¥TL EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1fb ¥TM EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1fc ¥TN EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1fd ¥TO EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1fe ¥TP EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1ff ¥TQ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted diff --git a/src/org/infinity/resource/cre/decoder/tables/infinityanimations-v6.ids b/src/org/infinity/resource/cre/decoder/tables/infinityanimations-v6.ids new file mode 100644 index 000000000..30ad010f3 --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/tables/infinityanimations-v6.ids @@ -0,0 +1,8724 @@ +IDS +0x1001 4AA EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1002 4AB EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1003 4AC EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1004 4AD EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1005 4AE EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1006 4AF EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1007 4AG EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1008 4AH EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1009 4AI EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x100a 4AJ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x100b 4AK EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x100c 4AL EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x100d 4AM EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x100e 4AN EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x100f 4AO EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1010 4AP EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1011 4AQ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1012 4AR EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1013 4AS EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1014 4AT EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1015 4AU EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1016 4AV EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1017 4AW EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1018 4AX EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1019 4AY EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x101a 4AZ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x101b 4BA EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x101c 4BB EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x101d 4BC EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x101e 4BD EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x101f 4BE EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1020 4BF EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1021 4BG EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1022 4BH EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1023 4BI EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1024 4BJ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1025 4BK EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1026 4BL EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1027 4BM EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1028 4BN EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1029 4BO EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x102a 4BP EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x102b 4BQ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x102c 4BR EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x102d 4BS EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x102e 4BT EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x102f 4BU EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1030 4BV EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1031 4BW EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1032 4BX EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1033 4BY EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1034 4BZ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1035 4CA EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1036 4CB EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1037 4CC EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1038 4CD EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1039 4CE EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x103a 4CF EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x103b 4CG EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x103c 4CH EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x103d 4CI EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x103e 4CJ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x103f 4CK EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1040 4CL EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1041 4CM EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1042 4CN EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1043 4CO EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1044 4CP EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1045 4CQ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1046 4CR EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1047 4CS EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1048 4CT EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1049 4CU EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x104a 4CV EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x104b 4CW EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x104c 4CX EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x104d 4CY EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x104e 4CZ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x104f 4DA EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1050 4DB EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1051 4DC EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1052 4DD EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1053 4DE EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1054 4DF EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1055 4DG EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1056 4DH EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1057 4DI EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1058 4DJ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1059 4DK EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x105a 4DL EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x105b 4DM EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x105c 4DN EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x105d 4DO EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x105e 4DP EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x105f 4DQ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1060 4DR EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1061 4DS EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1062 4DT EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1063 4DU EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1064 4DV EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1065 4DW EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1066 4DX EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1067 4DY EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1068 4DZ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1069 4EA EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x106a 4EB EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x106b 4EC EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x106c 4ED EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x106d 4EE EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x106e 4EF EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x106f 4EG EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1070 4EH EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1071 4EI EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1072 4EJ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1073 4EK EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1074 4EL EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1075 4EM EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1076 4EN EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1077 4EO EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1078 4EP EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1079 4EQ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x107a 4ER EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x107b 4ES EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x107c 4ET EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x107d 4EU EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x107e 4EV EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x107f 4EW EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1080 4EX EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1081 4EY EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1082 4EZ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1083 4FA EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1084 4FB EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1085 4FC EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1086 4FD EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1087 4FE EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1088 4FF EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1089 4FG EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x108a 4FH EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x108b 4FI EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x108c 4FJ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x108d 4FK EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x108e 4FL EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x108f 4FM EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1090 4FN EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1091 4FO EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1092 4FP EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1093 4FQ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1094 4FR EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1095 4FS EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1096 4FT EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1097 4FU EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1098 4FV EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1099 4FW EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x109a 4FX EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x109b 4FY EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x109c 4FZ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x109d 4GA EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x109e 4GB EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x109f 4GC EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10a0 4GD EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10a1 4GE EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10a2 4GF EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10a3 4GG EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10a4 4GH EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10a5 4GI EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10a6 4GJ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10a7 4GK EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10a8 4GL EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10a9 4GM EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10aa 4GN EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10ab 4GO EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10ac 4GP EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10ad 4GQ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10ae 4GR EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10af 4GS EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10b0 4GT EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10b1 4GU EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10b2 4GV EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10b3 4GW EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10b4 4GX EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10b5 4GY EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10b6 4GZ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10b7 4HA EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10b8 4HB EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10b9 4HC EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10ba 4HD EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10bb 4HE EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10bc 4HF EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10bd 4HG EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10be 4HH EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10bf 4HI EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10c0 4HJ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10c1 4HK EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10c2 4HL EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10c3 4HM EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10c4 4HN EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10c5 4HO EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10c6 4HP EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10c7 4HQ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10c8 4HR EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10c9 4HS EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10ca 4HT EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10cb 4HU EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10cc 4HV EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10cd 4HW EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10ce 4HX EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10cf 4HY EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10d0 4HZ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10d1 4IA EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10d2 4IB EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10d3 4IC EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10d4 4ID EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10d5 4IE EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10d6 4IF EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10d7 4IG EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10d8 4IH EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10d9 4II EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10da 4IJ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10db 4IK EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10dc 4IL EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10dd 4IM EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10de 4IN EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10df 4IO EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10e0 4IP EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10e1 4IQ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10e2 4IR EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10e3 4IS EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10e4 4IT EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10e5 4IU EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10e6 4IV EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10e7 4IW EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10e8 4IX EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10e9 4IY EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10ea 4IZ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10eb 4JA EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10ec 4JB EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10ed 4JC EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10ee 4JD EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10ef 4JE EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10f0 4JF EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10f1 4JG EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10f2 4JH EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10f3 4JI EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10f4 4JJ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10f5 4JK EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10f6 4JL EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10f7 4JM EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10f8 4JN EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10f9 4JO EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10fa 4JP EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10fb 4JQ EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10fc 4JR EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10fd 4JS EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10fe 4JT EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x10ff 4JU EMPTY LARGE 5 Lo BGI MONSTER LONG 4 PART unpaletted +0x1101 4JW EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1102 4JX EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1103 4JY EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1104 4JZ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1105 4KA EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1106 4KB EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1107 4KC EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1108 4KD EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1109 4KE EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x110a 4KF EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x110b 4KG EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x110c 4KH EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x110d 4KI EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x110e 4KJ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x110f 4KK EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1110 4KL EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1111 4KM EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1112 4KN EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1113 4KO EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1114 4KP EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1115 4KQ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1116 4KR EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1117 4KS EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1118 4KT EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1119 4KU EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x111a 4KV EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x111b 4KW EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x111c 4KX EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x111d 4KY EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x111e 4KZ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x111f 4LA EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1120 4LB EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1121 4LC EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1122 4LD EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1123 4LE EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1124 4LF EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1125 4LG EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1126 4LH EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1127 4LI EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1128 4LJ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1129 4LK EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x112a 4LL EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x112b 4LM EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x112c 4LN EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x112d 4LO EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x112e 4LP EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x112f 4LQ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1130 4LR EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1131 4LS EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1132 4LT EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1133 4LU EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1134 4LV EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1135 4LW EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1136 4LX EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1137 4LY EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1138 4LZ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1139 4MA EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x113a 4MB EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x113b 4MC EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x113c 4MD EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x113d 4ME EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x113e 4MF EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x113f 4MG EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1140 4MH EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1141 4MI EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1142 4MJ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1143 4MK EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1144 4ML EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1145 4MM EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1146 4MN EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1147 4MO EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1148 4MP EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1149 4MQ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x114a 4MR EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x114b 4MS EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x114c 4MT EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x114d 4MU EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x114e 4MV EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x114f 4MW EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1150 4MX EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1151 4MY EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1152 4MZ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1153 4NA EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1154 4NB EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1155 4NC EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1156 4ND EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1157 4NE EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1158 4NF EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1159 4NG EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x115a 4NH EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x115b 4NI EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x115c 4NJ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x115d 4NK EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x115e 4NL EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x115f 4NM EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1160 4NN EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1161 4NO EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1162 4NP EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1163 4NQ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1164 4NR EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1165 4NS EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1166 4NT EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1167 4NU EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1168 4NV EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1169 4NW EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x116a 4NX EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x116b 4NY EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x116c 4NZ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x116d 4OA EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x116e 4OB EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x116f 4OC EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1170 4OD EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1171 4OE EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1172 4OF EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1173 4OG EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1174 4OH EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1175 4OI EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1176 4OJ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1177 4OK EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1178 4OL EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1179 4OM EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x117a 4ON EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x117b 4OO EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x117c 4OP EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x117d 4OQ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x117e 4OR EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x117f 4OS EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1180 4OT EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1181 4OU EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1182 4OV EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1183 4OW EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1184 4OX EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1185 4OY EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1186 4OZ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1187 4PA EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1188 4PB EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1189 4PC EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x118a 4PD EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x118b 4PE EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x118c 4PF EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x118d 4PG EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x118e 4PH EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x118f 4PI EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1190 4PJ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1191 4PK EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1192 4PL EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1193 4PM EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1194 4PN EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1195 4PO EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1196 4PP EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1197 4PQ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1198 4PR EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1199 4PS EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x119a 4PT EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x119b 4PU EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x119c 4PV EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x119d 4PW EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x119e 4PX EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x119f 4PY EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11a0 4PZ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11a1 4QA EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11a2 4QB EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11a3 4QC EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11a4 4QD EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11a5 4QE EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11a6 4QF EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11a7 4QG EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11a8 4QH EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11a9 4QI EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11aa 4QJ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11ab 4QK EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11ac 4QL EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11ad 4QM EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11ae 4QN EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11af 4QO EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11b0 4QP EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11b1 4QQ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11b2 4QR EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11b3 4QS EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11b4 4QT EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11b5 4QU EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11b6 4QV EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11b7 4QW EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11b8 4QX EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11b9 4QY EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11ba 4QZ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11bb 4RA EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11bc 4RB EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11bd 4RC EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11be 4RD EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11bf 4RE EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11c0 4RF EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11c1 4RG EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11c2 4RH EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11c3 4RI EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11c4 4RJ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11c5 4RK EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11c6 4RL EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11c7 4RM EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11c8 4RN EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11c9 4RO EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11ca 4RP EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11cb 4RQ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11cc 4RR EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11cd 4RS EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11ce 4RT EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11cf 4RU EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11d0 4RV EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11d1 4RW EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11d2 4RX EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11d3 4RY EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11d4 4RZ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11d5 4SA EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11d6 4SB EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11d7 4SC EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11d8 4SD EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11d9 4SE EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11da 4SF EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11db 4SG EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11dc 4SH EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11dd 4SI EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11de 4SJ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11df 4SK EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11e0 4SL EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11e1 4SM EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11e2 4SN EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11e3 4SO EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11e4 4SP EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11e5 4SQ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11e6 4SR EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11e7 4SS EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11e8 4ST EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11e9 4SU EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11ea 4SV EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11eb 4SW EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11ec 4SX EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11ed 4SY EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11ee 4SZ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11ef 4TA EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11f0 4TB EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11f1 4TC EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11f2 4TD EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11f3 4TE EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11f4 4TF EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11f5 4TG EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11f6 4TH EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11f7 4TI EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11f8 4TJ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11f9 4TK EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11fa 4TL EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11fb 4TM EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11fc 4TN EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11fd 4TO EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11fe 4TP EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x11ff 4TQ EMPTY HUGE A Lo BGI MONSTER LONG 4 PART unpaletted +0x1209 XDR1 EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x120a XDR2 EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x120b XDR3 EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x120c XDR4 EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x120d XDR5 EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x120e XDR6 EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x120f XDR7 EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1210 XDR8 EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1211 XDR9 EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1212 XDRA EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1213 XDRB EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1214 XDRC EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1215 XDRD EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1216 XDRE EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1217 XDRF EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1218 XDRG EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1219 XDRH EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x121a XDRI EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x121b XDRJ EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x121c XDRK EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x121d XDRL EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x121e XDRM EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x121f XDRN EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1220 XDRO EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1221 XDRP EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1222 XDRQ EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1223 XDRR EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1224 XDRS EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1225 XDRT EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1226 XDRU EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1227 XDRV EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1228 XDRW EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1229 XDRX EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x122a XDRY EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x122b XDRZ EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x122c XDR0 EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x122d XDR# EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x122e XDR- EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x122f XDR_ EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1230 XDR! EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1231 XDR@ EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1232 XDR$ EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1233 YDR0 EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1234 YDR1 EMPTY HUGE D Do DRAGONS (XDR) unpaletted +0x1301 -AA EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1302 -AB EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1303 -AC EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1304 -AD EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1305 -AE EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1306 -AF EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1307 -AG EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1308 -AH EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1309 -AI EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x130a -AJ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x130b -AK EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x130c -AL EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x130d -AM EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x130e -AN EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x130f -AO EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1310 -AP EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1311 -AQ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1312 -AR EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1313 -AS EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1314 -AT EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1315 -AU EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1316 -AV EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1317 -AW EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1318 -AX EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1319 -AY EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x131a -AZ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x131b -BA EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x131c -BB EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x131d -BC EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x131e -BD EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x131f -BE EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1320 -BF EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1321 -BG EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1322 -BH EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1323 -BI EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1324 -BJ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1325 -BK EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1326 -BL EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1327 -BM EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1328 -BN EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1329 -BO EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x132a -BP EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x132b -BQ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x132c -BR EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x132d -BS EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x132e -BT EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x132f -BU EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1330 -BV EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1331 -BW EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1332 -BX EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1333 -BY EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1334 -BZ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1335 -CA EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1336 -CB EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1337 -CC EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1338 -CD EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1339 -CE EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x133a -CF EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x133b -CG EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x133c -CH EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x133d -CI EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x133e -CJ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x133f -CK EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1340 -CL EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1341 -CM EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1342 -CN EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1343 -CO EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1344 -CP EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1345 -CQ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1346 -CR EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1347 -CS EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1348 -CT EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1349 -CU EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x134a -CV EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x134b -CW EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x134c -CX EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x134d -CY EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x134e -CZ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x134f -DA EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1350 -DB EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1351 -DC EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1352 -DD EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1353 -DE EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1354 -DF EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1355 -DG EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1356 -DH EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1357 -DI EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1358 -DJ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1359 -DK EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x135a -DL EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x135b -DM EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x135c -DN EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x135d -DO EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x135e -DP EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x135f -DQ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1360 -DR EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1361 -DS EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1362 -DT EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1363 -DU EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1364 -DV EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1365 -DW EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1366 -DX EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1367 -DY EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1368 -DZ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1369 -EA EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x136a -EB EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x136b -EC EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x136c -ED EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x136d -EE EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x136e -EF EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x136f -EG EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1370 -EH EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1371 -EI EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1372 -EJ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1373 -EK EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1374 -EL EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1375 -EM EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1376 -EN EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1377 -EO EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1378 -EP EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1379 -EQ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x137a -ER EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x137b -ES EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x137c -ET EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x137d -EU EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x137e -EV EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x137f -EW EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1380 -EX EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1381 -EY EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1382 -EZ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1383 -FA EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1384 -FB EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1385 -FC EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1386 -FD EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1387 -FE EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1388 -FF EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1389 -FG EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x138a -FH EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x138b -FI EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x138c -FJ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x138d -FK EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x138e -FL EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x138f -FM EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1390 -FN EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1391 -FO EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1392 -FP EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1393 -FQ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1394 -FR EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1395 -FS EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1396 -FT EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1397 -FU EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1398 -FV EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x1399 -FW EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x139a -FX EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x139b -FY EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x139c -FZ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x139d -GA EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x139e -GB EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x139f -GC EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13a0 -GD EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13a1 -GE EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13a2 -GF EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13a3 -GG EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13a4 -GH EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13a5 -GI EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13a6 -GJ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13a7 -GK EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13a8 -GL EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13a9 -GM EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13aa -GN EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13ab -GO EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13ac -GP EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13ad -GQ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13ae -GR EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13af -GS EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13b0 -GT EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13b1 -GU EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13b2 -GV EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13b3 -GW EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13b4 -GX EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13b5 -GY EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13b6 -GZ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13b7 -HA EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13b8 -HB EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13b9 -HC EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13ba -HD EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13bb -HE EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13bc -HF EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13bd -HG EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13be -HH EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13bf -HI EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13c0 -HJ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13c1 -HK EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13c2 -HL EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13c3 -HM EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13c4 -HN EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13c5 -HO EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13c6 -HP EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13c7 -HQ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13c8 -HR EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13c9 -HS EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13ca -HT EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13cb -HU EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13cc -HV EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13cd -HW EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13ce -HX EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13cf -HY EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13d0 -HZ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13d1 -IA EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13d2 -IB EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13d3 -IC EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13d4 -ID EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13d5 -IE EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13d6 -IF EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13d7 -IG EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13d8 -IH EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13d9 -II EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13da -IJ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13db -IK EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13dc -IL EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13dd -IM EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13de -IN EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13df -IO EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13e0 -IP EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13e1 -IQ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13e2 -IR EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13e3 -IS EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13e4 -IT EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13e5 -IU EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13e6 -IV EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13e7 -IW EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13e8 -IX EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13e9 -IY EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13ea -IZ EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13eb -JA EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13ec -JB EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13ed -JC EMPTY LARGE 5 Lo BGII SPLIT 4 PART unpaletted +0x13ee -JD EMPTY HUGE A Lo BGII SPLIT 4 PART unpaletted +0x13ef -JE EMPTY HUGE A Lo BGII SPLIT 4 PART unpaletted +0x13f0 -JF EMPTY HUGE A Lo BGII SPLIT 4 PART unpaletted +0x13f1 -JG EMPTY HUGE A Lo BGII SPLIT 4 PART unpaletted +0x13f2 -JH EMPTY HUGE A Lo BGII SPLIT 4 PART unpaletted +0x13f3 -JI EMPTY HUGE A Lo BGII SPLIT 4 PART unpaletted +0x13f4 -JJ EMPTY HUGE A Lo BGII SPLIT 4 PART unpaletted +0x13f5 -JK EMPTY HUGE A Lo BGII SPLIT 4 PART unpaletted +0x13f6 -JL EMPTY HUGE A Lo BGII SPLIT 4 PART unpaletted +0x13f7 -JM EMPTY HUGE A Lo BGII SPLIT 4 PART unpaletted +0x13f8 -JN EMPTY HUGE A Lo BGII SPLIT 4 PART unpaletted +0x13f9 -JO EMPTY HUGE A Lo BGII SPLIT 4 PART unpaletted +0x13fa -JP EMPTY HUGE A Lo BGII SPLIT 4 PART unpaletted +0x13fb -JQ EMPTY HUGE A Lo BGII SPLIT 4 PART unpaletted +0x13fc -JR EMPTY HUGE A Lo BGII SPLIT 4 PART unpaletted +0x13fd -JS EMPTY HUGE A Lo BGII SPLIT 4 PART unpaletted +0x13fe -JT EMPTY HUGE A Lo BGII SPLIT 4 PART unpaletted +0x13ff -JU EMPTY HUGE A Lo BGII SPLIT 4 PART unpaletted +0x2001 2AA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2002 2AB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2003 2AC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2004 2AD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2005 2AE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2006 2AF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2007 2AG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2008 2AH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2009 2AI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x200a 2AJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x200b 2AK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x200c 2AL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x200d 2AM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x200e 2AN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x200f 2AO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2010 2AP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2011 2AQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2012 2AR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2013 2AS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2014 2AT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2015 2AU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2016 2AV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2017 2AW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2018 2AX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2019 2AY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x201a 2AZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x201b 2BA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x201c 2BB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x201d 2BC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x201e 2BD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x201f 2BE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2020 2BF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2021 2BG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2022 2BH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2023 2BI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2024 2BJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2025 2BK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2026 2BL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2027 2BM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2028 2BN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2029 2BO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x202a 2BP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x202b 2BQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x202c 2BR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x202d 2BS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x202e 2BT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x202f 2BU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2030 2BV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2031 2BW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2032 2BX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2033 2BY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2034 2BZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2035 2CA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2036 2CB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2037 2CC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2038 2CD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2039 2CE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x203a 2CF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x203b 2CG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x203c 2CH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x203d 2CI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x203e 2CJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x203f 2CK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2040 2CL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2041 2CM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2042 2CN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2043 2CO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2044 2CP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2045 2CQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2046 2CR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2047 2CS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2048 2CT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2049 2CU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x204a 2CV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x204b 2CW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x204c 2CX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x204d 2CY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x204e 2CZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x204f 2DA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2050 2DB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2051 2DC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2052 2DD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2053 2DE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2054 2DF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2055 2DG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2056 2DH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2057 2DI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2058 2DJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2059 2DK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x205a 2DL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x205b 2DM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x205c 2DN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x205d 2DO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x205e 2DP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x205f 2DQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2060 2DR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2061 2DS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2062 2DT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2063 2DU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2064 2DV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2065 2DW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2066 2DX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2067 2DY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2068 2DZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2069 2EA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x206a 2EB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x206b 2EC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x206c 2ED EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x206d 2EE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x206e 2EF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x206f 2EG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2070 2EH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2071 2EI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2072 2EJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2073 2EK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2074 2EL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2075 2EM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2076 2EN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2077 2EO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2078 2EP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2079 2EQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x207a 2ER EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x207b 2ES EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x207c 2ET EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x207d 2EU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x207e 2EV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x207f 2EW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2080 2EX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2081 2EY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2082 2EZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2083 2FA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2084 2FB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2085 2FC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2086 2FD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2087 2FE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2088 2FF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2089 2FG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x208a 2FH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x208b 2FI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x208c 2FJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x208d 2FK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x208e 2FL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x208f 2FM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2090 2FN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2091 2FO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2092 2FP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2093 2FQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2094 2FR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2095 2FS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2096 2FT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2097 2FU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2098 2FV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2099 2FW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x209a 2FX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x209b 2FY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x209c 2FZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x209d 2GA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x209e 2GB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x209f 2GC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20a0 2GD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20a1 2GE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20a2 2GF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20a3 2GG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20a4 2GH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20a5 2GI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20a6 2GJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20a7 2GK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20a8 2GL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20a9 2GM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20aa 2GN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20ab 2GO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20ac 2GP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20ad 2GQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20ae 2GR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20af 2GS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20b0 2GT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20b1 2GU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20b2 2GV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20b3 2GW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20b4 2GX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20b5 2GY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20b6 2GZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20b7 2HA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20b8 2HB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20b9 2HC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20ba 2HD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20bb 2HE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20bc 2HF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20bd 2HG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20be 2HH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20bf 2HI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20c0 2HJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20c1 2HK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20c2 2HL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20c3 2HM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20c4 2HN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20c5 2HO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20c6 2HP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20c7 2HQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20c8 2HR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20c9 2HS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20ca 2HT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20cb 2HU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20cc 2HV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20cd 2HW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20ce 2HX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20cf 2HY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20d0 2HZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20d1 2IA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20d2 2IB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20d3 2IC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20d4 2ID EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20d5 2IE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20d6 2IF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20d7 2IG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20d8 2IH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20d9 2II EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20da 2IJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20db 2IK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20dc 2IL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20dd 2IM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20de 2IN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20df 2IO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20e0 2IP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20e1 2IQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20e2 2IR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20e3 2IS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20e4 2IT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20e5 2IU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20e6 2IV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20e7 2IW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20e8 2IX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20e9 2IY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20ea 2IZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20eb 2JA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20ec 2JB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20ed 2JC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20ee 2JD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20ef 2JE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20f0 2JF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20f1 2JG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20f2 2JH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20f3 2JI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20f4 2JJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20f5 2JK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20f6 2JL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20f7 2JM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20f8 2JN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20f9 2JO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20fa 2JP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20fb 2JQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20fc 2JR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20fd 2JS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20fe 2JT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x20ff 2JU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2101 2JW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2102 2JX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2103 2JY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2104 2JZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2105 2KA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2106 2KB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2107 2KC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2108 2KD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2109 2KE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x210a 2KF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x210b 2KG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x210c 2KH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x210d 2KI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x210e 2KJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x210f 2KK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2110 2KL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2111 2KM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2112 2KN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2113 2KO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2114 2KP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2115 2KQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2116 2KR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2117 2KS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2118 2KT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2119 2KU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x211a 2KV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x211b 2KW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x211c 2KX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x211d 2KY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x211e 2KZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x211f 2LA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2120 2LB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2121 2LC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2122 2LD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2123 2LE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2124 2LF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2125 2LG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2126 2LH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2127 2LI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2128 2LJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2129 2LK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x212a 2LL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x212b 2LM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x212c 2LN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x212d 2LO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x212e 2LP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x212f 2LQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2130 2LR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2131 2LS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2132 2LT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2133 2LU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2134 2LV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2135 2LW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2136 2LX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2137 2LY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2138 2LZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2139 2MA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x213a 2MB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x213b 2MC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x213c 2MD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x213d 2ME EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x213e 2MF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x213f 2MG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2140 2MH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2141 2MI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2142 2MJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2143 2MK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2144 2ML EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2145 2MM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2146 2MN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2147 2MO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2148 2MP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2149 2MQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x214a 2MR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x214b 2MS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x214c 2MT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x214d 2MU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x214e 2MV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x214f 2MW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2150 2MX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2151 2MY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2152 2MZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2153 2NA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2154 2NB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2155 2NC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2156 2ND EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2157 2NE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2158 2NF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2159 2NG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x215a 2NH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x215b 2NI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x215c 2NJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x215d 2NK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x215e 2NL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x215f 2NM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2160 2NN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2161 2NO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2162 2NP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2163 2NQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2164 2NR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2165 2NS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2166 2NT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2167 2NU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2168 2NV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2169 2NW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x216a 2NX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x216b 2NY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x216c 2NZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x216d 2OA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x216e 2OB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x216f 2OC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2170 2OD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2171 2OE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2172 2OF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2173 2OG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2174 2OH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2175 2OI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2176 2OJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2177 2OK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2178 2OL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2179 2OM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x217a 2ON EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x217b 2OO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x217c 2OP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x217d 2OQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x217e 2OR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x217f 2OS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2180 2OT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2181 2OU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2182 2OV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2183 2OW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2184 2OX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2185 2OY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2186 2OZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2187 2PA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2188 2PB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2189 2PC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x218a 2PD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x218b 2PE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x218c 2PF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x218d 2PG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x218e 2PH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x218f 2PI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2190 2PJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2191 2PK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2192 2PL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2193 2PM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2194 2PN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2195 2PO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2196 2PP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2197 2PQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2198 2PR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2199 2PS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x219a 2PT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x219b 2PU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x219c 2PV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x219d 2PW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x219e 2PX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x219f 2PY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21a0 2PZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21a1 2QA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21a2 2QB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21a3 2QC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21a4 2QD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21a5 2QE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21a6 2QF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21a7 2QG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21a8 2QH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21a9 2QI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21aa 2QJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21ab 2QK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21ac 2QL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21ad 2QM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21ae 2QN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21af 2QO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21b0 2QP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21b1 2QQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21b2 2QR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21b3 2QS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21b4 2QT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21b5 2QU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21b6 2QV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21b7 2QW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21b8 2QX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21b9 2QY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21ba 2QZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21bb 2RA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21bc 2RB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21bd 2RC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21be 2RD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21bf 2RE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21c0 2RF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21c1 2RG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21c2 2RH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21c3 2RI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21c4 2RJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21c5 2RK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21c6 2RL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21c7 2RM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21c8 2RN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21c9 2RO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21ca 2RP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21cb 2RQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21cc 2RR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21cd 2RS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21ce 2RT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21cf 2RU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21d0 2RV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21d1 2RW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21d2 2RX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21d3 2RY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21d4 2RZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21d5 2SA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21d6 2SB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21d7 2SC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21d8 2SD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21d9 2SE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21da 2SF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21db 2SG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21dc 2SH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21dd 2SI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21de 2SJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21df 2SK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21e0 2SL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21e1 2SM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21e2 2SN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21e3 2SO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21e4 2SP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21e5 2SQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21e6 2SR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21e7 2SS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21e8 2ST EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21e9 2SU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21ea 2SV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21eb 2SW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21ec 2SX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21ed 2SY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21ee 2SZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21ef 2TA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21f0 2TB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21f1 2TC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21f2 2TD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21f3 2TE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21f4 2TF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21f5 2TG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21f6 2TH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21f7 2TI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21f8 2TJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21f9 2TK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21fa 2TL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21fb 2TM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21fc 2TN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21fd 2TO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21fe 2TP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x21ff 2TQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (BOW) paletted +0x2201 6AA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2202 6AB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2203 6AC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2204 6AD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2205 6AE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2206 6AF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2207 6AG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2208 6AH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2209 6AI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x220a 6AJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x220b 6AK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x220c 6AL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x220d 6AM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x220e 6AN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x220f 6AO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2210 6AP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2211 6AQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2212 6AR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2213 6AS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2214 6AT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2215 6AU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2216 6AV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2217 6AW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2218 6AX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2219 6AY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x221a 6AZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x221b 6BA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x221c 6BB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x221d 6BC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x221e 6BD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x221f 6BE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2220 6BF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2221 6BG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2222 6BH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2223 6BI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2224 6BJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2225 6BK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2226 6BL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2227 6BM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2228 6BN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2229 6BO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x222a 6BP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x222b 6BQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x222c 6BR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x222d 6BS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x222e 6BT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x222f 6BU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2230 6BV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2231 6BW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2232 6BX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2233 6BY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2234 6BZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2235 6CA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2236 6CB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2237 6CC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2238 6CD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2239 6CE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x223a 6CF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x223b 6CG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x223c 6CH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x223d 6CI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x223e 6CJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x223f 6CK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2240 6CL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2241 6CM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2242 6CN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2243 6CO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2244 6CP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2245 6CQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2246 6CR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2247 6CS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2248 6CT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2249 6CU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x224a 6CV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x224b 6CW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x224c 6CX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x224d 6CY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x224e 6CZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x224f 6DA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2250 6DB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2251 6DC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2252 6DD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2253 6DE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2254 6DF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2255 6DG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2256 6DH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2257 6DI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2258 6DJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2259 6DK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x225a 6DL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x225b 6DM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x225c 6DN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x225d 6DO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x225e 6DP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x225f 6DQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2260 6DR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2261 6DS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2262 6DT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2263 6DU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2264 6DV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2265 6DW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2266 6DX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2267 6DY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2268 6DZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2269 6EA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x226a 6EB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x226b 6EC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x226c 6ED EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x226d 6EE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x226e 6EF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x226f 6EG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2270 6EH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2271 6EI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2272 6EJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2273 6EK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2274 6EL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2275 6EM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2276 6EN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2277 6EO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2278 6EP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2279 6EQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x227a 6ER EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x227b 6ES EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x227c 6ET EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x227d 6EU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x227e 6EV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x227f 6EW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2280 6EX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2281 6EY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2282 6EZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2283 6FA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2284 6FB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2285 6FC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2286 6FD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2287 6FE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2288 6FF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2289 6FG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x228a 6FH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x228b 6FI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x228c 6FJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x228d 6FK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x228e 6FL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x228f 6FM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2290 6FN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2291 6FO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2292 6FP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2293 6FQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2294 6FR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2295 6FS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2296 6FT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2297 6FU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2298 6FV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x2299 6FW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x229a 6FX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x229b 6FY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x229c 6FZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x229d 6GA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x229e 6GB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x229f 6GC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22a0 6GD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22a1 6GE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22a2 6GF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22a3 6GG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22a4 6GH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22a5 6GI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22a6 6GJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22a7 6GK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22a8 6GL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22a9 6GM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22aa 6GN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22ab 6GO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22ac 6GP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22ad 6GQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22ae 6GR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22af 6GS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22b0 6GT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22b1 6GU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22b2 6GV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22b3 6GW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22b4 6GX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22b5 6GY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22b6 6GZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22b7 6HA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22b8 6HB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22b9 6HC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22ba 6HD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22bb 6HE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22bc 6HF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22bd 6HG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22be 6HH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22bf 6HI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22c0 6HJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22c1 6HK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22c2 6HL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22c3 6HM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22c4 6HN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22c5 6HO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22c6 6HP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22c7 6HQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22c8 6HR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22c9 6HS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22ca 6HT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22cb 6HU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22cc 6HV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22cd 6HW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22ce 6HX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22cf 6HY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22d0 6HZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22d1 6IA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22d2 6IB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22d3 6IC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22d4 6ID EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22d5 6IE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22d6 6IF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22d7 6IG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22d8 6IH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22d9 6II EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22da 6IJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22db 6IK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22dc 6IL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22dd 6IM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22de 6IN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22df 6IO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22e0 6IP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22e1 6IQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22e2 6IR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22e3 6IS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22e4 6IT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22e5 6IU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22e6 6IV EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22e7 6IW EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22e8 6IX EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22e9 6IY EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22ea 6IZ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22eb 6JA EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22ec 6JB EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22ed 6JC EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22ee 6JD EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22ef 6JE EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22f0 6JF EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22f1 6JG EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22f2 6JH EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22f3 6JI EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22f4 6JJ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22f5 6JK EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22f6 6JL EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22f7 6JM EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22f8 6JN EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22f9 6JO EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22fa 6JP EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22fb 6JQ EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22fc 6JR EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22fd 6JS EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22fe 6JT EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x22ff 6JU EMPTY NORMAL 3 Xo BGI SIMPLE CASTER (S1) paletted +0x3001 4AA EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3002 4AB EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3003 4AC EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3004 4AD EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3005 4AE EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3006 4AF EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3007 4AG EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3008 4AH EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3009 4AI EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x300a 4AJ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x300b 4AK EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x300c 4AL EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x300d 4AM EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x300e 4AN EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x300f 4AO EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3010 4AP EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3011 4AQ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3012 4AR EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3013 4AS EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3014 4AT EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3015 4AU EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3016 4AV EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3017 4AW EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3018 4AX EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3019 4AY EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x301a 4AZ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x301b 4BA EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x301c 4BB EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x301d 4BC EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x301e 4BD EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x301f 4BE EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3020 4BF EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3021 4BG EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3022 4BH EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3023 4BI EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3024 4BJ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3025 4BK EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3026 4BL EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3027 4BM EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3028 4BN EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3029 4BO EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x302a 4BP EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x302b 4BQ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x302c 4BR EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x302d 4BS EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x302e 4BT EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x302f 4BU EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3030 4BV EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3031 4BW EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3032 4BX EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3033 4BY EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3034 4BZ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3035 4CA EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3036 4CB EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3037 4CC EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3038 4CD EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3039 4CE EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x303a 4CF EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x303b 4CG EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x303c 4CH EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x303d 4CI EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x303e 4CJ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x303f 4CK EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3040 4CL EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3041 4CM EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3042 4CN EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3043 4CO EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3044 4CP EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3045 4CQ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3046 4CR EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3047 4CS EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3048 4CT EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3049 4CU EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x304a 4CV EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x304b 4CW EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x304c 4CX EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x304d 4CY EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x304e 4CZ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x304f 4DA EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3050 4DB EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3051 4DC EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3052 4DD EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3053 4DE EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3054 4DF EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3055 4DG EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3056 4DH EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3057 4DI EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3058 4DJ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3059 4DK EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x305a 4DL EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x305b 4DM EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x305c 4DN EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x305d 4DO EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x305e 4DP EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x305f 4DQ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3060 4DR EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3061 4DS EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3062 4DT EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3063 4DU EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3064 4DV EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3065 4DW EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3066 4DX EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3067 4DY EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3068 4DZ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3069 4EA EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x306a 4EB EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x306b 4EC EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x306c 4ED EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x306d 4EE EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x306e 4EF EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x306f 4EG EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3070 4EH EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3071 4EI EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3072 4EJ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3073 4EK EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3074 4EL EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3075 4EM EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3076 4EN EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3077 4EO EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3078 4EP EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3079 4EQ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x307a 4ER EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x307b 4ES EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x307c 4ET EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x307d 4EU EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x307e 4EV EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x307f 4EW EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3080 4EX EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3081 4EY EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3082 4EZ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3083 4FA EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3084 4FB EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3085 4FC EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3086 4FD EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3087 4FE EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3088 4FF EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3089 4FG EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x308a 4FH EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x308b 4FI EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x308c 4FJ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x308d 4FK EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x308e 4FL EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x308f 4FM EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3090 4FN EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3091 4FO EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3092 4FP EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3093 4FQ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3094 4FR EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3095 4FS EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3096 4FT EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3097 4FU EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3098 4FV EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3099 4FW EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x309a 4FX EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x309b 4FY EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x309c 4FZ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x309d 4GA EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x309e 4GB EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x309f 4GC EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30a0 4GD EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30a1 4GE EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30a2 4GF EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30a3 4GG EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30a4 4GH EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30a5 4GI EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30a6 4GJ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30a7 4GK EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30a8 4GL EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30a9 4GM EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30aa 4GN EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30ab 4GO EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30ac 4GP EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30ad 4GQ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30ae 4GR EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30af 4GS EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30b0 4GT EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30b1 4GU EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30b2 4GV EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30b3 4GW EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30b4 4GX EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30b5 4GY EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30b6 4GZ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30b7 4HA EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30b8 4HB EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30b9 4HC EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30ba 4HD EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30bb 4HE EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30bc 4HF EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30bd 4HG EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30be 4HH EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30bf 4HI EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30c0 4HJ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30c1 4HK EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30c2 4HL EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30c3 4HM EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30c4 4HN EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30c5 4HO EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30c6 4HP EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30c7 4HQ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30c8 4HR EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30c9 4HS EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30ca 4HT EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30cb 4HU EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30cc 4HV EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30cd 4HW EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30ce 4HX EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30cf 4HY EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30d0 4HZ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30d1 4IA EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30d2 4IB EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30d3 4IC EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30d4 4ID EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30d5 4IE EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30d6 4IF EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30d7 4IG EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30d8 4IH EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30d9 4II EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30da 4IJ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30db 4IK EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30dc 4IL EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30dd 4IM EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30de 4IN EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30df 4IO EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30e0 4IP EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30e1 4IQ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30e2 4IR EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30e3 4IS EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30e4 4IT EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30e5 4IU EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30e6 4IV EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30e7 4IW EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30e8 4IX EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30e9 4IY EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30ea 4IZ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30eb 4JA EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30ec 4JB EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30ed 4JC EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30ee 4JD EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30ef 4JE EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30f0 4JF EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30f1 4JG EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30f2 4JH EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30f3 4JI EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30f4 4JJ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30f5 4JK EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30f6 4JL EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30f7 4JM EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30f8 4JN EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30f9 4JO EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30fa 4JP EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30fb 4JQ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30fc 4JR EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30fd 4JS EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30fe 4JT EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x30ff 4JU EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3100 4JV EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3101 4JW EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3102 4JX EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3103 4JY EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3104 4JZ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3105 4KA EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3106 4KB EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3107 4KC EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3108 4KD EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3109 4KE EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x310a 4KF EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x310b 4KG EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x310c 4KH EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x310d 4KI EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x310e 4KJ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x310f 4KK EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3110 4KL EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3111 4KM EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3112 4KN EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3113 4KO EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3114 4KP EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3115 4KQ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3116 4KR EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3117 4KS EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3118 4KT EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3119 4KU EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x311a 4KV EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x311b 4KW EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x311c 4KX EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x311d 4KY EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x311e 4KZ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x311f 4LA EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3120 4LB EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3121 4LC EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3122 4LD EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3123 4LE EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3124 4LF EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3125 4LG EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3126 4LH EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3127 4LI EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3128 4LJ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3129 4LK EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x312a 4LL EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x312b 4LM EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x312c 4LN EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x312d 4LO EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x312e 4LP EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x312f 4LQ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3130 4LR EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3131 4LS EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3132 4LT EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3133 4LU EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3134 4LV EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3135 4LW EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3136 4LX EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3137 4LY EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3138 4LZ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3139 4MA EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x313a 4MB EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x313b 4MC EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x313c 4MD EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x313d 4ME EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x313e 4MF EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x313f 4MG EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3140 4MH EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3141 4MI EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3142 4MJ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3143 4MK EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3144 4ML EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3145 4MM EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3146 4MN EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3147 4MO EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3148 4MP EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3149 4MQ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x314a 4MR EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x314b 4MS EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x314c 4MT EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x314d 4MU EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x314e 4MV EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x314f 4MW EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3150 4MX EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3151 4MY EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3152 4MZ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3153 4NA EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3154 4NB EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3155 4NC EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3156 4ND EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3157 4NE EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3158 4NF EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3159 4NG EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x315a 4NH EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x315b 4NI EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x315c 4NJ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x315d 4NK EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x315e 4NL EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x315f 4NM EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3160 4NN EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3161 4NO EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3162 4NP EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3163 4NQ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3164 4NR EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3165 4NS EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3166 4NT EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3167 4NU EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3168 4NV EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3169 4NW EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x316a 4NX EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x316b 4NY EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x316c 4NZ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x316d 4OA EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x316e 4OB EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x316f 4OC EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3170 4OD EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3171 4OE EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3172 4OF EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3173 4OG EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3174 4OH EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3175 4OI EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3176 4OJ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3177 4OK EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3178 4OL EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3179 4OM EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x317a 4ON EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x317b 4OO EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x317c 4OP EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x317d 4OQ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x317e 4OR EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x317f 4OS EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3180 4OT EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3181 4OU EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3182 4OV EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3183 4OW EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3184 4OX EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3185 4OY EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3186 4OZ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3187 4PA EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3188 4PB EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3189 4PC EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x318a 4PD EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x318b 4PE EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x318c 4PF EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x318d 4PG EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x318e 4PH EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x318f 4PI EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3190 4PJ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3191 4PK EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3192 4PL EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3193 4PM EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3194 4PN EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3195 4PO EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3196 4PP EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3197 4PQ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3198 4PR EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x3199 4PS EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x319a 4PT EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x319b 4PU EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x319c 4PV EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x319d 4PW EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x319e 4PX EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x319f 4PY EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31a0 4PZ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31a1 4QA EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31a2 4QB EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31a3 4QC EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31a4 4QD EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31a5 4QE EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31a6 4QF EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31a7 4QG EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31a8 4QH EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31a9 4QI EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31aa 4QJ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31ab 4QK EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31ac 4QL EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31ad 4QM EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31ae 4QN EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31af 4QO EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31b0 4QP EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31b1 4QQ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31b2 4QR EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31b3 4QS EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31b4 4QT EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31b5 4QU EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31b6 4QV EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31b7 4QW EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31b8 4QX EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31b9 4QY EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31ba 4QZ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31bb 4RA EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31bc 4RB EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31bd 4RC EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31be 4RD EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31bf 4RE EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31c0 4RF EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31c1 4RG EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31c2 4RH EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31c3 4RI EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31c4 4RJ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31c5 4RK EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31c6 4RL EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31c7 4RM EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31c8 4RN EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31c9 4RO EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31ca 4RP EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31cb 4RQ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31cc 4RR EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31cd 4RS EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31ce 4RT EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31cf 4RU EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31d0 4RV EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31d1 4RW EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31d2 4RX EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31d3 4RY EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31d4 4RZ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31d5 4SA EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31d6 4SB EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31d7 4SC EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31d8 4SD EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31d9 4SE EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31da 4SF EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31db 4SG EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31dc 4SH EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31dd 4SI EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31de 4SJ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31df 4SK EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31e0 4SL EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31e1 4SM EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31e2 4SN EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31e3 4SO EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31e4 4SP EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31e5 4SQ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31e6 4SR EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31e7 4SS EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31e8 4ST EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31e9 4SU EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31ea 4SV EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31eb 4SW EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31ec 4SX EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31ed 4SY EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31ee 4SZ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31ef 4TA EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31f0 4TB EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31f1 4TC EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31f2 4TD EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31f3 4TE EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31f4 4TF EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31f5 4TG EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31f6 4TH EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31f7 4TI EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31f8 4TJ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31f9 4TK EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31fa 4TL EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31fb 4TM EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31fc 4TN EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31fd 4TO EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31fe 4TP EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x31ff 4TQ EMPTY LARGE 5 Xo BROKEN ANKHEG unpaletted +0x5000 3AA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5001 3AB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5002 3AC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5003 3AD EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5004 3AE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5005 3AF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5006 3AG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5007 3AH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5008 3AI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5009 3AJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x500a 3AK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x500b 3AL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x500c 3AM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x500d 3AN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x500e 3AO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x500f 3AP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5010 3AQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5011 3AR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5012 3AS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5013 3AT EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5014 3AU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5015 3AV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5016 3AW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5017 3AX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5018 3AY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5019 3AZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x501a 3BA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x501b 3BB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x501c 3BC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x501d 3BD EMPTY LARGE 5 Io IWD LARGE unpaletted +0x501e 3BE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x501f 3BF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5020 3BG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5021 3BH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5022 3BI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5023 3BJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5024 3BK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5025 3BL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5026 3BM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5027 3BN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5028 3BO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5029 3BP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x502a 3BQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x502b 3BR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x502c 3BS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x502d 3BT EMPTY LARGE 5 Io IWD LARGE unpaletted +0x502e 3BU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x502f 3BV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5030 3BW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5031 3BX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5032 3BY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5033 3BZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5034 3CA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5035 3CB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5036 3CC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5037 3CD EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5038 3CE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5039 3CF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x503a 3CG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x503b 3CH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x503c 3CI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x503d 3CJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x503e 3CK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x503f 3CL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5040 3CM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5041 3CN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5042 3CO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5043 3CP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5044 3CQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5045 3CR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5046 3CS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5047 3CT EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5048 3CU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5049 3CV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x504a 3CW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x504b 3CX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x504c 3CY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x504d 3CZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x504e 3DA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x504f 3DB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5050 3DC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5051 3DD EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5052 3DE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5053 3DF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5054 3DG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5055 3DH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5056 3DI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5057 3DJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5058 3DK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5059 3DL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x505a 3DM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x505b 3DN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x505c 3DO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x505d 3DP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x505e 3DQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x505f 3DR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5060 3DS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5061 3DT EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5062 3DU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5063 3DV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5064 3DW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5065 3DX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5066 3DY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5067 3DZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5068 3EA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5069 3EB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x506a 3EC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x506b 3ED EMPTY LARGE 5 Io IWD LARGE unpaletted +0x506c 3EE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x506d 3EF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x506e 3EG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x506f 3EH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5070 3EI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5071 3EJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5072 3EK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5073 3EL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5074 3EM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5075 3EN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5076 3EO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5077 3EP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5078 3EQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5079 3ER EMPTY LARGE 5 Io IWD LARGE unpaletted +0x507a 3ES EMPTY LARGE 5 Io IWD LARGE unpaletted +0x507b 3ET EMPTY LARGE 5 Io IWD LARGE unpaletted +0x507c 3EU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x507d 3EV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x507e 3EW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x507f 3EX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5080 3EY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5081 3EZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5082 3FA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5083 3FB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5084 3FC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5085 3FD EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5086 3FE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5087 3FF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5088 3FG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5089 3FH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x508a 3FI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x508b 3FJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x508c 3FK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x508d 3FL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x508e 3FM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x508f 3FN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5090 3FO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5091 3FP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5092 3FQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5093 3FR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5094 3FS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5095 3FT EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5096 3FU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5097 3FV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5098 3FW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5099 3FX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x509a 3FY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x509b 3FZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x509c 3GA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x509d 3GB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x509e 3GC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x509f 3GD EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50a0 3GE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50a1 3GF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50a2 3GG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50a3 3GH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50a4 3GI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50a5 3GJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50a6 3GK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50a7 3GL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50a8 3GM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50a9 3GN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50aa 3GO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50ab 3GP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50ac 3GQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50ad 3GR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50ae 3GS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50af 3GT EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50b0 3GU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50b1 3GV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50b2 3GW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50b3 3GX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50b4 3GY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50b5 3GZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50b6 3HA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50b7 3HB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50b8 3HC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50b9 3HD EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50ba 3HE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50bb 3HF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50bc 3HG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50bd 3HH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50be 3HI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50bf 3HJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50c0 3HK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50c1 3HL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50c2 3HM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50c3 3HN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50c4 3HO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50c5 3HP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50c6 3HQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50c7 3HR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50c8 3HS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50c9 3HT EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50ca 3HU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50cb 3HV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50cc 3HW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50cd 3HX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50ce 3HY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50cf 3HZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50d0 3IA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50d1 3IB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50d2 3IC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50d3 3ID EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50d4 3IE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50d5 3IF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50d6 3IG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50d7 3IH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50d8 3II EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50d9 3IJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50da 3IK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50db 3IL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50dc 3IM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50dd 3IN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50de 3IO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50df 3IP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50e0 3IQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50e1 3IR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50e2 3IS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50e3 3IT EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50e4 3IU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50e5 3IV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50e6 3IW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50e7 3IX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50e8 3IY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50e9 3IZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50ea 3JA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50eb 3JB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50ec 3JC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50ed 3JD EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50ee 3JE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50ef 3JF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50f0 3JG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50f1 3JH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50f2 3JI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50f3 3JJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50f4 3JK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50f5 3JL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50f6 3JM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50f7 3JN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50f8 3JO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50f9 3JP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50fa 3JQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50fb 3JR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50fc 3JS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50fd 3JT EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50fe 3JU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x50ff 3JV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5100 3JW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5101 3JX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5102 3JY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5103 3JZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5104 3KA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5105 3KB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5106 3KC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5107 3KD EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5108 3KE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5109 3KF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x510a 3KG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x510b 3KH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x510c 3KI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x510d 3KJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x510e 3KK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x510f 3KL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5110 3KM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5111 3KN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5112 3KO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5113 3KP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5114 3KQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5115 3KR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5116 3KS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5117 3KT EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5118 3KU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5119 3KV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x511a 3KW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x511b 3KX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x511c 3KY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x511d 3KZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x511e 3LA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x511f 3LB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5120 3LC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5121 3LD EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5122 3LE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5123 3LF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5124 3LG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5125 3LH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5126 3LI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5127 3LJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5128 3LK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5129 3LL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x512a 3LM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x512b 3LN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x512c 3LO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x512d 3LP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x512e 3LQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x512f 3LR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5130 3LS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5131 3LT EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5132 3LU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5133 3LV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5134 3LW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5135 3LX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5136 3LY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5137 3LZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5138 3MA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5139 3MB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x513a 3MC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x513b 3MD EMPTY LARGE 5 Io IWD LARGE unpaletted +0x513c 3ME EMPTY LARGE 5 Io IWD LARGE unpaletted +0x513d 3MF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x513e 3MG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x513f 3MH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5140 3MI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5141 3MJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5142 3MK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5143 3ML EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5144 3MM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5145 3MN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5146 3MO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5147 3MP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5148 3MQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5149 3MR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x514a 3MS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x514b 3MT EMPTY LARGE 5 Io IWD LARGE unpaletted +0x514c 3MU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x514d 3MV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x514e 3MW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x514f 3MX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5150 3MY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5151 3MZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5152 3NA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5153 3NB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5154 3NC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5155 3ND EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5156 3NE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5157 3NF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5158 3NG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5159 3NH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x515a 3NI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x515b 3NJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x515c 3NK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x515d 3NL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x515e 3NM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x515f 3NN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5160 3NO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5161 3NP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5162 3NQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5163 3NR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5164 3NS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5165 3NT EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5166 3NU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5167 3NV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5168 3NW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5169 3NX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x516a 3NY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x516b 3NZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x516c 3OA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x516d 3OB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x516e 3OC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x516f 3OD EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5170 3OE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5171 3OF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5172 3OG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5173 3OH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5174 3OI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5175 3OJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5176 3OK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5177 3OL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5178 3OM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5179 3ON EMPTY LARGE 5 Io IWD LARGE unpaletted +0x517a 3OO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x517b 3OP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x517c 3OQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x517d 3OR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x517e 3OS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x517f 3OT EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5180 3OU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5181 3OV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5182 3OW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5183 3OX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5184 3OY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5185 3OZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5186 3PA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5187 3PB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5188 3PC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5189 3PD EMPTY LARGE 5 Io IWD LARGE unpaletted +0x518a 3PE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x518b 3PF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x518c 3PG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x518d 3PH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x518e 3PI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x518f 3PJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5190 3PK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5191 3PL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5192 3PM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5193 3PN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5194 3PO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5195 3PP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5196 3PQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5197 3PR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5198 3PS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5199 3PT EMPTY LARGE 5 Io IWD LARGE unpaletted +0x519a 3PU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x519b 3PV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x519c 3PW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x519d 3PX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x519e 3PY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x519f 3PZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51a0 3QA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51a1 3QB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51a2 3QC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51a3 3QD EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51a4 3QE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51a5 3QF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51a6 3QG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51a7 3QH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51a8 3QI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51a9 3QJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51aa 3QK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51ab 3QL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51ac 3QM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51ad 3QN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51ae 3QO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51af 3QP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51b0 3QQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51b1 3QR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51b2 3QS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51b3 3QT EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51b4 3QU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51b5 3QV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51b6 3QW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51b7 3QX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51b8 3QY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51b9 3QZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51ba 3RA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51bb 3RB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51bc 3RC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51bd 3RD EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51be 3RE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51bf 3RF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51c0 3RG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51c1 3RH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51c2 3RI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51c3 3RJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51c4 3RK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51c5 3RL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51c6 3RM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51c7 3RN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51c8 3RO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51c9 3RP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51ca 3RQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51cb 3RR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51cc 3RS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51cd 3RT EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51ce 3RU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51cf 3RV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51d0 3RW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51d1 3RX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51d2 3RY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51d3 3RZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51d4 3SA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51d5 3SB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51d6 3SC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51d7 3SD EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51d8 3SE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51d9 3SF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51da 3SG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51db 3SH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51dc 3SI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51dd 3SJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51de 3SK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51df 3SL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51e0 3SM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51e1 3SN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51e2 3SO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51e3 3SP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51e4 3SQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51e5 3SR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51e6 3SS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51e7 3ST EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51e8 3SU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51e9 3SV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51ea 3SW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51eb 3SX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51ec 3SY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51ed 3SZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51ee 3TA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51ef 3TB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51f0 3TC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51f1 3TD EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51f2 3TE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51f3 3TF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51f4 3TG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51f5 3TH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51f6 3TI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51f7 3TJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51f8 3TK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51f9 3TL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51fa 3TM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51fb 3TN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51fc 3TO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51fd 3TP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51fe 3TQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x51ff 3TR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5200 3TS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5201 3TT EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5202 3TU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5203 3TV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5204 3TW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5205 3TX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5206 3TY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5207 3TZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5208 3UA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5209 3UB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x520a 3UC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x520b 3UD EMPTY LARGE 5 Io IWD LARGE unpaletted +0x520c 3UE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x520d 3UF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x520e 3UG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x520f 3UH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5210 3UI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5211 3UJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5212 3UK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5213 3UL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5214 3UM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5215 3UN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5216 3UO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5217 3UP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5218 3UQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5219 3UR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x521a 3US EMPTY LARGE 5 Io IWD LARGE unpaletted +0x521b 3UT EMPTY LARGE 5 Io IWD LARGE unpaletted +0x521c 3UU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x521d 3UV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x521e 3UW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x521f 3UX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5220 3UY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5221 3UZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5222 3VA EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5223 3VB EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5224 3VC EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5225 3VD EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5226 3VE EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5227 3VF EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5228 3VG EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5229 3VH EMPTY LARGE 5 Io IWD LARGE unpaletted +0x522a 3VI EMPTY LARGE 5 Io IWD LARGE unpaletted +0x522b 3VJ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x522c 3VK EMPTY LARGE 5 Io IWD LARGE unpaletted +0x522d 3VL EMPTY LARGE 5 Io IWD LARGE unpaletted +0x522e 3VM EMPTY LARGE 5 Io IWD LARGE unpaletted +0x522f 3VN EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5230 3VO EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5231 3VP EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5232 3VQ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5233 3VR EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5234 3VS EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5235 3VT EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5236 3VU EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5237 3VV EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5238 3VW EMPTY LARGE 5 Io IWD LARGE unpaletted +0x5239 3VX EMPTY LARGE 5 Io IWD LARGE unpaletted +0x523a 3VY EMPTY LARGE 5 Io IWD LARGE unpaletted +0x523b 3VZ EMPTY LARGE 5 Io IWD LARGE unpaletted +0x523d 3AA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x523e 3AB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x523f 3AC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5240 3AD EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5241 3AE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5242 3AF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5243 3AG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5244 3AH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5245 3AI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5246 3AJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5247 3AK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5248 3AL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5249 3AM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x524a 3AN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x524b 3AO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x524c 3AP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x524d 3AQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x524e 3AR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x524f 3AS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5250 3AT EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5251 3AU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5252 3AV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5253 3AW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5254 3AX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5255 3AY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5256 3AZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5257 3BA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5258 3BB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5259 3BC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x525a 3BD EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x525b 3BE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x525c 3BF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x525d 3BG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x525e 3BH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x525f 3BI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5260 3BJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5261 3BK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5262 3BL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5263 3BM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5264 3BN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5265 3BO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5266 3BP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5267 3BQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5268 3BR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5269 3BS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x526a 3BT EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x526b 3BU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x526c 3BV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x526d 3BW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x526e 3BX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x526f 3BY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5270 3BZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5271 3CA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5272 3CB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5273 3CC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5274 3CD EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5275 3CE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5276 3CF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5277 3CG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5278 3CH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5279 3CI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x527a 3CJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x527b 3CK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x527c 3CL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x527d 3CM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x527e 3CN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x527f 3CO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5280 3CP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5281 3CQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5282 3CR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5283 3CS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5284 3CT EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5285 3CU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5286 3CV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5287 3CW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5288 3CX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5289 3CY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x528a 3CZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x528b 3DA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x528c 3DB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x528d 3DC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x528e 3DD EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x528f 3DE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5290 3DF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5291 3DG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5292 3DH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5293 3DI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5294 3DJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5295 3DK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5296 3DL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5297 3DM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5298 3DN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5299 3DO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x529a 3DP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x529b 3DQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x529c 3DR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x529d 3DS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x529e 3DT EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x529f 3DU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52a0 3DV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52a1 3DW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52a2 3DX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52a3 3DY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52a4 3DZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52a5 3EA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52a6 3EB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52a7 3EC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52a8 3ED EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52a9 3EE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52aa 3EF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52ab 3EG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52ac 3EH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52ad 3EI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52ae 3EJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52af 3EK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52b0 3EL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52b1 3EM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52b2 3EN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52b3 3EO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52b4 3EP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52b5 3EQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52b6 3ER EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52b7 3ES EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52b8 3ET EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52b9 3EU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52ba 3EV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52bb 3EW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52bc 3EX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52bd 3EY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52be 3EZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52bf 3FA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52c0 3FB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52c1 3FC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52c2 3FD EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52c3 3FE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52c4 3FF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52c5 3FG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52c6 3FH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52c7 3FI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52c8 3FJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52c9 3FK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52ca 3FL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52cb 3FM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52cc 3FN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52cd 3FO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52ce 3FP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52cf 3FQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52d0 3FR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52d1 3FS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52d2 3FT EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52d3 3FU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52d4 3FV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52d5 3FW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52d6 3FX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52d7 3FY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52d8 3FZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52d9 3GA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52da 3GB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52db 3GC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52dc 3GD EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52dd 3GE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52de 3GF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52df 3GG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52e0 3GH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52e1 3GI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52e2 3GJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52e3 3GK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52e4 3GL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52e5 3GM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52e6 3GN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52e7 3GO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52e8 3GP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52e9 3GQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52ea 3GR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52eb 3GS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52ec 3GT EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52ed 3GU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52ee 3GV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52ef 3GW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52f0 3GX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52f1 3GY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52f2 3GZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52f3 3HA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52f4 3HB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52f5 3HC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52f6 3HD EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52f7 3HE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52f8 3HF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52f9 3HG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52fa 3HH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52fb 3HI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52fc 3HJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52fd 3HK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52fe 3HL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x52ff 3HM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5300 3HN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5301 3HO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5302 3HP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5303 3HQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5304 3HR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5305 3HS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5306 3HT EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5307 3HU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5308 3HV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5309 3HW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x530a 3HX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x530b 3HY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x530c 3HZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x530d 3IA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x530e 3IB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x530f 3IC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5310 3ID EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5311 3IE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5312 3IF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5313 3IG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5314 3IH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5315 3II EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5316 3IJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5317 3IK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5318 3IL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5319 3IM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x531a 3IN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x531b 3IO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x531c 3IP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x531d 3IQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x531e 3IR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x531f 3IS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5320 3IT EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5321 3IU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5322 3IV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5323 3IW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5324 3IX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5325 3IY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5326 3IZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5327 3JA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5328 3JB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5329 3JC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x532a 3JD EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x532b 3JE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x532c 3JF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x532d 3JG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x532e 3JH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x532f 3JI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5330 3JJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5331 3JK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5332 3JL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5333 3JM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5334 3JN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5335 3JO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5336 3JP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5337 3JQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5338 3JR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5339 3JS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x533a 3JT EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x533b 3JU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x533c 3JV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x533d 3JW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x533e 3JX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x533f 3JY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5340 3JZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5341 3KA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5342 3KB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5343 3KC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5344 3KD EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5345 3KE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5346 3KF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5347 3KG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5348 3KH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5349 3KI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x534a 3KJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x534b 3KK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x534c 3KL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x534d 3KM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x534e 3KN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x534f 3KO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5350 3KP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5351 3KQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5352 3KR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5353 3KS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5354 3KT EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5355 3KU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5356 3KV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5357 3KW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5358 3KX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5359 3KY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x535a 3KZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x535b 3LA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x535c 3LB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x535d 3LC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x535e 3LD EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x535f 3LE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5360 3LF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5361 3LG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5362 3LH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5363 3LI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5364 3LJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5365 3LK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5366 3LL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5367 3LM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5368 3LN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5369 3LO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x536a 3LP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x536b 3LQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x536c 3LR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x536d 3LS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x536e 3LT EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x536f 3LU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5370 3LV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5371 3LW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5372 3LX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5373 3LY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5374 3LZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5375 3MA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5376 3MB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5377 3MC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5378 3MD EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5379 3ME EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x537a 3MF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x537b 3MG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x537c 3MH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x537d 3MI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x537e 3MJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x537f 3MK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5380 3ML EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5381 3MM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5382 3MN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5383 3MO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5384 3MP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5385 3MQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5386 3MR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5387 3MS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5388 3MT EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5389 3MU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x538a 3MV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x538b 3MW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x538c 3MX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x538d 3MY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x538e 3MZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x538f 3NA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5390 3NB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5391 3NC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5392 3ND EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5393 3NE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5394 3NF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5395 3NG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5396 3NH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5397 3NI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5398 3NJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5399 3NK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x539a 3NL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x539b 3NM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x539c 3NN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x539d 3NO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x539e 3NP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x539f 3NQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53a0 3NR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53a1 3NS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53a2 3NT EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53a3 3NU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53a4 3NV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53a5 3NW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53a6 3NX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53a7 3NY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53a8 3NZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53a9 3OA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53aa 3OB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53ab 3OC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53ac 3OD EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53ad 3OE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53ae 3OF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53af 3OG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53b0 3OH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53b1 3OI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53b2 3OJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53b3 3OK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53b4 3OL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53b5 3OM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53b6 3ON EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53b7 3OO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53b8 3OP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53b9 3OQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53ba 3OR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53bb 3OS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53bc 3OT EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53bd 3OU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53be 3OV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53bf 3OW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53c0 3OX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53c1 3OY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53c2 3OZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53c3 3PA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53c4 3PB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53c5 3PC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53c6 3PD EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53c7 3PE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53c8 3PF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53c9 3PG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53ca 3PH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53cb 3PI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53cc 3PJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53cd 3PK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53ce 3PL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53cf 3PM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53d0 3PN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53d1 3PO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53d2 3PP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53d3 3PQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53d4 3PR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53d5 3PS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53d6 3PT EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53d7 3PU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53d8 3PV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53d9 3PW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53da 3PX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53db 3PY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53dc 3PZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53dd 3QA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53de 3QB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53df 3QC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53e0 3QD EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53e1 3QE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53e2 3QF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53e3 3QG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53e4 3QH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53e5 3QI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53e6 3QJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53e7 3QK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53e8 3QL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53e9 3QM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53ea 3QN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53eb 3QO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53ec 3QP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53ed 3QQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53ee 3QR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53ef 3QS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53f0 3QT EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53f1 3QU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53f2 3QV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53f3 3QW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53f4 3QX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53f5 3QY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53f6 3QZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53f7 3RA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53f8 3RB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53f9 3RC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53fa 3RD EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53fb 3RE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53fc 3RF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53fd 3RG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53fe 3RH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x53ff 3RI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5400 3RJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5401 3RK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5402 3RL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5403 3RM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5404 3RN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5405 3RO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5406 3RP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5407 3RQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5408 3RR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5409 3RS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x540a 3RT EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x540b 3RU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x540c 3RV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x540d 3RW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x540e 3RX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x540f 3RY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5410 3RZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5411 3SA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5412 3SB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5413 3SC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5414 3SD EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5415 3SE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5416 3SF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5417 3SG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5418 3SH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5419 3SI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x541a 3SJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x541b 3SK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x541c 3SL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x541d 3SM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x541e 3SN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x541f 3SO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5420 3SP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5421 3SQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5422 3SR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5423 3SS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5424 3ST EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5425 3SU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5426 3SV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5427 3SW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5428 3SX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5429 3SY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x542a 3SZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x542b 3TA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x542c 3TB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x542d 3TC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x542e 3TD EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x542f 3TE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5430 3TF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5431 3TG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5432 3TH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5433 3TI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5434 3TJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5435 3TK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5436 3TL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5437 3TM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5438 3TN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5439 3TO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x543a 3TP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x543b 3TQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x543c 3TR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x543d 3TS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x543e 3TT EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x543f 3TU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5440 3TV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5441 3TW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5442 3TX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5443 3TY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5444 3TZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5445 3UA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5446 3UB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5447 3UC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5448 3UD EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5449 3UE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x544a 3UF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x544b 3UG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x544c 3UH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x544d 3UI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x544e 3UJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x544f 3UK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5450 3UL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5451 3UM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5452 3UN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5453 3UO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5454 3UP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5455 3UQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5456 3UR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5457 3US EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5458 3UT EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5459 3UU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x545a 3UV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x545b 3UW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x545c 3UX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x545d 3UY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x545e 3UZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x545f 3VA EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5460 3VB EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5461 3VC EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5462 3VD EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5463 3VE EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5464 3VF EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5465 3VG EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5466 3VH EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5467 3VI EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5468 3VJ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5469 3VK EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x546a 3VL EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x546b 3VM EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x546c 3VN EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x546d 3VO EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x546e 3VP EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x546f 3VQ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5470 3VR EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5471 3VS EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5472 3VT EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5473 3VU EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5474 3VV EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5475 3VW EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5476 3VX EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5477 3VY EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x5478 3VZ EMPTY NORMAL 3 Io IWD SMALL unpaletted +0x547a 1AA EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x547b 1AB EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x547c 1AC EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x547d 1AD EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x547e 1AE EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x547f 1AF EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5480 1AG EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5481 1AH EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5482 1AI EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5483 1AJ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5484 1AK EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5485 1AL EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5486 1AM EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5487 1AN EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5488 1AO EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5489 1AP EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x548a 1AQ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x548b 1AR EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x548c 1AS EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x548d 1AT EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x548e 1AU EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x548f 1AV EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5490 1AW EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5491 1AX EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5492 1AY EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5493 1AZ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5494 1BA EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5495 1BB EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5496 1BC EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5497 1BD EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5498 1BE EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5499 1BF EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x549a 1BG EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x549b 1BH EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x549c 1BI EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x549d 1BJ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x549e 1BK EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x549f 1BL EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54a0 1BM EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54a1 1BN EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54a2 1BO EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54a3 1BP EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54a4 1BQ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54a5 1BR EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54a6 1BS EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54a7 1BT EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54a8 1BU EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54a9 1BV EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54aa 1BW EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54ab 1BX EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54ac 1BY EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54ad 1BZ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54ae 1CA EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54af 1CB EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54b0 1CC EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54b1 1CD EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54b2 1CE EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54b3 1CF EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54b4 1CG EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54b5 1CH EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54b6 1CI EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54b7 1CJ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54b8 1CK EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54b9 1CL EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54ba 1CM EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54bb 1CN EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54bc 1CO EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54bd 1CP EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54be 1CQ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54bf 1CR EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54c0 1CS EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54c1 1CT EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54c2 1CU EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54c3 1CV EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54c4 1CW EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54c5 1CX EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54c6 1CY EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54c7 1CZ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54c8 1DA EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54c9 1DB EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54ca 1DC EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54cb 1DD EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54cc 1DE EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54cd 1DF EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54ce 1DG EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54cf 1DH EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54d0 1DI EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54d1 1DJ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54d2 1DK EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54d3 1DL EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54d4 1DM EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54d5 1DN EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54d6 1DO EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54d7 1DP EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54d8 1DQ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54d9 1DR EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54da 1DS EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54db 1DT EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54dc 1DU EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54dd 1DV EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54de 1DW EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54df 1DX EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54e0 1DY EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54e1 1DZ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54e2 1EA EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54e3 1EB EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54e4 1EC EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54e5 1ED EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54e6 1EE EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54e7 1EF EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54e8 1EG EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54e9 1EH EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54ea 1EI EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54eb 1EJ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54ec 1EK EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54ed 1EL EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54ee 1EM EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54ef 1EN EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54f0 1EO EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54f1 1EP EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54f2 1EQ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54f3 1ER EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54f4 1ES EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54f5 1ET EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54f6 1EU EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54f7 1EV EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54f8 1EW EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54f9 1EX EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54fa 1EY EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54fb 1EZ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54fc 1FA EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54fd 1FB EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54fe 1FC EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x54ff 1FD EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5500 1FE EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5501 1FF EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5502 1FG EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5503 1FH EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5504 1FI EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5505 1FJ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5506 1FK EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5507 1FL EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5508 1FM EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5509 1FN EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x550a 1FO EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x550b 1FP EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x550c 1FQ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x550d 1FR EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x550e 1FS EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x550f 1FT EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5510 1FU EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5511 1FV EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5512 1FW EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5513 1FX EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5514 1FY EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5515 1FZ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5516 1GA EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5517 1GB EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5518 1GC EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5519 1GD EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x551a 1GE EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x551b 1GF EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x551c 1GG EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x551d 1GH EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x551e 1GI EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x551f 1GJ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5520 1GK EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5521 1GL EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5522 1GM EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5523 1GN EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5524 1GO EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5525 1GP EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5526 1GQ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5527 1GR EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5528 1GS EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5529 1GT EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x552a 1GU EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x552b 1GV EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x552c 1GW EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x552d 1GX EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x552e 1GY EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x552f 1GZ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5530 1HA EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5531 1HB EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5532 1HC EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5533 1HD EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5534 1HE EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5535 1HF EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5536 1HG EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5537 1HH EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5538 1HI EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5539 1HJ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x553a 1HK EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x553b 1HL EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x553c 1HM EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x553d 1HN EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x553e 1HO EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x553f 1HP EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5540 1HQ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5541 1HR EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5542 1HS EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5543 1HT EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5544 1HU EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5545 1HV EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5546 1HW EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5547 1HX EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5548 1HY EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5549 1HZ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x554a 1IA EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x554b 1IB EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x554c 1IC EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x554d 1ID EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x554e 1IE EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x554f 1IF EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5550 1IG EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5551 1IH EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5552 1II EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5553 1IJ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5554 1IK EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5555 1IL EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5556 1IM EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5557 1IN EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5558 1IO EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5559 1IP EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x555a 1IQ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x555b 1IR EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x555c 1IS EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x555d 1IT EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x555e 1IU EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x555f 1IV EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5560 1IW EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5561 1IX EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5562 1IY EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5563 1IZ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5564 1JA EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5565 1JB EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5566 1JC EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5567 1JD EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5568 1JE EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5569 1JF EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x556a 1JG EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x556b 1JH EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x556c 1JI EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x556d 1JJ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x556e 1JK EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x556f 1JL EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5570 1JM EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5571 1JN EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5572 1JO EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5573 1JP EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5574 1JQ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5575 1JR EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5576 1JS EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5577 1JT EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5578 1JU EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x5579 1JV EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x557a 1JW EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER unpaletted +0x557b 1JX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x557c 1JY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x557d 1JZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x557e 1KA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x557f 1KB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5580 1KC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5581 1KD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5582 1KE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5583 1KF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5584 1KG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5585 1KH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5586 1KI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5587 1KJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5588 1KK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5589 1KL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x558a 1KM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x558b 1KN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x558c 1KO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x558d 1KP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x558e 1KQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x558f 1KR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5590 1KS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5591 1KT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5592 1KU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5593 1KV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5594 1KW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5595 1KX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5596 1KY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5597 1KZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5598 1LA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5599 1LB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x559a 1LC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x559b 1LD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x559c 1LE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x559d 1LF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x559e 1LG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x559f 1LH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55a0 1LI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55a1 1LJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55a2 1LK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55a3 1LL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55a4 1LM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55a5 1LN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55a6 1LO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55a7 1LP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55a8 1LQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55a9 1LR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55aa 1LS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55ab 1LT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55ac 1LU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55ad 1LV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55ae 1LW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55af 1LX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55b0 1LY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55b1 1LZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55b2 1MA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55b3 1MB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55b4 1MC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55b5 1MD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55b6 1ME EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55b7 1MF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55b8 1MG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55b9 1MH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55ba 1MI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55bb 1MJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55bc 1MK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55bd 1ML EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55be 1MM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55bf 1MN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55c0 1MO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55c1 1MP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55c2 1MQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55c3 1MR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55c4 1MS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55c5 1MT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55c6 1MU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55c7 1MV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55c8 1MW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55c9 1MX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55ca 1MY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55cb 1MZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55cc 1NA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55cd 1NB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55ce 1NC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55cf 1ND EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55d0 1NE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55d1 1NF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55d2 1NG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55d3 1NH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55d4 1NI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55d5 1NJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55d6 1NK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55d7 1NL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55d8 1NM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55d9 1NN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55da 1NO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55db 1NP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55dc 1NQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55dd 1NR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55de 1NS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55df 1NT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55e0 1NU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55e1 1NV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55e2 1NW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55e3 1NX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55e4 1NY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55e5 1NZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55e6 1OA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55e7 1OB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55e8 1OC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55e9 1OD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55ea 1OE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55eb 1OF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55ec 1OG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55ed 1OH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55ee 1OI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55ef 1OJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55f0 1OK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55f1 1OL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55f2 1OM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55f3 1ON EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55f4 1OO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55f5 1OP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55f6 1OQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55f7 1OR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55f8 1OS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55f9 1OT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55fa 1OU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55fb 1OV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55fc 1OW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55fd 1OX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55fe 1OY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x55ff 1OZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5600 1PA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5601 1PB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5602 1PC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5603 1PD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5604 1PE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5605 1PF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5606 1PG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5607 1PH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5608 1PI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5609 1PJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x560a 1PK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x560b 1PL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x560c 1PM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x560d 1PN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x560e 1PO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x560f 1PP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5610 1PQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5611 1PR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5612 1PS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5613 1PT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5614 1PU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5615 1PV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5616 1PW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5617 1PX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5618 1PY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5619 1PZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x561a 1QA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x561b 1QB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x561c 1QC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x561d 1QD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x561e 1QE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x561f 1QF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5620 1QG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5621 1QH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5622 1QI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5623 1QJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5624 1QK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5625 1QL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5626 1QM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5627 1QN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5628 1QO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5629 1QP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x562a 1QQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x562b 1QR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x562c 1QS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x562d 1QT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x562e 1QU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x562f 1QV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5630 1QW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5631 1QX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5632 1QY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5633 1QZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5634 1RA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5635 1RB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5636 1RC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5637 1RD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5638 1RE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5639 1RF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x563a 1RG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x563b 1RH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x563c 1RI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x563d 1RJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x563e 1RK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x563f 1RL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5640 1RM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5641 1RN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5642 1RO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5643 1RP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5644 1RQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5645 1RR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5646 1RS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5647 1RT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5648 1RU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5649 1RV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x564a 1RW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x564b 1RX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x564c 1RY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x564d 1RZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x564e 1SA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x564f 1SB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5650 1SC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5651 1SD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5652 1SE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5653 1SF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5654 1SG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5655 1SH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5656 1SI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5657 1SJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5658 1SK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5659 1SL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x565a 1SM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x565b 1SN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x565c 1SO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x565d 1SP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x565e 1SQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x565f 1SR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5660 1SS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5661 1ST EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5662 1SU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5663 1SV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5664 1SW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5665 1SX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5666 1SY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5667 1SZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5668 1TA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5669 1TB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x566a 1TC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x566b 1TD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x566c 1TE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x566d 1TF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x566e 1TG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x566f 1TH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5670 1TI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5671 1TJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5672 1TK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5673 1TL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5674 1TM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5675 1TN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5676 1TO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5677 1TP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5678 1TQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5679 1TR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x567a 1TS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x567b 1TT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x567c 1TU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x567d 1TV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x567e 1TW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x567f 1TX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5680 1TY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5681 1TZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5682 1UA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5683 1UB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5684 1UC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5685 1UD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5686 1UE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5687 1UF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5688 1UG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5689 1UH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x568a 1UI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x568b 1UJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x568c 1UK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x568d 1UL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x568e 1UM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x568f 1UN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5690 1UO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5691 1UP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5692 1UQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5693 1UR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5694 1US EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5695 1UT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5696 1UU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5697 1UV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5698 1UW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x5699 1UX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x569a 1UY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x569b 1UZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x569c 1VA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x569d 1VB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x569e 1VC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x569f 1VD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56a0 1VE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56a1 1VF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56a2 1VG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56a3 1VH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56a4 1VI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56a5 1VJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56a6 1VK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56a7 1VL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56a8 1VM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56a9 1VN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56aa 1VO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56ab 1VP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56ac 1VQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56ad 1VR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56ae 1VS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56af 1VT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56b0 1VU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56b1 1VV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56b2 1VW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56b3 1VX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56b4 1VY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56b5 1VZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56b6 1WA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER unpaletted +0x56b7 1AA EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56b8 1AB EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56b9 1AC EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56ba 1AD EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56bb 1AE EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56bc 1AF EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56bd 1AG EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56be 1AH EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56bf 1AI EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56c0 1AJ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56c1 1AK EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56c2 1AL EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56c3 1AM EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56c4 1AN EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56c5 1AO EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56c6 1AP EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56c7 1AQ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56c8 1AR EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56c9 1AS EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56ca 1AT EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56cb 1AU EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56cc 1AV EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56cd 1AW EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56ce 1AX EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56cf 1AY EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56d0 1AZ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56d1 1BA EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56d2 1BB EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56d3 1BC EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56d4 1BD EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56d5 1BE EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56d6 1BF EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56d7 1BG EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56d8 1BH EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56d9 1BI EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56da 1BJ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56db 1BK EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56dc 1BL EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56dd 1BM EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56de 1BN EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56df 1BO EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56e0 1BP EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56e1 1BQ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56e2 1BR EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56e3 1BS EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56e4 1BT EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56e5 1BU EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56e6 1BV EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56e7 1BW EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56e8 1BX EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56e9 1BY EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56ea 1BZ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56eb 1CA EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56ec 1CB EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56ed 1CC EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56ee 1CD EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56ef 1CE EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56f0 1CF EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56f1 1CG EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56f2 1CH EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56f3 1CI EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56f4 1CJ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56f5 1CK EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56f6 1CL EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56f7 1CM EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56f8 1CN EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56f9 1CO EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56fa 1CP EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56fb 1CQ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56fc 1CR EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56fd 1CS EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56fe 1CT EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x56ff 1CU EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5700 1CV EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5701 1CW EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5702 1CX EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5703 1CY EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5704 1CZ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5705 1DA EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5706 1DB EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5707 1DC EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5708 1DD EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5709 1DE EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x570a 1DF EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x570b 1DG EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x570c 1DH EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x570d 1DI EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x570e 1DJ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x570f 1DK EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5710 1DL EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5711 1DM EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5712 1DN EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5713 1DO EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5714 1DP EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5715 1DQ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5716 1DR EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5717 1DS EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5718 1DT EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5719 1DU EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x571a 1DV EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x571b 1DW EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x571c 1DX EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x571d 1DY EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x571e 1DZ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x571f 1EA EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5720 1EB EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5721 1EC EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5722 1ED EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5723 1EE EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5724 1EF EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5725 1EG EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5726 1EH EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5727 1EI EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5728 1EJ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5729 1EK EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x572a 1EL EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x572b 1EM EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x572c 1EN EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x572d 1EO EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x572e 1EP EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x572f 1EQ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5730 1ER EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5731 1ES EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5732 1ET EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5733 1EU EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5734 1EV EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5735 1EW EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5736 1EX EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5737 1EY EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5738 1EZ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5739 1FA EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x573a 1FB EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x573b 1FC EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x573c 1FD EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x573d 1FE EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x573e 1FF EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x573f 1FG EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5740 1FH EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5741 1FI EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5742 1FJ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5743 1FK EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5744 1FL EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5745 1FM EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5746 1FN EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5747 1FO EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5748 1FP EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5749 1FQ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x574a 1FR EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x574b 1FS EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x574c 1FT EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x574d 1FU EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x574e 1FV EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x574f 1FW EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5750 1FX EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5751 1FY EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5752 1FZ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5753 1GA EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5754 1GB EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5755 1GC EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5756 1GD EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5757 1GE EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5758 1GF EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5759 1GG EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x575a 1GH EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x575b 1GI EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x575c 1GJ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x575d 1GK EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x575e 1GL EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x575f 1GM EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5760 1GN EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5761 1GO EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5762 1GP EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5763 1GQ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5764 1GR EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5765 1GS EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5766 1GT EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5767 1GU EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5768 1GV EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5769 1GW EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x576a 1GX EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x576b 1GY EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x576c 1GZ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x576d 1HA EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x576e 1HB EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x576f 1HC EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5770 1HD EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5771 1HE EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5772 1HF EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5773 1HG EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5774 1HH EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5775 1HI EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5776 1HJ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5777 1HK EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5778 1HL EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5779 1HM EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x577a 1HN EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x577b 1HO EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x577c 1HP EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x577d 1HQ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x577e 1HR EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x577f 1HS EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5780 1HT EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5781 1HU EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5782 1HV EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5783 1HW EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5784 1HX EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5785 1HY EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5786 1HZ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5787 1IA EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5788 1IB EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5789 1IC EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x578a 1ID EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x578b 1IE EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x578c 1IF EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x578d 1IG EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x578e 1IH EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x578f 1II EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5790 1IJ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5791 1IK EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5792 1IL EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5793 1IM EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5794 1IN EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5795 1IO EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5796 1IP EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5797 1IQ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5798 1IR EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x5799 1IS EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x579a 1IT EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x579b 1IU EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x579c 1IV EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x579d 1IW EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x579e 1IX EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x579f 1IY EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57a0 1IZ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57a1 1JA EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57a2 1JB EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57a3 1JC EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57a4 1JD EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57a5 1JE EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57a6 1JF EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57a7 1JG EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57a8 1JH EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57a9 1JI EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57aa 1JJ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57ab 1JK EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57ac 1JL EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57ad 1JM EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57ae 1JN EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57af 1JO EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57b0 1JP EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57b1 1JQ EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57b2 1JR EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57b3 1JS EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57b4 1JT EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57b5 1JU EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57b6 1JV EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57b7 1JW EMPTY NORMAL 3 Xo BGI SIMPLE MONSTER paletted +0x57b8 1JX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57b9 1JY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57ba 1JZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57bb 1KA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57bc 1KB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57bd 1KC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57be 1KD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57bf 1KE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57c0 1KF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57c1 1KG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57c2 1KH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57c3 1KI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57c4 1KJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57c5 1KK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57c6 1KL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57c7 1KM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57c8 1KN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57c9 1KO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57ca 1KP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57cb 1KQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57cc 1KR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57cd 1KS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57ce 1KT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57cf 1KU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57d0 1KV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57d1 1KW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57d2 1KX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57d3 1KY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57d4 1KZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57d5 1LA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57d6 1LB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57d7 1LC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57d8 1LD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57d9 1LE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57da 1LF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57db 1LG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57dc 1LH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57dd 1LI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57de 1LJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57df 1LK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57e0 1LL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57e1 1LM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57e2 1LN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57e3 1LO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57e4 1LP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57e5 1LQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57e6 1LR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57e7 1LS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57e8 1LT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57e9 1LU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57ea 1LV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57eb 1LW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57ec 1LX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57ed 1LY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57ee 1LZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57ef 1MA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57f0 1MB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57f1 1MC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57f2 1MD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57f3 1ME EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57f4 1MF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57f5 1MG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57f6 1MH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57f7 1MI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57f8 1MJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57f9 1MK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57fa 1ML EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57fb 1MM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57fc 1MN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57fd 1MO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57fe 1MP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x57ff 1MQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5800 1MR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5801 1MS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5802 1MT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5803 1MU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5804 1MV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5805 1MW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5806 1MX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5807 1MY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5808 1MZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5809 1NA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x580a 1NB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x580b 1NC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x580c 1ND EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x580d 1NE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x580e 1NF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x580f 1NG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5810 1NH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5811 1NI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5812 1NJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5813 1NK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5814 1NL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5815 1NM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5816 1NN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5817 1NO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5818 1NP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5819 1NQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x581a 1NR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x581b 1NS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x581c 1NT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x581d 1NU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x581e 1NV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x581f 1NW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5820 1NX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5821 1NY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5822 1NZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5823 1OA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5824 1OB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5825 1OC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5826 1OD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5827 1OE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5828 1OF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5829 1OG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x582a 1OH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x582b 1OI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x582c 1OJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x582d 1OK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x582e 1OL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x582f 1OM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5830 1ON EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5831 1OO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5832 1OP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5833 1OQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5834 1OR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5835 1OS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5836 1OT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5837 1OU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5838 1OV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5839 1OW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x583a 1OX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x583b 1OY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x583c 1OZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x583d 1PA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x583e 1PB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x583f 1PC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5840 1PD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5841 1PE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5842 1PF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5843 1PG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5844 1PH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5845 1PI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5846 1PJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5847 1PK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5848 1PL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5849 1PM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x584a 1PN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x584b 1PO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x584c 1PP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x584d 1PQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x584e 1PR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x584f 1PS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5850 1PT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5851 1PU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5852 1PV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5853 1PW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5854 1PX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5855 1PY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5856 1PZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5857 1QA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5858 1QB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5859 1QC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x585a 1QD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x585b 1QE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x585c 1QF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x585d 1QG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x585e 1QH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x585f 1QI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5860 1QJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5861 1QK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5862 1QL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5863 1QM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5864 1QN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5865 1QO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5866 1QP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5867 1QQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5868 1QR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5869 1QS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x586a 1QT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x586b 1QU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x586c 1QV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x586d 1QW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x586e 1QX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x586f 1QY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5870 1QZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5871 1RA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5872 1RB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5873 1RC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5874 1RD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5875 1RE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5876 1RF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5877 1RG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5878 1RH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5879 1RI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x587a 1RJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x587b 1RK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x587c 1RL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x587d 1RM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x587e 1RN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x587f 1RO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5880 1RP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5881 1RQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5882 1RR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5883 1RS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5884 1RT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5885 1RU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5886 1RV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5887 1RW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5888 1RX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5889 1RY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x588a 1RZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x588b 1SA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x588c 1SB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x588d 1SC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x588e 1SD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x588f 1SE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5890 1SF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5891 1SG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5892 1SH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5893 1SI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5894 1SJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5895 1SK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5896 1SL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5897 1SM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5898 1SN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5899 1SO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x589a 1SP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x589b 1SQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x589c 1SR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x589d 1SS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x589e 1ST EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x589f 1SU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58a0 1SV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58a1 1SW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58a2 1SX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58a3 1SY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58a4 1SZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58a5 1TA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58a6 1TB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58a7 1TC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58a8 1TD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58a9 1TE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58aa 1TF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58ab 1TG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58ac 1TH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58ad 1TI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58ae 1TJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58af 1TK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58b0 1TL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58b1 1TM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58b2 1TN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58b3 1TO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58b4 1TP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58b5 1TQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58b6 1TR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58b7 1TS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58b8 1TT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58b9 1TU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58ba 1TV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58bb 1TW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58bc 1TX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58bd 1TY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58be 1TZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58bf 1UA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58c0 1UB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58c1 1UC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58c2 1UD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58c3 1UE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58c4 1UF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58c5 1UG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58c6 1UH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58c7 1UI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58c8 1UJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58c9 1UK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58ca 1UL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58cb 1UM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58cc 1UN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58cd 1UO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58ce 1UP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58cf 1UQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58d0 1UR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58d1 1US EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58d2 1UT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58d3 1UU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58d4 1UV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58d5 1UW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58d6 1UX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58d7 1UY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58d8 1UZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58d9 1VA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58da 1VB EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58db 1VC EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58dc 1VD EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58dd 1VE EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58de 1VF EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58df 1VG EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58e0 1VH EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58e1 1VI EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58e2 1VJ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58e3 1VK EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58e4 1VL EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58e5 1VM EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58e6 1VN EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58e7 1VO EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58e8 1VP EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58e9 1VQ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58ea 1VR EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58eb 1VS EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58ec 1VT EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58ed 1VU EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58ee 1VV EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58ef 1VW EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58f0 1VX EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58f1 1VY EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58f2 1VZ EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x58f3 1WA EMPTY LARGE 5 Xo BGI SIMPLE MONSTER paletted +0x5b00 3AA EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b01 3AB EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b02 3AC EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b03 3AD EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b04 3AE EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b05 3AF EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b06 3AG EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b07 3AH EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b08 3AI EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b09 3AJ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b0a 3AK EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b0b 3AL EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b0c 3AM EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b0d 3AN EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b0e 3AO EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b0f 3AP EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b10 3AQ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b11 3AR EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b12 3AS EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b13 3AT EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b14 3AU EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b15 3AV EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b16 3AW EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b17 3AX EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b18 3AY EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b19 3AZ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b1a 3BA EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b1b 3BB EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b1c 3BC EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b1d 3BD EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b1e 3BE EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b1f 3BF EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b20 3BG EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b21 3BH EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b22 3BI EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b23 3BJ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b24 3BK EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b25 3BL EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b26 3BM EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b27 3BN EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b28 3BO EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b29 3BP EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b2a 3BQ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b2b 3BR EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b2c 3BS EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b2d 3BT EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b2e 3BU EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b2f 3BV EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b30 3BW EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b31 3BX EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b32 3BY EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b33 3BZ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b34 3CA EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b35 3CB EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b36 3CC EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b37 3CD EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b38 3CE EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b39 3CF EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b3a 3CG EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b3b 3CH EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b3c 3CI EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b3d 3CJ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b3e 3CK EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b3f 3CL EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b40 3CM EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b41 3CN EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b42 3CO EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b43 3CP EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b44 3CQ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b45 3CR EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b46 3CS EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b47 3CT EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b48 3CU EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b49 3CV EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b4a 3CW EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b4b 3CX EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b4c 3CY EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b4d 3CZ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b4e 3DA EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b4f 3DB EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b50 3DC EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b51 3DD EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b52 3DE EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b53 3DF EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b54 3DG EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b55 3DH EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b56 3DI EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b57 3DJ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b58 3DK EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b59 3DL EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b5a 3DM EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b5b 3DN EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b5c 3DO EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b5d 3DP EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b5e 3DQ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b5f 3DR EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b60 3DS EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b61 3DT EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b62 3DU EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b63 3DV EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b64 3DW EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b65 3DX EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b66 3DY EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b67 3DZ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b68 3EA EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b69 3EB EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b6a 3EC EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b6b 3ED EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b6c 3EE EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b6d 3EF EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b6e 3EG EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b6f 3EH EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b70 3EI EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b71 3EJ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b72 3EK EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b73 3EL EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b74 3EM EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b75 3EN EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b76 3EO EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b77 3EP EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b78 3EQ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b79 3ER EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b7a 3ES EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b7b 3ET EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b7c 3EU EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b7d 3EV EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b7e 3EW EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b7f 3EX EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b80 3EY EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b81 3EZ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b82 3FA EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b83 3FB EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b84 3FC EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b85 3FD EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b86 3FE EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b87 3FF EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b88 3FG EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b89 3FH EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b8a 3FI EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b8b 3FJ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b8c 3FK EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b8d 3FL EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b8e 3FM EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b8f 3FN EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b90 3FO EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b91 3FP EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b92 3FQ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b93 3FR EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b94 3FS EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b95 3FT EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b96 3FU EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b97 3FV EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b98 3FW EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b99 3FX EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b9a 3FY EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b9b 3FZ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b9c 3GA EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b9d 3GB EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b9e 3GC EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5b9f 3GD EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5ba0 3GE EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5ba1 3GF EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5ba2 3GG EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5ba3 3GH EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5ba4 3GI EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5ba5 3GJ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5ba6 3GK EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5ba7 3GL EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5ba8 3GM EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5ba9 3GN EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5baa 3GO EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bab 3GP EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bac 3GQ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bad 3GR EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bae 3GS EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5baf 3GT EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bb0 3GU EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bb1 3GV EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bb2 3GW EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bb3 3GX EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bb4 3GY EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bb5 3GZ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bb6 3HA EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bb7 3HB EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bb8 3HC EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bb9 3HD EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bba 3HE EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bbb 3HF EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bbc 3HG EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bbd 3HH EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bbe 3HI EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bbf 3HJ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bc0 3HK EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bc1 3HL EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bc2 3HM EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bc3 3HN EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bc4 3HO EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bc5 3HP EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bc6 3HQ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bc7 3HR EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bc8 3HS EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bc9 3HT EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bca 3HU EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bcb 3HV EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bcc 3HW EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bcd 3HX EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bce 3HY EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bcf 3HZ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bd0 3IA EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bd1 3IB EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bd2 3IC EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bd3 3ID EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bd4 3IE EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bd5 3IF EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bd6 3IG EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bd7 3IH EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bd8 3II EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bd9 3IJ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bda 3IK EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bdb 3IL EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bdc 3IM EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bdd 3IN EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bde 3IO EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bdf 3IP EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5be0 3IQ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5be1 3IR EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5be2 3IS EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5be3 3IT EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5be4 3IU EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5be5 3IV EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5be6 3IW EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5be7 3IX EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5be8 3IY EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5be9 3IZ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bea 3JA EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5beb 3JB EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bec 3JC EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bed 3JD EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bee 3JE EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bef 3JF EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bf0 3JG EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bf1 3JH EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bf2 3JI EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bf3 3JJ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bf4 3JK EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bf5 3JL EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bf6 3JM EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bf7 3JN EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bf8 3JO EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bf9 3JP EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bfa 3JQ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bfb 3JR EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bfc 3JS EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bfd 3JT EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bfe 3JU EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5bff 3JV EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. unpaletted +0x5c00 3JW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c01 3JX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c02 3JY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c03 3JZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c04 3KA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c05 3KB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c06 3KC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c07 3KD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c08 3KE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c09 3KF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c0a 3KG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c0b 3KH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c0c 3KI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c0d 3KJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c0e 3KK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c0f 3KL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c10 3KM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c11 3KN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c12 3KO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c13 3KP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c14 3KQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c15 3KR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c16 3KS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c17 3KT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c18 3KU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c19 3KV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c1a 3KW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c1b 3KX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c1c 3KY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c1d 3KZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c1e 3LA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c1f 3LB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c20 3LC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c21 3LD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c22 3LE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c23 3LF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c24 3LG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c25 3LH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c26 3LI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c27 3LJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c28 3LK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c29 3LL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c2a 3LM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c2b 3LN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c2c 3LO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c2d 3LP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c2e 3LQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c2f 3LR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c30 3LS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c31 3LT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c32 3LU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c33 3LV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c34 3LW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c35 3LX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c36 3LY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c37 3LZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c38 3MA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c39 3MB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c3a 3MC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c3b 3MD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c3c 3ME EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c3d 3MF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c3e 3MG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c3f 3MH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c40 3MI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c41 3MJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c42 3MK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c43 3ML EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c44 3MM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c45 3MN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c46 3MO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c47 3MP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c48 3MQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c49 3MR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c4a 3MS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c4b 3MT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c4c 3MU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c4d 3MV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c4e 3MW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c4f 3MX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c50 3MY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c51 3MZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c52 3NA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c53 3NB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c54 3NC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c55 3ND EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c56 3NE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c57 3NF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c58 3NG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c59 3NH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c5a 3NI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c5b 3NJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c5c 3NK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c5d 3NL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c5e 3NM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c5f 3NN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c60 3NO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c61 3NP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c62 3NQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c63 3NR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c64 3NS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c65 3NT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c66 3NU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c67 3NV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c68 3NW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c69 3NX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c6a 3NY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c6b 3NZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c6c 3OA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c6d 3OB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c6e 3OC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c6f 3OD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c70 3OE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c71 3OF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c72 3OG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c73 3OH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c74 3OI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c75 3OJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c76 3OK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c77 3OL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c78 3OM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c79 3ON EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c7a 3OO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c7b 3OP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c7c 3OQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c7d 3OR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c7e 3OS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c7f 3OT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c80 3OU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c81 3OV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c82 3OW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c83 3OX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c84 3OY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c85 3OZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c86 3PA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c87 3PB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c88 3PC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c89 3PD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c8a 3PE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c8b 3PF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c8c 3PG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c8d 3PH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c8e 3PI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c8f 3PJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c90 3PK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c91 3PL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c92 3PM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c93 3PN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c94 3PO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c95 3PP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c96 3PQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c97 3PR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c98 3PS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c99 3PT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c9a 3PU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c9b 3PV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c9c 3PW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c9d 3PX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c9e 3PY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5c9f 3PZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ca0 3QA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ca1 3QB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ca2 3QC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ca3 3QD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ca4 3QE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ca5 3QF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ca6 3QG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ca7 3QH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ca8 3QI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ca9 3QJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5caa 3QK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cab 3QL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cac 3QM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cad 3QN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cae 3QO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5caf 3QP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cb0 3QQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cb1 3QR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cb2 3QS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cb3 3QT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cb4 3QU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cb5 3QV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cb6 3QW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cb7 3QX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cb8 3QY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cb9 3QZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cba 3RA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cbb 3RB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cbc 3RC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cbd 3RD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cbe 3RE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cbf 3RF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cc0 3RG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cc1 3RH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cc2 3RI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cc3 3RJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cc4 3RK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cc5 3RL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cc6 3RM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cc7 3RN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cc8 3RO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cc9 3RP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cca 3RQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ccb 3RR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ccc 3RS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ccd 3RT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cce 3RU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ccf 3RV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cd0 3RW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cd1 3RX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cd2 3RY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cd3 3RZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cd4 3SA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cd5 3SB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cd6 3SC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cd7 3SD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cd8 3SE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cd9 3SF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cda 3SG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cdb 3SH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cdc 3SI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cdd 3SJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cde 3SK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cdf 3SL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ce0 3SM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ce1 3SN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ce2 3SO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ce3 3SP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ce4 3SQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ce5 3SR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ce6 3SS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ce7 3ST EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ce8 3SU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ce9 3SV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cea 3SW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ceb 3SX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cec 3SY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5ced 3SZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cee 3TA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cef 3TB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cf0 3TC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cf1 3TD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cf2 3TE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cf3 3TF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cf4 3TG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cf5 3TH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cf6 3TI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cf7 3TJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cf8 3TK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cf9 3TL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cfa 3TM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cfb 3TN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cfc 3TO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cfd 3TP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cfe 3TQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5cff 3TR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d00 3TS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d01 3TT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d02 3TU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d03 3TV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d04 3TW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d05 3TX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d06 3TY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d07 3TZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d08 3UA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d09 3UB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d0a 3UC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d0b 3UD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d0c 3UE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d0d 3UF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d0e 3UG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d0f 3UH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d10 3UI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d11 3UJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d12 3UK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d13 3UL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d14 3UM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d15 3UN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d16 3UO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d17 3UP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d18 3UQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d19 3UR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d1a 3US EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d1b 3UT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d1c 3UU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d1d 3UV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d1e 3UW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d1f 3UX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d20 3UY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d21 3UZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d22 3VA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d23 3VB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d24 3VC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d25 3VD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d26 3VE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d27 3VF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d28 3VG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d29 3VH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d2a 3VI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d2b 3VJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d2c 3VK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d2d 3VL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d2e 3VM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d2f 3VN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d30 3VO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d31 3VP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d32 3VQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d33 3VR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d34 3VS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d35 3VT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d36 3VU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d37 3VV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d38 3VW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d39 3VX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d3a 3VY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d3b 3VZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. unpaletted +0x5d3d 3AA EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d3e 3AB EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d3f 3AC EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d40 3AD EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d41 3AE EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d42 3AF EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d43 3AG EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d44 3AH EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d45 3AI EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d46 3AJ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d47 3AK EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d48 3AL EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d49 3AM EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d4a 3AN EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d4b 3AO EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d4c 3AP EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d4d 3AQ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d4e 3AR EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d4f 3AS EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d50 3AT EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d51 3AU EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d52 3AV EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d53 3AW EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d54 3AX EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d55 3AY EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d56 3AZ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d57 3BA EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d58 3BB EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d59 3BC EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d5a 3BD EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d5b 3BE EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d5c 3BF EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d5d 3BG EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d5e 3BH EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d5f 3BI EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d60 3BJ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d61 3BK EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d62 3BL EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d63 3BM EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d64 3BN EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d65 3BO EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d66 3BP EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d67 3BQ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d68 3BR EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d69 3BS EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d6a 3BT EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d6b 3BU EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d6c 3BV EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d6d 3BW EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d6e 3BX EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d6f 3BY EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d70 3BZ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d71 3CA EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d72 3CB EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d73 3CC EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d74 3CD EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d75 3CE EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d76 3CF EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d77 3CG EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d78 3CH EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d79 3CI EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d7a 3CJ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d7b 3CK EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d7c 3CL EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d7d 3CM EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d7e 3CN EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d7f 3CO EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d80 3CP EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d81 3CQ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d82 3CR EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d83 3CS EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d84 3CT EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d85 3CU EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d86 3CV EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d87 3CW EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d88 3CX EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d89 3CY EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d8a 3CZ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d8b 3DA EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d8c 3DB EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d8d 3DC EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d8e 3DD EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d8f 3DE EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d90 3DF EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d91 3DG EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d92 3DH EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d93 3DI EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d94 3DJ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d95 3DK EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d96 3DL EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d97 3DM EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d98 3DN EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d99 3DO EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d9a 3DP EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d9b 3DQ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d9c 3DR EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d9d 3DS EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d9e 3DT EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5d9f 3DU EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5da0 3DV EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5da1 3DW EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5da2 3DX EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5da3 3DY EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5da4 3DZ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5da5 3EA EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5da6 3EB EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5da7 3EC EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5da8 3ED EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5da9 3EE EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5daa 3EF EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dab 3EG EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dac 3EH EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dad 3EI EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dae 3EJ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5daf 3EK EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5db0 3EL EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5db1 3EM EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5db2 3EN EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5db3 3EO EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5db4 3EP EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5db5 3EQ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5db6 3ER EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5db7 3ES EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5db8 3ET EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5db9 3EU EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dba 3EV EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dbb 3EW EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dbc 3EX EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dbd 3EY EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dbe 3EZ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dbf 3FA EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dc0 3FB EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dc1 3FC EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dc2 3FD EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dc3 3FE EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dc4 3FF EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dc5 3FG EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dc6 3FH EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dc7 3FI EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dc8 3FJ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dc9 3FK EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dca 3FL EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dcb 3FM EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dcc 3FN EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dcd 3FO EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dce 3FP EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dcf 3FQ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dd0 3FR EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dd1 3FS EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dd2 3FT EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dd3 3FU EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dd4 3FV EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dd5 3FW EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dd6 3FX EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dd7 3FY EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dd8 3FZ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dd9 3GA EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dda 3GB EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5ddb 3GC EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5ddc 3GD EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5ddd 3GE EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dde 3GF EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5ddf 3GG EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5de0 3GH EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5de1 3GI EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5de2 3GJ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5de3 3GK EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5de4 3GL EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5de5 3GM EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5de6 3GN EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5de7 3GO EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5de8 3GP EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5de9 3GQ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dea 3GR EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5deb 3GS EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dec 3GT EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5ded 3GU EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dee 3GV EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5def 3GW EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5df0 3GX EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5df1 3GY EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5df2 3GZ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5df3 3HA EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5df4 3HB EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5df5 3HC EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5df6 3HD EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5df7 3HE EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5df8 3HF EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5df9 3HG EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dfa 3HH EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dfb 3HI EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dfc 3HJ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dfd 3HK EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dfe 3HL EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5dff 3HM EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e00 3HN EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e01 3HO EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e02 3HP EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e03 3HQ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e04 3HR EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e05 3HS EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e06 3HT EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e07 3HU EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e08 3HV EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e09 3HW EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e0a 3HX EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e0b 3HY EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e0c 3HZ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e0d 3IA EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e0e 3IB EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e0f 3IC EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e10 3ID EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e11 3IE EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e12 3IF EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e13 3IG EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e14 3IH EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e15 3II EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e16 3IJ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e17 3IK EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e18 3IL EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e19 3IM EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e1a 3IN EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e1b 3IO EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e1c 3IP EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e1d 3IQ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e1e 3IR EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e1f 3IS EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e20 3IT EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e21 3IU EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e22 3IV EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e23 3IW EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e24 3IX EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e25 3IY EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e26 3IZ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e27 3JA EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e28 3JB EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e29 3JC EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e2a 3JD EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e2b 3JE EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e2c 3JF EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e2d 3JG EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e2e 3JH EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e2f 3JI EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e30 3JJ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e31 3JK EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e32 3JL EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e33 3JM EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e34 3JN EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e35 3JO EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e36 3JP EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e37 3JQ EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e38 3JR EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e39 3JS EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e3a 3JT EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e3b 3JU EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e3c 3JV EMPTY NORMAL 3 Bo BGII UNSPLIT EXT. paletted +0x5e3d 3JW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e3e 3JX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e3f 3JY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e40 3JZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e41 3KA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e42 3KB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e43 3KC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e44 3KD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e45 3KE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e46 3KF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e47 3KG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e48 3KH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e49 3KI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e4a 3KJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e4b 3KK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e4c 3KL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e4d 3KM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e4e 3KN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e4f 3KO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e50 3KP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e51 3KQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e52 3KR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e53 3KS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e54 3KT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e55 3KU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e56 3KV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e57 3KW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e58 3KX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e59 3KY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e5a 3KZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e5b 3LA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e5c 3LB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e5d 3LC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e5e 3LD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e5f 3LE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e60 3LF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e61 3LG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e62 3LH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e63 3LI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e64 3LJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e65 3LK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e66 3LL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e67 3LM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e68 3LN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e69 3LO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e6a 3LP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e6b 3LQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e6c 3LR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e6d 3LS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e6e 3LT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e6f 3LU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e70 3LV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e71 3LW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e72 3LX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e73 3LY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e74 3LZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e75 3MA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e76 3MB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e77 3MC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e78 3MD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e79 3ME EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e7a 3MF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e7b 3MG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e7c 3MH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e7d 3MI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e7e 3MJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e7f 3MK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e80 3ML EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e81 3MM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e82 3MN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e83 3MO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e84 3MP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e85 3MQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e86 3MR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e87 3MS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e88 3MT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e89 3MU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e8a 3MV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e8b 3MW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e8c 3MX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e8d 3MY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e8e 3MZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e8f 3NA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e90 3NB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e91 3NC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e92 3ND EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e93 3NE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e94 3NF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e95 3NG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e96 3NH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e97 3NI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e98 3NJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e99 3NK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e9a 3NL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e9b 3NM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e9c 3NN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e9d 3NO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e9e 3NP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5e9f 3NQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ea0 3NR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ea1 3NS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ea2 3NT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ea3 3NU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ea4 3NV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ea5 3NW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ea6 3NX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ea7 3NY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ea8 3NZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ea9 3OA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eaa 3OB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eab 3OC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eac 3OD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ead 3OE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eae 3OF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eaf 3OG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eb0 3OH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eb1 3OI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eb2 3OJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eb3 3OK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eb4 3OL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eb5 3OM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eb6 3ON EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eb7 3OO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eb8 3OP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eb9 3OQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eba 3OR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ebb 3OS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ebc 3OT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ebd 3OU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ebe 3OV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ebf 3OW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ec0 3OX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ec1 3OY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ec2 3OZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ec3 3PA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ec4 3PB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ec5 3PC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ec6 3PD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ec7 3PE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ec8 3PF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ec9 3PG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eca 3PH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ecb 3PI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ecc 3PJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ecd 3PK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ece 3PL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ecf 3PM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ed0 3PN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ed1 3PO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ed2 3PP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ed3 3PQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ed4 3PR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ed5 3PS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ed6 3PT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ed7 3PU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ed8 3PV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ed9 3PW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eda 3PX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5edb 3PY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5edc 3PZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5edd 3QA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ede 3QB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5edf 3QC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ee0 3QD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ee1 3QE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ee2 3QF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ee3 3QG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ee4 3QH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ee5 3QI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ee6 3QJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ee7 3QK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ee8 3QL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ee9 3QM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eea 3QN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eeb 3QO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eec 3QP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eed 3QQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eee 3QR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eef 3QS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ef0 3QT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ef1 3QU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ef2 3QV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ef3 3QW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ef4 3QX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ef5 3QY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ef6 3QZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ef7 3RA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ef8 3RB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5ef9 3RC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5efa 3RD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5efb 3RE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5efc 3RF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5efd 3RG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5efe 3RH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5eff 3RI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f00 3RJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f01 3RK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f02 3RL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f03 3RM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f04 3RN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f05 3RO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f06 3RP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f07 3RQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f08 3RR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f09 3RS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f0a 3RT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f0b 3RU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f0c 3RV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f0d 3RW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f0e 3RX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f0f 3RY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f10 3RZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f11 3SA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f12 3SB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f13 3SC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f14 3SD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f15 3SE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f16 3SF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f17 3SG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f18 3SH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f19 3SI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f1a 3SJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f1b 3SK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f1c 3SL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f1d 3SM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f1e 3SN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f1f 3SO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f20 3SP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f21 3SQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f22 3SR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f23 3SS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f24 3ST EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f25 3SU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f26 3SV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f27 3SW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f28 3SX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f29 3SY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f2a 3SZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f2b 3TA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f2c 3TB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f2d 3TC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f2e 3TD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f2f 3TE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f30 3TF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f31 3TG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f32 3TH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f33 3TI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f34 3TJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f35 3TK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f36 3TL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f37 3TM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f38 3TN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f39 3TO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f3a 3TP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f3b 3TQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f3c 3TR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f3d 3TS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f3e 3TT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f3f 3TU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f40 3TV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f41 3TW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f42 3TX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f43 3TY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f44 3TZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f45 3UA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f46 3UB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f47 3UC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f48 3UD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f49 3UE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f4a 3UF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f4b 3UG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f4c 3UH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f4d 3UI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f4e 3UJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f4f 3UK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f50 3UL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f51 3UM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f52 3UN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f53 3UO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f54 3UP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f55 3UQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f56 3UR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f57 3US EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f58 3UT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f59 3UU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f5a 3UV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f5b 3UW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f5c 3UX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f5d 3UY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f5e 3UZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f5f 3VA EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f60 3VB EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f61 3VC EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f62 3VD EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f63 3VE EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f64 3VF EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f65 3VG EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f66 3VH EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f67 3VI EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f68 3VJ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f69 3VK EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f6a 3VL EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f6b 3VM EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f6c 3VN EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f6d 3VO EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f6e 3VP EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f6f 3VQ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f70 3VR EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f71 3VS EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f72 3VT EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f73 3VU EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f74 3VV EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f75 3VW EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f76 3VX EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f77 3VY EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f78 3VZ EMPTY LARGE 5 Bo BGII UNSPLIT EXT. paletted +0x5f7a 8AA EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f7b 8AB EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f7c 8AC EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f7d 8AD EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f7e 8AE EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f7f 8AF EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f80 8AG EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f81 8AH EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f82 8AI EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f83 8AJ EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f84 8AK EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f85 8AL EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f86 8AM EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f87 8AN EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f88 8AO EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f89 8AP EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f8a 8AQ EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f8b 8AR EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f8c 8AS EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f8d 8AT EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f8e 8AU EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f8f 8AV EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f90 8AW EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f91 8AX EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f92 8AY EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f93 8AZ EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f94 8BA EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f95 8BB EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f96 8BC EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f97 8BD EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f98 8BE EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f99 8BF EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f9a 8BG EMPTY NORMAL 3 Bo BGII SPLIT paletted +0x5f9b 8BH EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5f9c 8BI EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5f9d 8BJ EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5f9e 8BK EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5f9f 8BL EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fa0 8BM EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fa1 8BN EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fa2 8BO EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fa3 8BP EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fa4 8BQ EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fa5 8BR EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fa6 8BS EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fa7 8BT EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fa8 8BU EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fa9 8BV EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5faa 8BW EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fab 8BX EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fac 8BY EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fad 8BZ EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fae 8CA EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5faf 8CB EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fb0 8CC EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fb1 8CD EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fb2 8CE EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fb3 8CF EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fb4 8CG EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fb5 8CH EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fb6 8CI EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fb7 8CJ EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fb8 8CK EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fb9 8CL EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fba 8CM EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fbb 8CN EMPTY LARGE 5 Bo BGII SPLIT paletted +0x5fbc 8CO EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fbd 8CP EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fbe 8CQ EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fbf 8CR EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fc0 8CS EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fc1 8CT EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fc2 8CU EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fc3 8CV EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fc4 8CW EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fc5 8CX EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fc6 8CY EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fc7 8CZ EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fc8 8DA EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fc9 8DB EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fca 8DC EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fcb 8DD EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fcc 8DE EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fcd 8DF EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fce 8DG EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fcf 8DH EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fd0 8DI EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fd1 8DJ EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fd2 8DK EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fd3 8DL EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fd4 8DM EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fd5 8DN EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fd6 8DO EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fd7 8DP EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fd8 8DQ EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fd9 8DR EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fda 8DS EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fdb 8DT EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fdc 8DU EMPTY NORMAL 3 Bo BGII SPLIT unpaletted +0x5fdd 8DV EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5fde 8DW EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5fdf 8DX EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5fe0 8DY EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5fe1 8DZ EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5fe2 8EA EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5fe3 8EB EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5fe4 8EC EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5fe5 8ED EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5fe6 8EE EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5fe7 8EF EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5fe8 8EG EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5fe9 8EH EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5fea 8EI EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5feb 8EJ EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5fec 8EK EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5fed 8EL EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5fee 8EM EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5fef 8EN EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5ff0 8EO EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5ff1 8EP EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5ff2 8EQ EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5ff3 8ER EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5ff4 8ES EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5ff5 8ET EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5ff6 8EU EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5ff7 8EV EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5ff8 8EW EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5ff9 8EX EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5ffa 8EY EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5ffb 8EZ EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5ffc 8FA EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5ffd 8FB EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5ffe 8FC EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x5fff 8FD EMPTY LARGE 5 Bo BGII SPLIT unpaletted +0x6600 0A3A EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6601 0A3B EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6602 0A3C EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6603 0A3D EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6604 0A3E EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6605 0A3F EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6606 0A3G EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6607 0A3H EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6608 0A3I EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6609 0A3J EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x660a 0A3K EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x660b 0A3L EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x660c 0A3M EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x660d 0A3N EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x660e 0A3O EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x660f 0A3P EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6610 0A3Q EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6611 0A3R EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6612 0A3S EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6613 0A3T EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6614 0A3U EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6615 0A3V EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6616 0A3W EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6617 0A3X EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6618 0A3Y EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6619 0A3Z EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x661a 0B3A EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x661b 0B3B EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x661c 0B3C EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x661d 0B3D EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x661e 0B3E EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x661f 0B3F EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6620 0B3G EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6621 0B3H EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6622 0B3I EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6623 0B3J EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6624 0B3K EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6625 0B3L EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6626 0B3M EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6627 0B3N EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6628 0B3O EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6629 0B3P EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x662a 0B3Q EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x662b 0B3R EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x662c 0B3S EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x662d 0B3T EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x662e 0B3U EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x662f 0B3V EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6630 0B3W EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6631 0B3X EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6632 0B3Y EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6633 0B3Z EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6634 0C3A EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6635 0C3B EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6636 0C3C EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6637 0C3D EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6638 0C3E EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6639 0C3F EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x663a 0C3G EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x663b 0C3H EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x663c 0C3I EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x663d 0C3J EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x663e 0C3K EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x663f 0C3L EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6640 0C3M EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6641 0C3N EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6642 0C3O EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6643 0C3P EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6644 0C3Q EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6645 0C3R EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6646 0C3S EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6647 0C3T EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6648 0C3U EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6649 0C3V EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x664a 0C3W EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x664b 0C3X EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x664c 0C3Y EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x664d 0C3Z EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x664e 0D3A EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x664f 0D3B EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6650 0D3C EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6651 0D3D EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6652 0D3E EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6653 0D3F EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6654 0D3G EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6655 0D3H EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6656 0D3I EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6657 0D3J EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6658 0D3K EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6659 0D3L EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x665a 0D3M EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x665b 0D3N EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x665c 0D3O EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x665d 0D3P EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x665e 0D3Q EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x665f 0D3R EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6660 0D3S EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6661 0D3T EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6662 0D3U EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6663 0D3V EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6664 0D3W EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6665 0D3X EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6666 0D3Y EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6667 0D3Z EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6668 0E3A EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6669 0E3B EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x666a 0E3C EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x666b 0E3D EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x666c 0E3E EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x666d 0E3F EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x666e 0E3G EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x666f 0E3H EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6670 0E3I EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6671 0E3J EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6672 0E3K EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6673 0E3L EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6674 0E3M EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6675 0E3N EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6676 0E3O EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6677 0E3P EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6678 0E3Q EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6679 0E3R EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x667a 0E3S EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x667b 0E3T EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x667c 0E3U EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x667d 0E3V EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x667e 0E3W EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x667f 0E3X EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6680 0E3Y EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6681 0E3Z EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6682 0F3A EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6683 0F3B EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6684 0F3C EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6685 0F3D EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6686 0F3E EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6687 0F3F EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6688 0F3G EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6689 0F3H EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x668a 0F3I EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x668b 0F3J EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x668c 0F3K EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x668d 0F3L EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x668e 0F3M EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x668f 0F3N EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6690 0F3O EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6691 0F3P EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6692 0F3Q EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6693 0F3R EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6694 0F3S EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6695 0F3T EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6696 0F3U EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6697 0F3V EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6698 0F3W EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6699 0F3X EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x669a 0F3Y EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x669b 0F3Z EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x669c 0G3A EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x669d 0G3B EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x669e 0G3C EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x669f 0G3D EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66a0 0G3E EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66a1 0G3F EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66a2 0G3G EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66a3 0G3H EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66a4 0G3I EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66a5 0G3J EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66a6 0G3K EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66a7 0G3L EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66a8 0G3M EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66a9 0G3N EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66aa 0G3O EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66ab 0G3P EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66ac 0G3Q EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66ad 0G3R EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66ae 0G3S EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66af 0G3T EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66b0 0G3U EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66b1 0G3V EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66b2 0G3W EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66b3 0G3X EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66b4 0G3Y EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66b5 0G3Z EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66b6 0H3A EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66b7 0H3B EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66b8 0H3C EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66b9 0H3D EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66ba 0H3E EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66bb 0H3F EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66bc 0H3G EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66bd 0H3H EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66be 0H3I EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66bf 0H3J EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66c0 0H3K EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66c1 0H3L EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66c2 0H3M EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66c3 0H3N EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66c4 0H3O EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66c5 0H3P EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66c6 0H3Q EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66c7 0H3R EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66c8 0H3S EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66c9 0H3T EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66ca 0H3U EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66cb 0H3V EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66cc 0H3W EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66cd 0H3X EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66ce 0H3Y EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66cf 0H3Z EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66d0 0I3A EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66d1 0I3B EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66d2 0I3C EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66d3 0I3D EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66d4 0I3E EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66d5 0I3F EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66d6 0I3G EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66d7 0I3H EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66d8 0I3I EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66d9 0I3J EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66da 0I3K EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66db 0I3L EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66dc 0I3M EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66dd 0I3N EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66de 0I3O EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66df 0I3P EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66e0 0I3Q EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66e1 0I3R EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66e2 0I3S EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66e3 0I3T EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66e4 0I3U EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66e5 0I3V EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66e6 0I3W EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66e7 0I3X EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66e8 0I3Y EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66e9 0I3Z EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66ea 0J3A EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66eb 0J3B EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66ec 0J3C EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66ed 0J3D EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66ee 0J3E EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66ef 0J3F EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66f0 0J3G EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66f1 0J3H EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66f2 0J3I EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66f3 0J3J EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66f4 0J3K EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66f5 0J3L EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66f6 0J3M EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66f7 0J3N EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66f8 0J3O EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66f9 0J3P EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66fa 0J3Q EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66fb 0J3R EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66fc 0J3S EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66fd 0J3T EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66fe 0J3U EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x66ff 0J3V EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6700 0J3W EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6701 0J3X EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6702 0J3Y EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6703 0J3Z EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6704 0K3A EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6705 0K3B EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6706 0K3C EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6707 0K3D EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6708 0K3E EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6709 0K3F EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x670a 0K3G EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x670b 0K3H EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x670c 0K3I EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x670d 0K3J EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x670e 0K3K EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x670f 0K3L EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6710 0K3M EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6711 0K3N EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6712 0K3O EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6713 0K3P EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6714 0K3Q EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6715 0K3R EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6716 0K3S EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6717 0K3T EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6718 0K3U EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6719 0K3V EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x671a 0K3W EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x671b 0K3X EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x671c 0K3Y EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x671d 0K3Z EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x671e 0L3A EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x671f 0L3B EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6720 0L3C EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6721 0L3D EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6722 0L3E EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6723 0L3F EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6724 0L3G EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6725 0L3H EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6726 0L3I EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6727 0L3J EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6728 0L3K EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6729 0L3L EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x672a 0L3M EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x672b 0L3N EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x672c 0L3O EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x672d 0L3P EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x672e 0L3Q EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x672f 0L3R EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6730 0L3S EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6731 0L3T EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6732 0L3U EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6733 0L3V EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6734 0L3W EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6735 0L3X EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6736 0L3Y EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6737 0L3Z EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6738 0M3A EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6739 0M3B EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x673a 0M3C EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x673b 0M3D EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x673c 0M3E EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x673d 0M3F EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x673e 0M3G EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x673f 0M3H EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6740 0M3I EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6741 0M3J EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6742 0M3K EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6743 0M3L EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6744 0M3M EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6745 0M3N EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6746 0M3O EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6747 0M3P EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6748 0M3Q EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6749 0M3R EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x674a 0M3S EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x674b 0M3T EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x674c 0M3U EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x674d 0M3V EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x674e 0M3W EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x674f 0M3X EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6750 0M3Y EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6751 0M3Z EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6752 0N3A EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6753 0N3B EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6754 0N3C EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6755 0N3D EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6756 0N3E EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6757 0N3F EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6758 0N3G EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6759 0N3H EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x675a 0N3I EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x675b 0N3J EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x675c 0N3K EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x675d 0N3L EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x675e 0N3M EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x675f 0N3N EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6760 0N3O EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6761 0N3P EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6762 0N3Q EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6763 0N3R EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6764 0N3S EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6765 0N3T EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6766 0N3U EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6767 0N3V EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6768 0N3W EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6769 0N3X EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x676a 0N3Y EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x676b 0N3Z EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x676c 0O3A EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x676d 0O3B EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x676e 0O3C EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x676f 0O3D EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6770 0O3E EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6771 0O3F EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6772 0O3G EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6773 0O3H EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6774 0O3I EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6775 0O3J EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6776 0O3K EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6777 0O3L EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6778 0O3M EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6779 0O3N EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x677a 0O3O EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x677b 0O3P EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x677c 0O3Q EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x677d 0O3R EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x677e 0O3S EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x677f 0O3T EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6780 0O3U EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6781 0O3V EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6782 0O3W EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6783 0O3X EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6784 0O3Y EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6785 0O3Z EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6786 0P3A EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6787 0P3B EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6788 0P3C EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6789 0P3D EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x678a 0P3E EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x678b 0P3F EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x678c 0P3G EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x678d 0P3H EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x678e 0P3I EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x678f 0P3J EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6790 0P3K EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6791 0P3L EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6792 0P3M EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6793 0P3N EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6794 0P3O EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6795 0P3P EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6796 0P3Q EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6797 0P3R EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6798 0P3S EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6799 0P3T EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x679a 0P3U EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x679b 0P3V EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x679c 0P3W EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x679d 0P3X EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x679e 0P3Y EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x679f 0P3Z EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67a0 0Q3A EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67a1 0Q3B EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67a2 0Q3C EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67a3 0Q3D EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67a4 0Q3E EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67a5 0Q3F EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67a6 0Q3G EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67a7 0Q3H EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67a8 0Q3I EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67a9 0Q3J EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67aa 0Q3K EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67ab 0Q3L EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67ac 0Q3M EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67ad 0Q3N EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67ae 0Q3O EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67af 0Q3P EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67b0 0Q3Q EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67b1 0Q3R EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67b2 0Q3S EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67b3 0Q3T EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67b4 0Q3U EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67b5 0Q3V EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67b6 0Q3W EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67b7 0Q3X EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67b8 0Q3Y EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67b9 0Q3Z EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67ba 0R3A EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67bb 0R3B EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67bc 0R3C EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67bd 0R3D EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67be 0R3E EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67bf 0R3F EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67c0 0R3G EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67c1 0R3H EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67c2 0R3I EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67c3 0R3J EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67c4 0R3K EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67c5 0R3L EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67c6 0R3M EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67c7 0R3N EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67c8 0R3O EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67c9 0R3P EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67ca 0R3Q EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67cb 0R3R EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67cc 0R3S EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67cd 0R3T EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67ce 0R3U EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67cf 0R3V EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67d0 0R3W EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67d1 0R3X EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67d2 0R3Y EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67d3 0R3Z EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67d4 0S3A EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67d5 0S3B EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67d6 0S3C EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67d7 0S3D EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67d8 0S3E EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67d9 0S3F EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67da 0S3G EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67db 0S3H EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67dc 0S3I EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67dd 0S3J EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67de 0S3K EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67df 0S3L EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67e0 0S3M EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67e1 0S3N EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67e2 0S3O EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67e3 0S3P EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67e4 0S3Q EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67e5 0S3R EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67e6 0S3S EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67e7 0S3T EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67e8 0S3U EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67e9 0S3V EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67ea 0S3W EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67eb 0S3X EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67ec 0S3Y EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67ed 0S3Z EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67ee 0T3A EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67ef 0T3B EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67f0 0T3C EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67f1 0T3D EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67f2 0T3E EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67f3 0T3F EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67f4 0T3G EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67f5 0T3H EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67f6 0T3I EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67f7 0T3J EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67f8 0T3K EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67f9 0T3L EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67fa 0T3M EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67fb 0T3N EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67fc 0T3O EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67fd 0T3P EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67fe 0T3Q EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x67ff 0T3R EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6800 0T3S EMPTY NORMAL 3 Co CHARACTER BGII paletted +0x6801 0T3T EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6802 0T3U EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6803 0T3V EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6804 0T3W EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6805 0T3X EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6806 0T3Y EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6807 0T3Z EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6808 0U3A EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6809 0U3B EMPTY LARGE 5 Co CHARACTER BGII paletted +0x680a 0U3C EMPTY LARGE 5 Co CHARACTER BGII paletted +0x680b 0U3D EMPTY LARGE 5 Co CHARACTER BGII paletted +0x680c 0U3E EMPTY LARGE 5 Co CHARACTER BGII paletted +0x680d 0U3F EMPTY LARGE 5 Co CHARACTER BGII paletted +0x680e 0U3G EMPTY LARGE 5 Co CHARACTER BGII paletted +0x680f 0U3H EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6810 0U3I EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6811 0U3J EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6812 0U3K EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6813 0U3L EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6814 0U3M EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6815 0U3N EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6816 0U3O EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6817 0U3P EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6818 0U3Q EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6819 0U3R EMPTY LARGE 5 Co CHARACTER BGII paletted +0x681a 0U3S EMPTY LARGE 5 Co CHARACTER BGII paletted +0x681b 0U3T EMPTY LARGE 5 Co CHARACTER BGII paletted +0x681c 0U3U EMPTY LARGE 5 Co CHARACTER BGII paletted +0x681d 0U3V EMPTY LARGE 5 Co CHARACTER BGII paletted +0x681e 0U3W EMPTY LARGE 5 Co CHARACTER BGII paletted +0x681f 0U3X EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6820 0U3Y EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6821 0U3Z EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6822 0V3A EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6823 0V3B EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6824 0V3C EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6825 0V3D EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6826 0V3E EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6827 0V3F EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6828 0V3G EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6829 0V3H EMPTY LARGE 5 Co CHARACTER BGII paletted +0x682a 0V3I EMPTY LARGE 5 Co CHARACTER BGII paletted +0x682b 0V3J EMPTY LARGE 5 Co CHARACTER BGII paletted +0x682c 0V3K EMPTY LARGE 5 Co CHARACTER BGII paletted +0x682d 0V3L EMPTY LARGE 5 Co CHARACTER BGII paletted +0x682e 0V3M EMPTY LARGE 5 Co CHARACTER BGII paletted +0x682f 0V3N EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6830 0V3O EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6831 0V3P EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6832 0V3Q EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6833 0V3R EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6834 0V3S EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6835 0V3T EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6836 0V3U EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6837 0V3V EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6838 0V3W EMPTY LARGE 5 Co CHARACTER BGII paletted +0x6839 0V3X EMPTY LARGE 5 Co CHARACTER BGII paletted +0x683a 0V3Y EMPTY LARGE 5 Co CHARACTER BGII paletted +0x683b 0V3Z EMPTY LARGE 5 Co CHARACTER BGII paletted +0x683c 0W3A EMPTY LARGE 5 Co CHARACTER BGII paletted +0x683d 0A0A EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x683e 0A0B EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x683f 0A0C EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x6840 0A0D EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x6841 0A0E EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6842 0A0F EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6843 0A0G EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6844 0A0H EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6845 0A0I EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6846 0A0J EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6847 0A0K EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6848 0A0L EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6849 0A0M EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x684a 0A0N EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x684b 0A0O EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x684c 0A0P EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x684d 0A0Q EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x684e 0A0R EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x684f 0A0S EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x6850 0A0T EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x6851 0A0U EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6852 0A0V EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6853 0A0W EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6854 0A0X EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6855 0A0Y EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6856 0A0Z EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6857 0B0A EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6858 0B0B EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6859 0B0C EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x685a 0B0D EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x685b 0B0E EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x685c 0B0F EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x685d 0B0G EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x685e 0B0H EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x685f 0B0I EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x6860 0B0J EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x6861 0B0K EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6862 0B0L EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6863 0B0M EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6864 0B0N EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6865 0B0O EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6866 0B0P EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6867 0B0Q EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6868 0B0R EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6869 0B0S EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x686a 0B0T EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x686b 0B0U EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x686c 0B0V EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x686d 0B0W EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x686e 0B0X EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x686f 0B0Y EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x6870 0B0Z EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x6871 0C0A EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6872 0C0B EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6873 0C0C EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6874 0C0D EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6875 0C0E EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6876 0C0F EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6877 0C0G EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6878 0C0H EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6879 0C0I EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x687a 0C0J EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x687b 0C0K EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x687c 0C0L EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x687d 0C0M EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x687e 0C0N EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x687f 0C0O EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x6880 0C0P EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x6881 0C0Q EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6882 0C0R EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6883 0C0S EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6884 0C0T EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6885 0C0U EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6886 0C0V EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6887 0C0W EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6888 0C0X EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6889 0C0Y EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x688a 0C0Z EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x688b 0D0A EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x688c 0D0B EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x688d 0D0C EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x688e 0D0D EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x688f 0D0E EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x6890 0D0F EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x6891 0D0G EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6892 0D0H EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6893 0D0I EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6894 0D0J EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x6895 0D0K EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6896 0D0L EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6897 0D0M EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6898 0D0N EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x6899 0D0O EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x689a 0D0P EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x689b 0D0Q EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x689c 0D0R EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x689d 0D0S EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x689e 0D0T EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x689f 0D0U EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x68a0 0D0V EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x68a1 0D0W EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x68a2 0D0X EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x68a3 0D0Y EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x68a4 0D0Z EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x68a5 0E0A EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x68a6 0E0B EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x68a7 0E0C EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x68a8 0E0D EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x68a9 0E0E EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x68aa 0E0F EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x68ab 0E0G EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x68ac 0E0H EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x68ad 0E0I EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x68ae 0E0J EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x68af 0E0K EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x68b0 0E0L EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x68b1 0E0M EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x68b2 0E0N EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x68b3 0E0O EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x68b4 0E0P EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x68b5 0E0Q EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x68b6 0E0R EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x68b7 0E0S EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x68b8 0E0T EMPTY NORMAL 3 Co CHARACTER BGII WQN paletted +0x68b9 0E0U EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x68ba 0E0V EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x68bb 0E0W EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x68bc 0E0X EMPTY NORMAL 3 Co CHARACTER BGII WQS paletted +0x68bd 0E0Y EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x68be 0E0Z EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x68bf 0F0A EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x68c0 0F0B EMPTY NORMAL 3 Co CHARACTER BGII WQL paletted +0x68c1 0F0C EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x68c2 0F0D EMPTY NORMAL 3 Co CHARACTER BGII WQM paletted +0x68c3 0A3A EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68c4 0A3B EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68c5 0A3C EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68c6 0A3D EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68c7 0A3E EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68c8 0A3F EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68c9 0A3G EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68ca 0A3H EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68cb 0A3I EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68cc 0A3J EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68cd 0A3K EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68ce 0A3L EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68cf 0A3M EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68d0 0A3N EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68d1 0A3O EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68d2 0A3P EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68d3 0A3Q EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68d4 0A3R EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68d5 0A3S EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68d6 0A3T EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68d7 0A3U EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68d8 0A3V EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68d9 0A3W EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68da 0A3X EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68db 0A3Y EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68dc 0A3Z EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68dd 0B3A EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68de 0B3B EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68df 0B3C EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68e0 0B3D EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68e1 0B3E EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68e2 0B3F EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68e3 0B3G EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68e4 0B3H EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68e5 0B3I EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68e6 0B3J EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68e7 0B3K EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68e8 0B3L EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68e9 0B3M EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68ea 0B3N EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68eb 0B3O EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68ec 0B3P EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68ed 0B3Q EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68ee 0B3R EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68ef 0B3S EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68f0 0B3T EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68f1 0B3U EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68f2 0B3V EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68f3 0B3W EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68f4 0B3X EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68f5 0B3Y EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68f6 0B3Z EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68f7 0C3A EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68f8 0C3B EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68f9 0C3C EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68fa 0C3D EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68fb 0C3E EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68fc 0C3F EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68fd 0C3G EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68fe 0C3H EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x68ff 0C3I EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6900 0C3J EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6901 0C3K EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6902 0C3L EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6903 0C3M EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6904 0C3N EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6905 0C3O EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6906 0C3P EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6907 0C3Q EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6908 0C3R EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6909 0C3S EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x690a 0C3T EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x690b 0C3U EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x690c 0C3V EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x690d 0C3W EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x690e 0C3X EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x690f 0C3Y EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6910 0C3Z EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6911 0D3A EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6912 0D3B EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6913 0D3C EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6914 0D3D EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6915 0D3E EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6916 0D3F EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6917 0D3G EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6918 0D3H EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6919 0D3I EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x691a 0D3J EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x691b 0D3K EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x691c 0D3L EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x691d 0D3M EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x691e 0D3N EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x691f 0D3O EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6920 0D3P EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6921 0D3Q EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6922 0D3R EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6923 0D3S EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6924 0D3T EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6925 0D3U EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6926 0D3V EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6927 0D3W EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6928 0D3X EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6929 0D3Y EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x692a 0D3Z EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x692b 0E3A EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x692c 0E3B EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x692d 0E3C EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x692e 0E3D EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x692f 0E3E EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6930 0E3F EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6931 0E3G EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6932 0E3H EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6933 0E3I EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6934 0E3J EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6935 0E3K EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6936 0E3L EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6937 0E3M EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6938 0E3N EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6939 0E3O EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x693a 0E3P EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x693b 0E3Q EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x693c 0E3R EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x693d 0E3S EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x693e 0E3T EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x693f 0E3U EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6940 0E3V EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6941 0E3W EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6942 0E3X EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6943 0E3Y EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6944 0E3Z EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6945 0F3A EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6946 0F3B EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6947 0F3C EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6948 0F3D EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6949 0F3E EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x694a 0F3F EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x694b 0F3G EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x694c 0F3H EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x694d 0F3I EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x694e 0F3J EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x694f 0F3K EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6950 0F3L EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6951 0F3M EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6952 0F3N EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6953 0F3O EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6954 0F3P EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6955 0F3Q EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6956 0F3R EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6957 0F3S EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6958 0F3T EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6959 0F3U EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x695a 0F3V EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x695b 0F3W EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x695c 0F3X EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x695d 0F3Y EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x695e 0F3Z EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x695f 0G3A EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6960 0G3B EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6961 0G3C EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6962 0G3D EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6963 0G3E EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6964 0G3F EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6965 0G3G EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6966 0G3H EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6967 0G3I EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6968 0G3J EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6969 0G3K EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x696a 0G3L EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x696b 0G3M EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x696c 0G3N EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x696d 0G3O EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x696e 0G3P EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x696f 0G3Q EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6970 0G3R EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6971 0G3S EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6972 0G3T EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6973 0G3U EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6974 0G3V EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6975 0G3W EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6976 0G3X EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6977 0G3Y EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6978 0G3Z EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6979 0H3A EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x697a 0H3B EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x697b 0H3C EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x697c 0H3D EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x697d 0H3E EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x697e 0H3F EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x697f 0H3G EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6980 0H3H EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6981 0H3I EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6982 0H3J EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6983 0H3K EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6984 0H3L EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6985 0H3M EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6986 0H3N EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6987 0H3O EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6988 0H3P EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6989 0H3Q EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x698a 0H3R EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x698b 0H3S EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x698c 0H3T EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x698d 0H3U EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x698e 0H3V EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x698f 0H3W EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6990 0H3X EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6991 0H3Y EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6992 0H3Z EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6993 0I3A EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6994 0I3B EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6995 0I3C EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6996 0I3D EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6997 0I3E EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6998 0I3F EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6999 0I3G EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x699a 0I3H EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x699b 0I3I EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x699c 0I3J EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x699d 0I3K EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x699e 0I3L EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x699f 0I3M EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69a0 0I3N EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69a1 0I3O EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69a2 0I3P EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69a3 0I3Q EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69a4 0I3R EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69a5 0I3S EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69a6 0I3T EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69a7 0I3U EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69a8 0I3V EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69a9 0I3W EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69aa 0I3X EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69ab 0I3Y EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69ac 0I3Z EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69ad 0J3A EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69ae 0J3B EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69af 0J3C EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69b0 0J3D EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69b1 0J3E EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69b2 0J3F EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69b3 0J3G EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69b4 0J3H EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69b5 0J3I EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69b6 0J3J EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69b7 0J3K EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69b8 0J3L EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69b9 0J3M EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69ba 0J3N EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69bb 0J3O EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69bc 0J3P EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69bd 0J3Q EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69be 0J3R EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69bf 0J3S EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69c0 0J3T EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69c1 0J3U EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69c2 0J3V EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69c3 0J3W EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69c4 0J3X EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69c5 0J3Y EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69c6 0J3Z EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69c7 0K3A EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69c8 0K3B EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69c9 0K3C EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69ca 0K3D EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69cb 0K3E EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69cc 0K3F EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69cd 0K3G EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69ce 0K3H EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69cf 0K3I EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69d0 0K3J EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69d1 0K3K EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69d2 0K3L EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69d3 0K3M EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69d4 0K3N EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69d5 0K3O EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69d6 0K3P EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69d7 0K3Q EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69d8 0K3R EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69d9 0K3S EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69da 0K3T EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69db 0K3U EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69dc 0K3V EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69dd 0K3W EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69de 0K3X EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69df 0K3Y EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69e0 0K3Z EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69e1 0L3A EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69e2 0L3B EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69e3 0L3C EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69e4 0L3D EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69e5 0L3E EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69e6 0L3F EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69e7 0L3G EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69e8 0L3H EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69e9 0L3I EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69ea 0L3J EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69eb 0L3K EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69ec 0L3L EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69ed 0L3M EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69ee 0L3N EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69ef 0L3O EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69f0 0L3P EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69f1 0L3Q EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69f2 0L3R EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69f3 0L3S EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69f4 0L3T EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69f5 0L3U EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69f6 0L3V EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69f7 0L3W EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69f8 0L3X EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69f9 0L3Y EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69fa 0L3Z EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69fb 0M3A EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69fc 0M3B EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69fd 0M3C EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69fe 0M3D EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x69ff 0M3E EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a00 0M3F EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a01 0M3G EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a02 0M3H EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a03 0M3I EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a04 0M3J EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a05 0M3K EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a06 0M3L EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a07 0M3M EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a08 0M3N EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a09 0M3O EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a0a 0M3P EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a0b 0M3Q EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a0c 0M3R EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a0d 0M3S EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a0e 0M3T EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a0f 0M3U EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a10 0M3V EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a11 0M3W EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a12 0M3X EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a13 0M3Y EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a14 0M3Z EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a15 0N3A EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a16 0N3B EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a17 0N3C EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a18 0N3D EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a19 0N3E EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a1a 0N3F EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a1b 0N3G EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a1c 0N3H EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a1d 0N3I EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a1e 0N3J EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a1f 0N3K EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a20 0N3L EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a21 0N3M EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a22 0N3N EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a23 0N3O EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a24 0N3P EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a25 0N3Q EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a26 0N3R EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a27 0N3S EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a28 0N3T EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a29 0N3U EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a2a 0N3V EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a2b 0N3W EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a2c 0N3X EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a2d 0N3Y EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a2e 0N3Z EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a2f 0O3A EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a30 0O3B EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a31 0O3C EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a32 0O3D EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a33 0O3E EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a34 0O3F EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a35 0O3G EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a36 0O3H EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a37 0O3I EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a38 0O3J EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a39 0O3K EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a3a 0O3L EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a3b 0O3M EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a3c 0O3N EMPTY NORMAL 3 Co CHARACTER BGII unpaletted +0x6a3d 0O3O EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a3e 0O3P EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a3f 0O3Q EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a40 0O3R EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a41 0O3S EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a42 0O3T EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a43 0O3U EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a44 0O3V EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a45 0O3W EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a46 0O3X EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a47 0O3Y EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a48 0O3Z EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a49 0P3A EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a4a 0P3B EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a4b 0P3C EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a4c 0P3D EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a4d 0P3E EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a4e 0P3F EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a4f 0P3G EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a50 0P3H EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a51 0P3I EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a52 0P3J EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a53 0P3K EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a54 0P3L EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a55 0P3M EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a56 0P3N EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a57 0P3O EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a58 0P3P EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a59 0P3Q EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a5a 0P3R EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a5b 0P3S EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a5c 0P3T EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a5d 0P3U EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a5e 0P3V EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a5f 0P3W EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a60 0P3X EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a61 0P3Y EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a62 0P3Z EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a63 0Q3A EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a64 0Q3B EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a65 0Q3C EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a66 0Q3D EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a67 0Q3E EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a68 0Q3F EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a69 0Q3G EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a6a 0Q3H EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a6b 0Q3I EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a6c 0Q3J EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a6d 0Q3K EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a6e 0Q3L EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a6f 0Q3M EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a70 0Q3N EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a71 0Q3O EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a72 0Q3P EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a73 0Q3Q EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a74 0Q3R EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a75 0Q3S EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a76 0Q3T EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a77 0Q3U EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a78 0Q3V EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a79 0Q3W EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a7a 0Q3X EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a7b 0Q3Y EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a7c 0Q3Z EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a7d 0R3A EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a7e 0R3B EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a7f 0R3C EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a80 0R3D EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a81 0R3E EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a82 0R3F EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a83 0R3G EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a84 0R3H EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a85 0R3I EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a86 0R3J EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a87 0R3K EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a88 0R3L EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a89 0R3M EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a8a 0R3N EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a8b 0R3O EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a8c 0R3P EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a8d 0R3Q EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a8e 0R3R EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a8f 0R3S EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a90 0R3T EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a91 0R3U EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a92 0R3V EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a93 0R3W EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a94 0R3X EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a95 0R3Y EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a96 0R3Z EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a97 0S3A EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a98 0S3B EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a99 0S3C EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a9a 0S3D EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a9b 0S3E EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a9c 0S3F EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a9d 0S3G EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a9e 0S3H EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6a9f 0S3I EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aa0 0S3J EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aa1 0S3K EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aa2 0S3L EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aa3 0S3M EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aa4 0S3N EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aa5 0S3O EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aa6 0S3P EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aa7 0S3Q EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aa8 0S3R EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aa9 0S3S EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aaa 0S3T EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aab 0S3U EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aac 0S3V EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aad 0S3W EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aae 0S3X EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aaf 0S3Y EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ab0 0S3Z EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ab1 0T3A EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ab2 0T3B EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ab3 0T3C EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ab4 0T3D EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ab5 0T3E EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ab6 0T3F EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ab7 0T3G EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ab8 0T3H EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ab9 0T3I EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aba 0T3J EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6abb 0T3K EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6abc 0T3L EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6abd 0T3M EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6abe 0T3N EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6abf 0T3O EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ac0 0T3P EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ac1 0T3Q EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ac2 0T3R EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ac3 0T3S EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ac4 0T3T EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ac5 0T3U EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ac6 0T3V EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ac7 0T3W EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ac8 0T3X EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ac9 0T3Y EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aca 0T3Z EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6acb 0U3A EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6acc 0U3B EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6acd 0U3C EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ace 0U3D EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6acf 0U3E EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ad0 0U3F EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ad1 0U3G EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ad2 0U3H EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ad3 0U3I EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ad4 0U3J EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ad5 0U3K EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ad6 0U3L EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ad7 0U3M EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ad8 0U3N EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ad9 0U3O EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ada 0U3P EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6adb 0U3Q EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6adc 0U3R EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6add 0U3S EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ade 0U3T EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6adf 0U3U EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ae0 0U3V EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ae1 0U3W EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ae2 0U3X EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ae3 0U3Y EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ae4 0U3Z EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ae5 0V3A EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ae6 0V3B EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ae7 0V3C EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ae8 0V3D EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6ae9 0V3E EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aea 0V3F EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aeb 0V3G EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aec 0V3H EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aed 0V3I EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aee 0V3J EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aef 0V3K EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6af0 0V3L EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6af1 0V3M EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6af2 0V3N EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6af3 0V3O EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6af4 0V3P EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6af5 0V3Q EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6af6 0V3R EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6af7 0V3S EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6af8 0V3T EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6af9 0V3U EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6afa 0V3V EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6afb 0V3W EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6afc 0V3X EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6afd 0V3Y EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6afe 0V3Z EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6aff 0W3A EMPTY LARGE 5 Co CHARACTER BGII unpaletted +0x6b00 0A3A EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b01 0A3B EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b02 0A3C EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b03 0A3D EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b04 0A3E EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b05 0A3F EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b06 0A3G EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b07 0A3H EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b08 0A3I EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b09 0A3J EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b0a 0A3K EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b0b 0A3L EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b0c 0A3M EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b0d 0A3N EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b0e 0A3O EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b0f 0A3P EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b10 0A3Q EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b11 0A3R EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b12 0A3S EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b13 0A3T EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b14 0A3U EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b15 0A3V EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b16 0A3W EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b17 0A3X EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b18 0A3Y EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b19 0A3Z EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b1a 0B3A EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b1b 0B3B EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b1c 0B3C EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b1d 0B3D EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b1e 0B3E EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b1f 0B3F EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b20 0B3G EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b21 0B3H EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b22 0B3I EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b23 0B3J EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b24 0B3K EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b25 0B3L EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b26 0B3M EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b27 0B3N EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b28 0B3O EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b29 0B3P EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b2a 0B3Q EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b2b 0B3R EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b2c 0B3S EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b2d 0B3T EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b2e 0B3U EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b2f 0B3V EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b30 0B3W EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b31 0B3X EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b32 0B3Y EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b33 0B3Z EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b34 0C3A EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b35 0C3B EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b36 0C3C EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b37 0C3D EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b38 0C3E EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b39 0C3F EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b3a 0C3G EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b3b 0C3H EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b3c 0C3I EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b3d 0C3J EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b3e 0C3K EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b3f 0C3L EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b40 0C3M EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b41 0C3N EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b42 0C3O EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b43 0C3P EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b44 0C3Q EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b45 0C3R EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b46 0C3S EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b47 0C3T EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b48 0C3U EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b49 0C3V EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b4a 0C3W EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b4b 0C3X EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b4c 0C3Y EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b4d 0C3Z EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b4e 0D3A EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b4f 0D3B EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b50 0D3C EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b51 0D3D EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b52 0D3E EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b53 0D3F EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b54 0D3G EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b55 0D3H EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b56 0D3I EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b57 0D3J EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b58 0D3K EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b59 0D3L EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b5a 0D3M EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b5b 0D3N EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b5c 0D3O EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b5d 0D3P EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b5e 0D3Q EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b5f 0D3R EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b60 0D3S EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b61 0D3T EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b62 0D3U EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b63 0D3V EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b64 0D3W EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b65 0D3X EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b66 0D3Y EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b67 0D3Z EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b68 0E3A EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b69 0E3B EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b6a 0E3C EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b6b 0E3D EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b6c 0E3E EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b6d 0E3F EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b6e 0E3G EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b6f 0E3H EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b70 0E3I EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b71 0E3J EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b72 0E3K EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b73 0E3L EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b74 0E3M EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b75 0E3N EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b76 0E3O EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b77 0E3P EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b78 0E3Q EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b79 0E3R EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b7a 0E3S EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b7b 0E3T EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b7c 0E3U EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b7d 0E3V EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b7e 0E3W EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b7f 0E3X EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b80 0E3Y EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b81 0E3Z EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b82 0F3A EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b83 0F3B EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b84 0F3C EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b85 0F3D EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b86 0F3E EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b87 0F3F EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b88 0F3G EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b89 0F3H EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b8a 0F3I EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b8b 0F3J EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b8c 0F3K EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b8d 0F3L EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b8e 0F3M EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b8f 0F3N EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b90 0F3O EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b91 0F3P EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b92 0F3Q EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b93 0F3R EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b94 0F3S EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b95 0F3T EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b96 0F3U EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b97 0F3V EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b98 0F3W EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b99 0F3X EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b9a 0F3Y EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b9b 0F3Z EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b9c 0G3A EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b9d 0G3B EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b9e 0G3C EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6b9f 0G3D EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ba0 0G3E EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ba1 0G3F EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ba2 0G3G EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ba3 0G3H EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ba4 0G3I EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ba5 0G3J EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ba6 0G3K EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ba7 0G3L EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ba8 0G3M EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ba9 0G3N EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6baa 0G3O EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bab 0G3P EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bac 0G3Q EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bad 0G3R EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bae 0G3S EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6baf 0G3T EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bb0 0G3U EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bb1 0G3V EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bb2 0G3W EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bb3 0G3X EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bb4 0G3Y EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bb5 0G3Z EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bb6 0H3A EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bb7 0H3B EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bb8 0H3C EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bb9 0H3D EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bba 0H3E EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bbb 0H3F EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bbc 0H3G EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bbd 0H3H EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bbe 0H3I EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bbf 0H3J EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bc0 0H3K EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bc1 0H3L EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bc2 0H3M EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bc3 0H3N EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bc4 0H3O EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bc5 0H3P EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bc6 0H3Q EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bc7 0H3R EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bc8 0H3S EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bc9 0H3T EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bca 0H3U EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bcb 0H3V EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bcc 0H3W EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bcd 0H3X EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bce 0H3Y EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bcf 0H3Z EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bd0 0I3A EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bd1 0I3B EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bd2 0I3C EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bd3 0I3D EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bd4 0I3E EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bd5 0I3F EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bd6 0I3G EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bd7 0I3H EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bd8 0I3I EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bd9 0I3J EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bda 0I3K EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bdb 0I3L EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bdc 0I3M EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bdd 0I3N EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bde 0I3O EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bdf 0I3P EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6be0 0I3Q EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6be1 0I3R EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6be2 0I3S EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6be3 0I3T EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6be4 0I3U EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6be5 0I3V EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6be6 0I3W EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6be7 0I3X EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6be8 0I3Y EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6be9 0I3Z EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bea 0J3A EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6beb 0J3B EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bec 0J3C EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bed 0J3D EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bee 0J3E EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bef 0J3F EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bf0 0J3G EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bf1 0J3H EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bf2 0J3I EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bf3 0J3J EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bf4 0J3K EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bf5 0J3L EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bf6 0J3M EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bf7 0J3N EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bf8 0J3O EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bf9 0J3P EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bfa 0J3Q EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bfb 0J3R EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bfc 0J3S EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bfd 0J3T EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bfe 0J3U EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6bff 0J3V EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c00 0J3W EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c01 0J3X EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c02 0J3Y EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c03 0J3Z EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c04 0K3A EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c05 0K3B EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c06 0K3C EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c07 0K3D EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c08 0K3E EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c09 0K3F EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c0a 0K3G EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c0b 0K3H EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c0c 0K3I EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c0d 0K3J EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c0e 0K3K EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c0f 0K3L EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c10 0K3M EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c11 0K3N EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c12 0K3O EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c13 0K3P EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c14 0K3Q EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c15 0K3R EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c16 0K3S EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c17 0K3T EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c18 0K3U EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c19 0K3V EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c1a 0K3W EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c1b 0K3X EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c1c 0K3Y EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c1d 0K3Z EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c1e 0L3A EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c1f 0L3B EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c20 0L3C EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c21 0L3D EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c22 0L3E EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c23 0L3F EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c24 0L3G EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c25 0L3H EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c26 0L3I EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c27 0L3J EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c28 0L3K EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c29 0L3L EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c2a 0L3M EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c2b 0L3N EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c2c 0L3O EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c2d 0L3P EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c2e 0L3Q EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c2f 0L3R EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c30 0L3S EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c31 0L3T EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c32 0L3U EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c33 0L3V EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c34 0L3W EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c35 0L3X EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c36 0L3Y EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c37 0L3Z EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c38 0M3A EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c39 0M3B EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c3a 0M3C EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c3b 0M3D EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c3c 0M3E EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c3d 0M3F EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c3e 0M3G EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c3f 0M3H EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c40 0M3I EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c41 0M3J EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c42 0M3K EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c43 0M3L EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c44 0M3M EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c45 0M3N EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c46 0M3O EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c47 0M3P EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c48 0M3Q EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c49 0M3R EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c4a 0M3S EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c4b 0M3T EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c4c 0M3U EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c4d 0M3V EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c4e 0M3W EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c4f 0M3X EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c50 0M3Y EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c51 0M3Z EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c52 0N3A EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c53 0N3B EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c54 0N3C EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c55 0N3D EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c56 0N3E EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c57 0N3F EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c58 0N3G EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c59 0N3H EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c5a 0N3I EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c5b 0N3J EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c5c 0N3K EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c5d 0N3L EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c5e 0N3M EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c5f 0N3N EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c60 0N3O EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c61 0N3P EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c62 0N3Q EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c63 0N3R EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c64 0N3S EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c65 0N3T EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c66 0N3U EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c67 0N3V EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c68 0N3W EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c69 0N3X EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c6a 0N3Y EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c6b 0N3Z EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c6c 0O3A EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c6d 0O3B EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c6e 0O3C EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c6f 0O3D EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c70 0O3E EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c71 0O3F EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c72 0O3G EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c73 0O3H EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c74 0O3I EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c75 0O3J EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c76 0O3K EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c77 0O3L EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c78 0O3M EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c79 0O3N EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c7a 0O3O EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c7b 0O3P EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c7c 0O3Q EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c7d 0O3R EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c7e 0O3S EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c7f 0O3T EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c80 0O3U EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c81 0O3V EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c82 0O3W EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c83 0O3X EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c84 0O3Y EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c85 0O3Z EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c86 0P3A EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c87 0P3B EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c88 0P3C EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c89 0P3D EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c8a 0P3E EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c8b 0P3F EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c8c 0P3G EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c8d 0P3H EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c8e 0P3I EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c8f 0P3J EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c90 0P3K EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c91 0P3L EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c92 0P3M EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c93 0P3N EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c94 0P3O EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c95 0P3P EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c96 0P3Q EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c97 0P3R EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c98 0P3S EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c99 0P3T EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c9a 0P3U EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c9b 0P3V EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c9c 0P3W EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c9d 0P3X EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c9e 0P3Y EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6c9f 0P3Z EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ca0 0Q3A EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ca1 0Q3B EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ca2 0Q3C EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ca3 0Q3D EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ca4 0Q3E EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ca5 0Q3F EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ca6 0Q3G EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ca7 0Q3H EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ca8 0Q3I EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ca9 0Q3J EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6caa 0Q3K EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cab 0Q3L EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cac 0Q3M EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cad 0Q3N EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cae 0Q3O EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6caf 0Q3P EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cb0 0Q3Q EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cb1 0Q3R EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cb2 0Q3S EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cb3 0Q3T EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cb4 0Q3U EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cb5 0Q3V EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cb6 0Q3W EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cb7 0Q3X EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cb8 0Q3Y EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cb9 0Q3Z EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cba 0R3A EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cbb 0R3B EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cbc 0R3C EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cbd 0R3D EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cbe 0R3E EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cbf 0R3F EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cc0 0R3G EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cc1 0R3H EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cc2 0R3I EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cc3 0R3J EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cc4 0R3K EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cc5 0R3L EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cc6 0R3M EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cc7 0R3N EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cc8 0R3O EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cc9 0R3P EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cca 0R3Q EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ccb 0R3R EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ccc 0R3S EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ccd 0R3T EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cce 0R3U EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ccf 0R3V EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cd0 0R3W EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cd1 0R3X EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cd2 0R3Y EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cd3 0R3Z EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cd4 0S3A EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cd5 0S3B EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cd6 0S3C EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cd7 0S3D EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cd8 0S3E EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cd9 0S3F EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cda 0S3G EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cdb 0S3H EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cdc 0S3I EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cdd 0S3J EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cde 0S3K EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cdf 0S3L EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ce0 0S3M EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ce1 0S3N EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ce2 0S3O EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ce3 0S3P EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ce4 0S3Q EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ce5 0S3R EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ce6 0S3S EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ce7 0S3T EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ce8 0S3U EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ce9 0S3V EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cea 0S3W EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ceb 0S3X EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cec 0S3Y EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6ced 0S3Z EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cee 0T3A EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cef 0T3B EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cf0 0T3C EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cf1 0T3D EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cf2 0T3E EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cf3 0T3F EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cf4 0T3G EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cf5 0T3H EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cf6 0T3I EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cf7 0T3J EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cf8 0T3K EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cf9 0T3L EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cfa 0T3M EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cfb 0T3N EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cfc 0T3O EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cfd 0T3P EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cfe 0T3Q EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6cff 0T3R EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6d00 0T3S EMPTY NORMAL 3 Co CHARACTER BGI paletted +0x6d01 0T3T EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d02 0T3U EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d03 0T3V EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d04 0T3W EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d05 0T3X EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d06 0T3Y EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d07 0T3Z EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d08 0U3A EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d09 0U3B EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d0a 0U3C EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d0b 0U3D EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d0c 0U3E EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d0d 0U3F EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d0e 0U3G EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d0f 0U3H EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d10 0U3I EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d11 0U3J EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d12 0U3K EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d13 0U3L EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d14 0U3M EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d15 0U3N EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d16 0U3O EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d17 0U3P EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d18 0U3Q EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d19 0U3R EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d1a 0U3S EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d1b 0U3T EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d1c 0U3U EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d1d 0U3V EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d1e 0U3W EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d1f 0U3X EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d20 0U3Y EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d21 0U3Z EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d22 0V3A EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d23 0V3B EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d24 0V3C EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d25 0V3D EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d26 0V3E EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d27 0V3F EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d28 0V3G EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d29 0V3H EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d2a 0V3I EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d2b 0V3J EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d2c 0V3K EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d2d 0V3L EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d2e 0V3M EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d2f 0V3N EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d30 0V3O EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d31 0V3P EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d32 0V3Q EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d33 0V3R EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d34 0V3S EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d35 0V3T EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d36 0V3U EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d37 0V3V EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d38 0V3W EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d39 0V3X EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d3a 0V3Y EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d3b 0V3Z EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d3c 0W3A EMPTY LARGE 5 Co CHARACTER BGI paletted +0x6d3d 0A0A EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d3e 0A0B EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d3f 0A0C EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d40 0A0D EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d41 0A0E EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d42 0A0F EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d43 0A0G EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d44 0A0H EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d45 0A0I EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d46 0A0J EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d47 0A0K EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d48 0A0L EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d49 0A0M EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d4a 0A0N EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d4b 0A0O EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d4c 0A0P EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d4d 0A0Q EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d4e 0A0R EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d4f 0A0S EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d50 0A0T EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d51 0A0U EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d52 0A0V EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d53 0A0W EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d54 0A0X EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d55 0A0Y EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d56 0A0Z EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d57 0B0A EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d58 0B0B EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d59 0B0C EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d5a 0B0D EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d5b 0B0E EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d5c 0B0F EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d5d 0B0G EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d5e 0B0H EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d5f 0B0I EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d60 0B0J EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d61 0B0K EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d62 0B0L EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d63 0B0M EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d64 0B0N EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d65 0B0O EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d66 0B0P EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d67 0B0Q EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d68 0B0R EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d69 0B0S EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d6a 0B0T EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d6b 0B0U EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d6c 0B0V EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d6d 0B0W EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d6e 0B0X EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d6f 0B0Y EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d70 0B0Z EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d71 0C0A EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d72 0C0B EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d73 0C0C EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d74 0C0D EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d75 0C0E EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d76 0C0F EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d77 0C0G EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d78 0C0H EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d79 0C0I EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d7a 0C0J EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d7b 0C0K EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d7c 0C0L EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d7d 0C0M EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d7e 0C0N EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d7f 0C0O EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d80 0C0P EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d81 0C0Q EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d82 0C0R EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d83 0C0S EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d84 0C0T EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d85 0C0U EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d86 0C0V EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d87 0C0W EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d88 0C0X EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d89 0C0Y EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d8a 0C0Z EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d8b 0D0A EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d8c 0D0B EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d8d 0D0C EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d8e 0D0D EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d8f 0D0E EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d90 0D0F EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d91 0D0G EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d92 0D0H EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d93 0D0I EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d94 0D0J EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d95 0D0K EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d96 0D0L EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d97 0D0M EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6d98 0D0N EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d99 0D0O EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d9a 0D0P EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d9b 0D0Q EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d9c 0D0R EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6d9d 0D0S EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d9e 0D0T EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6d9f 0D0U EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6da0 0D0V EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6da1 0D0W EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6da2 0D0X EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6da3 0D0Y EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6da4 0D0Z EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6da5 0E0A EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6da6 0E0B EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6da7 0E0C EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6da8 0E0D EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6da9 0E0E EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6daa 0E0F EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6dab 0E0G EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6dac 0E0H EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6dad 0E0I EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6dae 0E0J EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6daf 0E0K EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6db0 0E0L EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6db1 0E0M EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6db2 0E0N EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6db3 0E0O EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6db4 0E0P EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6db5 0E0Q EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6db6 0E0R EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6db7 0E0S EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6db8 0E0T EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6db9 0E0U EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6dba 0E0V EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6dbb 0E0W EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6dbc 0E0X EMPTY NORMAL 3 Co CHARACTER BGI WPS paletted +0x6dbd 0E0Y EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6dbe 0E0Z EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6dbf 0F0A EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6dc0 0F0B EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6dc1 0F0C EMPTY NORMAL 3 Co CHARACTER BGI WPL paletted +0x6dc2 0F0D EMPTY NORMAL 3 Co CHARACTER BGI WPM paletted +0x6dc3 0A3A EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dc4 0A3B EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dc5 0A3C EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dc6 0A3D EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dc7 0A3E EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dc8 0A3F EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dc9 0A3G EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dca 0A3H EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dcb 0A3I EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dcc 0A3J EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dcd 0A3K EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dce 0A3L EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dcf 0A3M EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dd0 0A3N EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dd1 0A3O EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dd2 0A3P EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dd3 0A3Q EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dd4 0A3R EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dd5 0A3S EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dd6 0A3T EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dd7 0A3U EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dd8 0A3V EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dd9 0A3W EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dda 0A3X EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ddb 0A3Y EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ddc 0A3Z EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ddd 0B3A EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dde 0B3B EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ddf 0B3C EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6de0 0B3D EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6de1 0B3E EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6de2 0B3F EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6de3 0B3G EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6de4 0B3H EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6de5 0B3I EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6de6 0B3J EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6de7 0B3K EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6de8 0B3L EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6de9 0B3M EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dea 0B3N EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6deb 0B3O EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dec 0B3P EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ded 0B3Q EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dee 0B3R EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6def 0B3S EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6df0 0B3T EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6df1 0B3U EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6df2 0B3V EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6df3 0B3W EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6df4 0B3X EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6df5 0B3Y EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6df6 0B3Z EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6df7 0C3A EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6df8 0C3B EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6df9 0C3C EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dfa 0C3D EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dfb 0C3E EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dfc 0C3F EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dfd 0C3G EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dfe 0C3H EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6dff 0C3I EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e00 0C3J EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e01 0C3K EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e02 0C3L EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e03 0C3M EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e04 0C3N EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e05 0C3O EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e06 0C3P EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e07 0C3Q EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e08 0C3R EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e09 0C3S EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e0a 0C3T EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e0b 0C3U EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e0c 0C3V EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e0d 0C3W EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e0e 0C3X EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e0f 0C3Y EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e10 0C3Z EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e11 0D3A EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e12 0D3B EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e13 0D3C EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e14 0D3D EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e15 0D3E EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e16 0D3F EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e17 0D3G EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e18 0D3H EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e19 0D3I EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e1a 0D3J EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e1b 0D3K EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e1c 0D3L EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e1d 0D3M EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e1e 0D3N EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e1f 0D3O EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e20 0D3P EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e21 0D3Q EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e22 0D3R EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e23 0D3S EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e24 0D3T EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e25 0D3U EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e26 0D3V EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e27 0D3W EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e28 0D3X EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e29 0D3Y EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e2a 0D3Z EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e2b 0E3A EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e2c 0E3B EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e2d 0E3C EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e2e 0E3D EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e2f 0E3E EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e30 0E3F EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e31 0E3G EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e32 0E3H EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e33 0E3I EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e34 0E3J EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e35 0E3K EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e36 0E3L EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e37 0E3M EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e38 0E3N EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e39 0E3O EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e3a 0E3P EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e3b 0E3Q EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e3c 0E3R EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e3d 0E3S EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e3e 0E3T EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e3f 0E3U EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e40 0E3V EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e41 0E3W EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e42 0E3X EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e43 0E3Y EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e44 0E3Z EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e45 0F3A EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e46 0F3B EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e47 0F3C EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e48 0F3D EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e49 0F3E EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e4a 0F3F EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e4b 0F3G EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e4c 0F3H EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e4d 0F3I EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e4e 0F3J EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e4f 0F3K EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e50 0F3L EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e51 0F3M EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e52 0F3N EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e53 0F3O EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e54 0F3P EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e55 0F3Q EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e56 0F3R EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e57 0F3S EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e58 0F3T EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e59 0F3U EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e5a 0F3V EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e5b 0F3W EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e5c 0F3X EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e5d 0F3Y EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e5e 0F3Z EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e5f 0G3A EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e60 0G3B EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e61 0G3C EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e62 0G3D EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e63 0G3E EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e64 0G3F EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e65 0G3G EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e66 0G3H EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e67 0G3I EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e68 0G3J EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e69 0G3K EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e6a 0G3L EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e6b 0G3M EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e6c 0G3N EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e6d 0G3O EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e6e 0G3P EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e6f 0G3Q EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e70 0G3R EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e71 0G3S EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e72 0G3T EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e73 0G3U EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e74 0G3V EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e75 0G3W EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e76 0G3X EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e77 0G3Y EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e78 0G3Z EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e79 0H3A EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e7a 0H3B EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e7b 0H3C EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e7c 0H3D EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e7d 0H3E EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e7e 0H3F EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e7f 0H3G EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e80 0H3H EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e81 0H3I EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e82 0H3J EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e83 0H3K EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e84 0H3L EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e85 0H3M EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e86 0H3N EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e87 0H3O EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e88 0H3P EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e89 0H3Q EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e8a 0H3R EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e8b 0H3S EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e8c 0H3T EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e8d 0H3U EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e8e 0H3V EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e8f 0H3W EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e90 0H3X EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e91 0H3Y EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e92 0H3Z EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e93 0I3A EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e94 0I3B EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e95 0I3C EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e96 0I3D EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e97 0I3E EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e98 0I3F EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e99 0I3G EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e9a 0I3H EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e9b 0I3I EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e9c 0I3J EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e9d 0I3K EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e9e 0I3L EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6e9f 0I3M EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ea0 0I3N EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ea1 0I3O EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ea2 0I3P EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ea3 0I3Q EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ea4 0I3R EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ea5 0I3S EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ea6 0I3T EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ea7 0I3U EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ea8 0I3V EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ea9 0I3W EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eaa 0I3X EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eab 0I3Y EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eac 0I3Z EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ead 0J3A EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eae 0J3B EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eaf 0J3C EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eb0 0J3D EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eb1 0J3E EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eb2 0J3F EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eb3 0J3G EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eb4 0J3H EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eb5 0J3I EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eb6 0J3J EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eb7 0J3K EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eb8 0J3L EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eb9 0J3M EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eba 0J3N EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ebb 0J3O EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ebc 0J3P EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ebd 0J3Q EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ebe 0J3R EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ebf 0J3S EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ec0 0J3T EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ec1 0J3U EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ec2 0J3V EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ec3 0J3W EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ec4 0J3X EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ec5 0J3Y EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ec6 0J3Z EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ec7 0K3A EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ec8 0K3B EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ec9 0K3C EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eca 0K3D EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ecb 0K3E EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ecc 0K3F EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ecd 0K3G EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ece 0K3H EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ecf 0K3I EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ed0 0K3J EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ed1 0K3K EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ed2 0K3L EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ed3 0K3M EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ed4 0K3N EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ed5 0K3O EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ed6 0K3P EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ed7 0K3Q EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ed8 0K3R EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ed9 0K3S EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eda 0K3T EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6edb 0K3U EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6edc 0K3V EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6edd 0K3W EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ede 0K3X EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6edf 0K3Y EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ee0 0K3Z EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ee1 0L3A EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ee2 0L3B EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ee3 0L3C EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ee4 0L3D EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ee5 0L3E EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ee6 0L3F EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ee7 0L3G EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ee8 0L3H EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ee9 0L3I EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eea 0L3J EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eeb 0L3K EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eec 0L3L EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eed 0L3M EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eee 0L3N EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eef 0L3O EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ef0 0L3P EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ef1 0L3Q EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ef2 0L3R EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ef3 0L3S EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ef4 0L3T EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ef5 0L3U EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ef6 0L3V EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ef7 0L3W EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ef8 0L3X EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6ef9 0L3Y EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6efa 0L3Z EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6efb 0M3A EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6efc 0M3B EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6efd 0M3C EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6efe 0M3D EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6eff 0M3E EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f00 0M3F EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f01 0M3G EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f02 0M3H EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f03 0M3I EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f04 0M3J EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f05 0M3K EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f06 0M3L EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f07 0M3M EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f08 0M3N EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f09 0M3O EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f0a 0M3P EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f0b 0M3Q EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f0c 0M3R EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f0d 0M3S EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f0e 0M3T EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f0f 0M3U EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f10 0M3V EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f11 0M3W EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f12 0M3X EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f13 0M3Y EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f14 0M3Z EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f15 0N3A EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f16 0N3B EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f17 0N3C EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f18 0N3D EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f19 0N3E EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f1a 0N3F EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f1b 0N3G EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f1c 0N3H EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f1d 0N3I EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f1e 0N3J EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f1f 0N3K EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f20 0N3L EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f21 0N3M EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f22 0N3N EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f23 0N3O EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f24 0N3P EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f25 0N3Q EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f26 0N3R EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f27 0N3S EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f28 0N3T EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f29 0N3U EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f2a 0N3V EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f2b 0N3W EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f2c 0N3X EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f2d 0N3Y EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f2e 0N3Z EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f2f 0O3A EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f30 0O3B EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f31 0O3C EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f32 0O3D EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f33 0O3E EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f34 0O3F EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f35 0O3G EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f36 0O3H EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f37 0O3I EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f38 0O3J EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f39 0O3K EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f3a 0O3L EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f3b 0O3M EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f3c 0O3N EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f3d 0O3O EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f3e 0O3P EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f3f 0O3Q EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f40 0O3R EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f41 0O3S EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f42 0O3T EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f43 0O3U EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f44 0O3V EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f45 0O3W EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f46 0O3X EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f47 0O3Y EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f48 0O3Z EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f49 0P3A EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f4a 0P3B EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f4b 0P3C EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f4c 0P3D EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f4d 0P3E EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f4e 0P3F EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f4f 0P3G EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f50 0P3H EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f51 0P3I EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f52 0P3J EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f53 0P3K EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f54 0P3L EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f55 0P3M EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f56 0P3N EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f57 0P3O EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f58 0P3P EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f59 0P3Q EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f5a 0P3R EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f5b 0P3S EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f5c 0P3T EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f5d 0P3U EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f5e 0P3V EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f5f 0P3W EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f60 0P3X EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f61 0P3Y EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f62 0P3Z EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f63 0Q3A EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f64 0Q3B EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f65 0Q3C EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f66 0Q3D EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f67 0Q3E EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f68 0Q3F EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f69 0Q3G EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f6a 0Q3H EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f6b 0Q3I EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f6c 0Q3J EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f6d 0Q3K EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f6e 0Q3L EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f6f 0Q3M EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f70 0Q3N EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f71 0Q3O EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f72 0Q3P EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f73 0Q3Q EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f74 0Q3R EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f75 0Q3S EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f76 0Q3T EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f77 0Q3U EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f78 0Q3V EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f79 0Q3W EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f7a 0Q3X EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f7b 0Q3Y EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f7c 0Q3Z EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f7d 0R3A EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f7e 0R3B EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f7f 0R3C EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f80 0R3D EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f81 0R3E EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f82 0R3F EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f83 0R3G EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f84 0R3H EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f85 0R3I EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f86 0R3J EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f87 0R3K EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f88 0R3L EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f89 0R3M EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f8a 0R3N EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f8b 0R3O EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f8c 0R3P EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f8d 0R3Q EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f8e 0R3R EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f8f 0R3S EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f90 0R3T EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f91 0R3U EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f92 0R3V EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f93 0R3W EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f94 0R3X EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f95 0R3Y EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f96 0R3Z EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f97 0S3A EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f98 0S3B EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f99 0S3C EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f9a 0S3D EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f9b 0S3E EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f9c 0S3F EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f9d 0S3G EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f9e 0S3H EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6f9f 0S3I EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fa0 0S3J EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fa1 0S3K EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fa2 0S3L EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fa3 0S3M EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fa4 0S3N EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fa5 0S3O EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fa6 0S3P EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fa7 0S3Q EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fa8 0S3R EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fa9 0S3S EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6faa 0S3T EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fab 0S3U EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fac 0S3V EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fad 0S3W EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fae 0S3X EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6faf 0S3Y EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fb0 0S3Z EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fb1 0T3A EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fb2 0T3B EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fb3 0T3C EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fb4 0T3D EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fb5 0T3E EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fb6 0T3F EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fb7 0T3G EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fb8 0T3H EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fb9 0T3I EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fba 0T3J EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fbb 0T3K EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fbc 0T3L EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fbd 0T3M EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fbe 0T3N EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fbf 0T3O EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fc0 0T3P EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fc1 0T3Q EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fc2 0T3R EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fc3 0T3S EMPTY NORMAL 3 Co CHARACTER BGI unpaletted +0x6fc4 0T3T EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fc5 0T3U EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fc6 0T3V EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fc7 0T3W EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fc8 0T3X EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fc9 0T3Y EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fca 0T3Z EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fcb 0U3A EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fcc 0U3B EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fcd 0U3C EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fce 0U3D EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fcf 0U3E EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fd0 0U3F EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fd1 0U3G EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fd2 0U3H EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fd3 0U3I EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fd4 0U3J EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fd5 0U3K EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fd6 0U3L EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fd7 0U3M EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fd8 0U3N EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fd9 0U3O EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fda 0U3P EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fdb 0U3Q EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fdc 0U3R EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fdd 0U3S EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fde 0U3T EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fdf 0U3U EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fe0 0U3V EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fe1 0U3W EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fe2 0U3X EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fe3 0U3Y EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fe4 0U3Z EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fe5 0V3A EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fe6 0V3B EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fe7 0V3C EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fe8 0V3D EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fe9 0V3E EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fea 0V3F EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6feb 0V3G EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fec 0V3H EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fed 0V3I EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fee 0V3J EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fef 0V3K EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6ff0 0V3L EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6ff1 0V3M EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6ff2 0V3N EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6ff3 0V3O EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6ff4 0V3P EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6ff5 0V3Q EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6ff6 0V3R EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6ff7 0V3S EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6ff8 0V3T EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6ff9 0V3U EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6ffa 0V3V EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6ffb 0V3W EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6ffc 0V3X EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6ffd 0V3Y EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6ffe 0V3Z EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0x6fff 0W3A EMPTY LARGE 5 Co CHARACTER BGI unpaletted +0xa001 2AA EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa002 2AB EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa003 2AC EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa004 2AD EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa005 2AE EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa006 2AF EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa007 2AG EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa008 2AH EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa009 2AI EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa00a 2AJ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa00b 2AK EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa00c 2AL EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa00d 2AM EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa00e 2AN EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa00f 2AO EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa010 2AP EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa011 2AQ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa012 2AR EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa013 2AS EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa014 2AT EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa015 2AU EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa016 2AV EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa017 2AW EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa018 2AX EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa019 2AY EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa01a 2AZ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa01b 2BA EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa01c 2BB EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa01d 2BC EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa01e 2BD EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa01f 2BE EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa020 2BF EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa021 2BG EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa022 2BH EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa023 2BI EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa024 2BJ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa025 2BK EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa026 2BL EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa027 2BM EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa028 2BN EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa029 2BO EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa02a 2BP EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa02b 2BQ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa02c 2BR EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa02d 2BS EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa02e 2BT EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa02f 2BU EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa030 2BV EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa031 2BW EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa032 2BX EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa033 2BY EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa034 2BZ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa035 2CA EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa036 2CB EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa037 2CC EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa038 2CD EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa039 2CE EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa03a 2CF EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa03b 2CG EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa03c 2CH EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa03d 2CI EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa03e 2CJ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa03f 2CK EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa040 2CL EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa041 2CM EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa042 2CN EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa043 2CO EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa044 2CP EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa045 2CQ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa046 2CR EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa047 2CS EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa048 2CT EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa049 2CU EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa04a 2CV EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa04b 2CW EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa04c 2CX EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa04d 2CY EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa04e 2CZ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa04f 2DA EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa050 2DB EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa051 2DC EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa052 2DD EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa053 2DE EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa054 2DF EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa055 2DG EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa056 2DH EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa057 2DI EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa058 2DJ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa059 2DK EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa05a 2DL EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa05b 2DM EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa05c 2DN EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa05d 2DO EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa05e 2DP EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa05f 2DQ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa060 2DR EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa061 2DS EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa062 2DT EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa063 2DU EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa064 2DV EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa065 2DW EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa066 2DX EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa067 2DY EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa068 2DZ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa069 2EA EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa06a 2EB EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa06b 2EC EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa06c 2ED EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa06d 2EE EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa06e 2EF EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa06f 2EG EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa070 2EH EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa071 2EI EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa072 2EJ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa073 2EK EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa074 2EL EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa075 2EM EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa076 2EN EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa077 2EO EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa078 2EP EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa079 2EQ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa07a 2ER EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa07b 2ES EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa07c 2ET EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa07d 2EU EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa07e 2EV EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa07f 2EW EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa080 2EX EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa081 2EY EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa082 2EZ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa083 2FA EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa084 2FB EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa085 2FC EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa086 2FD EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa087 2FE EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa088 2FF EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa089 2FG EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa08a 2FH EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa08b 2FI EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa08c 2FJ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa08d 2FK EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa08e 2FL EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa08f 2FM EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa090 2FN EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa091 2FO EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa092 2FP EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa093 2FQ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa094 2FR EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa095 2FS EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa096 2FT EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa097 2FU EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa098 2FV EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa099 2FW EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa09a 2FX EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa09b 2FY EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa09c 2FZ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa09d 2GA EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa09e 2GB EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa09f 2GC EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0a0 2GD EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0a1 2GE EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0a2 2GF EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0a3 2GG EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0a4 2GH EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0a5 2GI EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0a6 2GJ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0a7 2GK EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0a8 2GL EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0a9 2GM EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0aa 2GN EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0ab 2GO EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0ac 2GP EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0ad 2GQ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0ae 2GR EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0af 2GS EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0b0 2GT EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0b1 2GU EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0b2 2GV EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0b3 2GW EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0b4 2GX EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0b5 2GY EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0b6 2GZ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0b7 2HA EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0b8 2HB EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0b9 2HC EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0ba 2HD EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0bb 2HE EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0bc 2HF EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0bd 2HG EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0be 2HH EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0bf 2HI EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0c0 2HJ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0c1 2HK EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0c2 2HL EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0c3 2HM EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0c4 2HN EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0c5 2HO EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0c6 2HP EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0c7 2HQ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0c8 2HR EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0c9 2HS EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0ca 2HT EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0cb 2HU EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0cc 2HV EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0cd 2HW EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0ce 2HX EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0cf 2HY EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0d0 2HZ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0d1 2IA EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0d2 2IB EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0d3 2IC EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0d4 2ID EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0d5 2IE EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0d6 2IF EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0d7 2IG EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0d8 2IH EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0d9 2II EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0da 2IJ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0db 2IK EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0dc 2IL EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0dd 2IM EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0de 2IN EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0df 2IO EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0e0 2IP EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0e1 2IQ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0e2 2IR EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0e3 2IS EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0e4 2IT EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0e5 2IU EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0e6 2IV EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0e7 2IW EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0e8 2IX EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0e9 2IY EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0ea 2IZ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0eb 2JA EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0ec 2JB EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0ed 2JC EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0ee 2JD EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0ef 2JE EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0f0 2JF EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0f1 2JG EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0f2 2JH EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0f3 2JI EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0f4 2JJ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0f5 2JK EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0f6 2JL EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0f7 2JM EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0f8 2JN EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0f9 2JO EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0fa 2JP EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0fb 2JQ EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0fc 2JR EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0fd 2JS EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0fe 2JT EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa0ff 2JU EMPTY NORMAL 3 Xo BGI MONSTER LONG unpaletted +0xa101 2JW EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa102 2JX EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa103 2JY EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa104 2JZ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa105 2KA EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa106 2KB EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa107 2KC EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa108 2KD EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa109 2KE EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa10a 2KF EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa10b 2KG EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa10c 2KH EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa10d 2KI EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa10e 2KJ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa10f 2KK EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa110 2KL EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa111 2KM EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa112 2KN EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa113 2KO EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa114 2KP EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa115 2KQ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa116 2KR EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa117 2KS EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa118 2KT EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa119 2KU EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa11a 2KV EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa11b 2KW EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa11c 2KX EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa11d 2KY EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa11e 2KZ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa11f 2LA EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa120 2LB EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa121 2LC EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa122 2LD EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa123 2LE EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa124 2LF EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa125 2LG EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa126 2LH EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa127 2LI EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa128 2LJ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa129 2LK EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa12a 2LL EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa12b 2LM EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa12c 2LN EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa12d 2LO EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa12e 2LP EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa12f 2LQ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa130 2LR EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa131 2LS EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa132 2LT EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa133 2LU EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa134 2LV EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa135 2LW EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa136 2LX EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa137 2LY EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa138 2LZ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa139 2MA EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa13a 2MB EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa13b 2MC EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa13c 2MD EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa13d 2ME EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa13e 2MF EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa13f 2MG EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa140 2MH EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa141 2MI EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa142 2MJ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa143 2MK EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa144 2ML EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa145 2MM EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa146 2MN EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa147 2MO EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa148 2MP EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa149 2MQ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa14a 2MR EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa14b 2MS EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa14c 2MT EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa14d 2MU EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa14e 2MV EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa14f 2MW EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa150 2MX EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa151 2MY EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa152 2MZ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa153 2NA EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa154 2NB EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa155 2NC EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa156 2ND EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa157 2NE EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa158 2NF EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa159 2NG EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa15a 2NH EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa15b 2NI EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa15c 2NJ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa15d 2NK EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa15e 2NL EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa15f 2NM EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa160 2NN EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa161 2NO EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa162 2NP EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa163 2NQ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa164 2NR EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa165 2NS EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa166 2NT EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa167 2NU EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa168 2NV EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa169 2NW EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa16a 2NX EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa16b 2NY EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa16c 2NZ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa16d 2OA EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa16e 2OB EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa16f 2OC EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa170 2OD EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa171 2OE EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa172 2OF EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa173 2OG EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa174 2OH EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa175 2OI EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa176 2OJ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa177 2OK EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa178 2OL EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa179 2OM EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa17a 2ON EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa17b 2OO EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa17c 2OP EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa17d 2OQ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa17e 2OR EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa17f 2OS EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa180 2OT EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa181 2OU EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa182 2OV EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa183 2OW EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa184 2OX EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa185 2OY EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa186 2OZ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa187 2PA EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa188 2PB EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa189 2PC EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa18a 2PD EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa18b 2PE EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa18c 2PF EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa18d 2PG EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa18e 2PH EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa18f 2PI EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa190 2PJ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa191 2PK EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa192 2PL EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa193 2PM EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa194 2PN EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa195 2PO EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa196 2PP EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa197 2PQ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa198 2PR EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa199 2PS EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa19a 2PT EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa19b 2PU EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa19c 2PV EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa19d 2PW EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa19e 2PX EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa19f 2PY EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1a0 2PZ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1a1 2QA EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1a2 2QB EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1a3 2QC EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1a4 2QD EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1a5 2QE EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1a6 2QF EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1a7 2QG EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1a8 2QH EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1a9 2QI EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1aa 2QJ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1ab 2QK EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1ac 2QL EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1ad 2QM EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1ae 2QN EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1af 2QO EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1b0 2QP EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1b1 2QQ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1b2 2QR EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1b3 2QS EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1b4 2QT EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1b5 2QU EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1b6 2QV EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1b7 2QW EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1b8 2QX EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1b9 2QY EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1ba 2QZ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1bb 2RA EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1bc 2RB EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1bd 2RC EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1be 2RD EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1bf 2RE EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1c0 2RF EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1c1 2RG EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1c2 2RH EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1c3 2RI EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1c4 2RJ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1c5 2RK EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1c6 2RL EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1c7 2RM EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1c8 2RN EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1c9 2RO EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1ca 2RP EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1cb 2RQ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1cc 2RR EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1cd 2RS EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1ce 2RT EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1cf 2RU EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1d0 2RV EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1d1 2RW EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1d2 2RX EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1d3 2RY EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1d4 2RZ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1d5 2SA EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1d6 2SB EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1d7 2SC EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1d8 2SD EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1d9 2SE EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1da 2SF EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1db 2SG EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1dc 2SH EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1dd 2SI EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1de 2SJ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1df 2SK EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1e0 2SL EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1e1 2SM EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1e2 2SN EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1e3 2SO EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1e4 2SP EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1e5 2SQ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1e6 2SR EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1e7 2SS EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1e8 2ST EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1e9 2SU EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1ea 2SV EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1eb 2SW EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1ec 2SX EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1ed 2SY EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1ee 2SZ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1ef 2TA EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1f0 2TB EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1f1 2TC EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1f2 2TD EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1f3 2TE EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1f4 2TF EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1f5 2TG EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1f6 2TH EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1f7 2TI EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1f8 2TJ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1f9 2TK EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1fa 2TL EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1fb 2TM EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1fc 2TN EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1fd 2TO EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1fe 2TP EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted +0xa1ff 2TQ EMPTY LARGE 5 Xo BGI MONSTER LONG unpaletted diff --git a/src/org/infinity/resource/cre/decoder/tables/notes.txt b/src/org/infinity/resource/cre/decoder/tables/notes.txt new file mode 100644 index 000000000..00fbd4189 --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/tables/notes.txt @@ -0,0 +1,73 @@ +Table format +~~~~~~~~~~~~ + +Non-PST: (all games except PST and PST:EE) +- header column: (int/hex, composite) + - contains the numeric animation id (usually in hexadecimal notation) + - may contain an optional mask value (separated from id by an underscore) + - indicates a slot range that uses the same definitions +- RESREF (string) + - the animation prefix +- TYPE (int) + - an index referring to a SpriteDecoder.AnimationType enum +- ELLIPSE (int) + - the selection circle radius +- SPACE (int) + - the personal space in search map units +- BLENDING (int) + - a bitfield: bit0=brightest, bit1=multiply_blend, bit2=light_source +- PALETTE (string) + - resref of a replacement palette + - meaning depends on animation type +- PALETTE2 (string) + - resref of a secondary replacement palette + - meaning depends on animation type +- TRANSLUCENT (boolean) + - whether animation is semi-transparent +- CLOWN (bool) + - whether false color replacement is enabled +- SPLIT (bool) + - whether animations are spread over multiple sub-BAMs + - animation type "effect": specifies "random_render" attribute +- HELMET (bool/int) + - animation types "character", "character_old": whether helmet overlay is drawn + - animation types "monster_layered", "monster_quadrant": specifies "caster" attribute + - animation type "effect": specifies BAM cycle +- WEAPON (bool) + - animation type "character_old": whether weapon overlay is drawn + - animation type "monster_iwd": specifies "weapon_left_handed" attribute + - animation type "monster_layered": specifies "dual_attack" attribute +- HEIGHT (string) + - animation type "character", "character_old": overlay prefix for weapons and helmets + - animation type "monster_layered": specifies "resref_weapon1" attribute + - animation type "effect": if SPLIT=1: specifies a second BAM resref to be used randomly +- HEIGHT_SHIELD (string) + - animation type "character": overlay prefix for shields + - animation type "monster_layered": specifies "resref_weapon2" attribute + + +PST: (PST and PST:EE only) +- header column: (int/hex) + - contains the base part of the numeric animation id (usually in hexadecimal notation) + - high byte value is determined by the animation class + - slots with id >= 0x1000 are handled specially +- RESREF (string) + - combination of prefix (one char), resref (3 chars) and suffix (one char) + - prefix "c" indicates slot range (PST: 0xE000, PSTEE: 0xF000) + - prefix "d" indicates slot range (PST: 0x6000, PSTEE: 0xF000) + - action prefix is determined by the animation class + - special animation slots are added verbatim +- RESREF2 (string) + - contains a secondary BAM resref used by a small number of special animation slots +- TYPE (int) + - an index referring to a SpriteDecoder.AnimationType enum +- ELLIPSE (int) + - the selection circle radius +- SPACE (int) + - the personal space in search map units +- CLOWN (int) + - number of false color ranges to replace (* indicates disabled false color replacement) +- ARMOR (int) + - (currently) unused by NI +- BESTIARY (int) + - (currently) unused by NI diff --git a/src/org/infinity/resource/cre/decoder/util/AnimationInfo.java b/src/org/infinity/resource/cre/decoder/util/AnimationInfo.java new file mode 100644 index 000000000..0532d0c70 --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/util/AnimationInfo.java @@ -0,0 +1,351 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.decoder.util; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.EnumMap; +import java.util.EnumSet; +import java.util.List; + +import org.infinity.resource.Profile; +import org.infinity.util.Misc; +import org.infinity.util.tuples.Couple; + +/** + * Static class containing animation slot and type information. + */ +public class AnimationInfo +{ + // Predefined sets of games with common animation slot mappings + private static final EnumSet TYPE_GAME_BG1 = EnumSet.of(Profile.Game.BG1, Profile.Game.BG1TotSC); + private static final EnumSet TYPE_GAME_BG2_TOB = EnumSet.of(Profile.Game.BG2ToB, Profile.Game.BGT, Profile.Game.Tutu); + // all BG2 game variants + private static final EnumSet TYPE_GAME_BG2 = EnumSet.of(Profile.Game.BG2SoA, Profile.Game.BG2ToB, Profile.Game.BGT, + Profile.Game.Tutu); + private static final EnumSet TYPE_GAME_IWD = EnumSet.of(Profile.Game.IWD); + private static final EnumSet TYPE_GAME_IWD_HOW = EnumSet.of(Profile.Game.IWDHoW, Profile.Game.IWDHowTotLM); + private static final EnumSet TYPE_GAME_IWD2 = EnumSet.of(Profile.Game.IWD2); + private static final EnumSet TYPE_GAME_PST = EnumSet.of(Profile.Game.PST); + // all EE games + private static final EnumSet TYPE_GAME_EE = EnumSet.of(Profile.Game.BG1EE, Profile.Game.BG1SoD, Profile.Game.BG2EE, + Profile.Game.EET, Profile.Game.IWDEE, Profile.Game.PSTEE); + private static final EnumSet TYPE_GAME_PSTEE = EnumSet.of(Profile.Game.PSTEE); + // all games except PST + private static final EnumSet TYPE_GAME_ALL = EnumSet.complementOf(EnumSet.of(Profile.Game.Unknown, Profile.Game.PST)); + + // Predefined slot ranges for specific animation types + private static final List RANGE_EFFECT = Arrays.asList(new NumberRange(0, 0xfff)); + private static final List RANGE_MONSTER_QUADRANT = Arrays.asList(new NumberRange(0x1000, 0x1ff)); + private static final List RANGE_MONSTER_MULTI = Arrays.asList(new NumberRange(0x1200, 0xff), + new NumberRange(0x1400, 0xbff)); + private static final List RANGE_MONSTER_MULTI_NEW = Arrays.asList(new NumberRange(0x1300, 0xff)); + private static final List RANGE_MONSTER_LAYERED = Arrays.asList(new NumberRange(0x8000, 0xfff)); + private static final List RANGE_MONSTER_LAYERED_SPELL = Arrays.asList(new NumberRange(0x2000, 0xfff)); + private static final List RANGE_MONSTER_ANKHEG = Arrays.asList(new NumberRange(0x3000, 0xfff)); + private static final List RANGE_TOWN_STATIC = Arrays.asList(new NumberRange(0x4000, 0xfff)); + private static final List RANGE_CHARACTER = Arrays.asList(new NumberRange(0x5000, 0x3ff), + new NumberRange(0x5500, 0xff), + new NumberRange(0x6000, 0x3ff), + new NumberRange(0x6500, 0xff)); + private static final List RANGE_CHARACTER_IA = Arrays.asList(new NumberRange(0x6600, 0x4ff)); + private static final List RANGE_CHARACTER_OLD = Arrays.asList(new NumberRange(0x5400, 0xff), + new NumberRange(0x5600, 0x9ff), + new NumberRange(0x6400, 0xff), + new NumberRange(0x6600, 0x9ff)); + private static final List RANGE_MONSTER = Arrays.asList(new NumberRange(0x7002, 0xd, 0x00, 0x1f, 4), + new NumberRange(0x7004, 0xb, 0x20, 0x0f, 4), + new NumberRange(0x7000, 0xf, 0x30, 0x0f, 4), + new NumberRange(0x7003, 0xc, 0x40, 0x0f, 4), + new NumberRange(0x7002, 0xd, 0x50, 0x0f, 4), + new NumberRange(0x7003, 0xc, 0x70, 0x0f, 4), + new NumberRange(0x7005, 0xa, 0x90, 0x1f, 4), + new NumberRange(0x7007, 0x8, 0xb0, 0x0f, 4), + new NumberRange(0x7002, 0xd, 0xc0, 0x0f, 4), + new NumberRange(0x7002, 0xd, 0xe0, 0x0f, 4), + new NumberRange(0x7000, 0xf, 0xf0, 0x0f, 4)); + private static final List RANGE_MONSTER_IA = Arrays.asList(new NumberRange(0x5b00, 0x4ff)); + private static final List RANGE_MONSTER_OLD = Arrays.asList(new NumberRange(0x7000, 0x1, 0x00, 0x1f, 4), + new NumberRange(0x7000, 0x3, 0x20, 0x0f, 4), + new NumberRange(0x7000, 0x2, 0x40, 0x0f, 4), + new NumberRange(0x7000, 0x1, 0x50, 0x0f, 4), + new NumberRange(0x7000, 0xf, 0x60, 0x0f, 4), + new NumberRange(0x7000, 0x2, 0x70, 0x0f, 4), + new NumberRange(0x7000, 0xf, 0x80, 0x0f, 4), + new NumberRange(0x7000, 0x4, 0x90, 0x1f, 4), + new NumberRange(0x7000, 0x6, 0xb0, 0x0f, 4), + new NumberRange(0x7000, 0x1, 0xc0, 0x0f, 4), + new NumberRange(0x7000, 0xf, 0xd0, 0x0f, 4), + new NumberRange(0x7000, 0x1, 0xe0, 0x0f, 4)); + private static final List RANGE_MONSTER_OLD_IA = Arrays.asList(new NumberRange(0x547a, 0x479)); + private static final List RANGE_MONSTER_LARGE = Arrays.asList(new NumberRange(0x9000, 0xfff)); + private static final List RANGE_MONSTER_LARGE_16 = Arrays.asList(new NumberRange(0xa000, 0xfff)); + private static final List RANGE_AMBIENT_STATIC = Arrays.asList(new NumberRange(0xb000, 0xfff)); + private static final List RANGE_AMBIENT = Arrays.asList(new NumberRange(0xc000, 0xfff)); + private static final List RANGE_FLYING = Arrays.asList(new NumberRange(0xd000, 0xfff)); + private static final List RANGE_MONSTER_ICEWIND = Arrays.asList(new NumberRange(0xe000, 0xfff)); + private static final List RANGE_MONSTER_ICEWIND_EX = Arrays.asList(new NumberRange(0xe000, 0x1fff)); + private static final List RANGE_MONSTER_ICEWIND_IA = Arrays.asList(new NumberRange(0x5000, 0x478)); + private static final List RANGE_MONSTER_PLANESCAPE = Arrays.asList(new NumberRange(0xf000, 0xfff)); + private static final List RANGE_MONSTER_PLANESCAPE_EX = Arrays.asList(new NumberRange(0x0000, 0xffff)); + + public enum Type { + /** Animation type: 0000 */ + EFFECT(0x0000, "effect", Arrays.asList( + Couple.with(TYPE_GAME_ALL, RANGE_EFFECT))), // type=0 + /** Animation type: 1000 (slots [1000..11ff]) */ + MONSTER_QUADRANT(0x1000, "monster_quadrant", Arrays.asList( + Couple.with(TYPE_GAME_ALL, RANGE_MONSTER_QUADRANT))), // type=1 + /** Animation type: 1000 (slots [1200..12ff], [1400..1fff] */ + MONSTER_MULTI(0x1000, "monster_multi", Arrays.asList( + Couple.with(TYPE_GAME_EE, RANGE_MONSTER_MULTI), + Couple.with(TYPE_GAME_BG2, RANGE_MONSTER_MULTI), + Couple.with(TYPE_GAME_IWD2, RANGE_MONSTER_MULTI))), // type=2 + /** Animation type: 1000 (slots [1300..13ff]) */ + MONSTER_MULTI_NEW(0x1000, "multi_new", Arrays.asList( + Couple.with(TYPE_GAME_EE, RANGE_MONSTER_MULTI_NEW), + Couple.with(TYPE_GAME_BG2_TOB, RANGE_MONSTER_MULTI_NEW))), // type=3 + /** Animation type: 8000 */ + MONSTER_LAYERED(0x8000, "monster_layered", Arrays.asList( + Couple.with(TYPE_GAME_ALL, RANGE_MONSTER_LAYERED))), // type=4 + /** Animation type: 2000 */ + MONSTER_LAYERED_SPELL(0x2000, "monster_layered_spell", Arrays.asList( + Couple.with(TYPE_GAME_ALL, RANGE_MONSTER_LAYERED_SPELL))), // type=5 + /** Animation type: 3000 */ + MONSTER_ANKHEG(0x3000, "monster_ankheg", Arrays.asList( + Couple.with(TYPE_GAME_ALL, RANGE_MONSTER_ANKHEG))), // type=6 + /** Animation type: 4000 */ + TOWN_STATIC(0x4000, "town_static", Arrays.asList( + Couple.with(TYPE_GAME_ALL, RANGE_TOWN_STATIC))), // type=7 + /** Animation types: 5000, 6000 (slots [5000..53ff], [5500..55ff], [6000..63ff], [6500..65ff]) */ + CHARACTER(new int[] {0x5000, 0x6000}, "character", Arrays.asList( + Couple.with(TYPE_GAME_EE, RANGE_CHARACTER), + Couple.with(TYPE_GAME_BG2, RANGE_CHARACTER), + Couple.with(TYPE_GAME_IWD_HOW, RANGE_CHARACTER), + Couple.with(TYPE_GAME_IWD2, RANGE_CHARACTER)), + RANGE_CHARACTER_IA), // type=8 + /** Animation types: 5000, 6000 (slots [5400..54ff], [5600..5fff], [6400..64ff], [6600..6fff]) */ + CHARACTER_OLD(new int[] {0x5000, 0x6000}, "character_old", Arrays.asList( + Couple.with(TYPE_GAME_ALL, RANGE_CHARACTER_OLD), + Couple.with(TYPE_GAME_BG1, RANGE_CHARACTER), + Couple.with(TYPE_GAME_IWD, RANGE_CHARACTER))), // type=9 + /** Animation type: 7000 (many subranges) */ + MONSTER(0x7000, "monster", Arrays.asList( + Couple.with(TYPE_GAME_EE, RANGE_MONSTER), + Couple.with(TYPE_GAME_BG2, RANGE_MONSTER), + Couple.with(TYPE_GAME_IWD, RANGE_MONSTER), + Couple.with(TYPE_GAME_IWD_HOW, RANGE_MONSTER), + Couple.with(TYPE_GAME_IWD2, RANGE_MONSTER)), + RANGE_MONSTER_IA), // type=10 + /** Animation type: 7000 (many subranges) */ + MONSTER_OLD(0x7000, "monster_old", Arrays.asList( + Couple.with(TYPE_GAME_ALL, RANGE_MONSTER_OLD)), + RANGE_MONSTER_OLD_IA), // type=11 + /** Animation type: 9000 */ + MONSTER_LARGE(0x9000, "monster_large", Arrays.asList( + Couple.with(TYPE_GAME_ALL, RANGE_MONSTER_LARGE))), // type=12 + /** Animation type: A000 */ + MONSTER_LARGE_16(0xa000, "monster_large16", Arrays.asList( + Couple.with(TYPE_GAME_ALL, RANGE_MONSTER_LARGE_16))), // type=13 + /** Animation type: B000 */ + AMBIENT_STATIC(0xb000, "ambient_static", Arrays.asList( + Couple.with(TYPE_GAME_ALL, RANGE_AMBIENT_STATIC))), // type=14 + /** Animation type: C000 */ + AMBIENT(0xc000, "ambient", Arrays.asList( + Couple.with(TYPE_GAME_ALL, RANGE_AMBIENT))), // type=15 + /** Animation type: D000 */ + FLYING(0xd000, "flying", Arrays.asList( + Couple.with(TYPE_GAME_ALL, RANGE_FLYING))), // type=16 + /** Animation type: E000 (for non-EE: also slots [f000..ffff]) */ + MONSTER_ICEWIND(0xe000, "monster_icewind", Arrays.asList( + Couple.with(TYPE_GAME_EE, RANGE_MONSTER_ICEWIND), + Couple.with(TYPE_GAME_BG2, RANGE_MONSTER_ICEWIND), + Couple.with(TYPE_GAME_IWD, RANGE_MONSTER_ICEWIND_EX), + Couple.with(TYPE_GAME_IWD_HOW, RANGE_MONSTER_ICEWIND_EX), + Couple.with(TYPE_GAME_IWD2, RANGE_MONSTER_ICEWIND_EX)), + RANGE_MONSTER_ICEWIND_IA), // type=17 + /** Animation type: F000 */ + MONSTER_PLANESCAPE(0xf000, "monster_planescape", Arrays.asList( + Couple.with(TYPE_GAME_PSTEE, RANGE_MONSTER_PLANESCAPE), + Couple.with(TYPE_GAME_PST, RANGE_MONSTER_PLANESCAPE_EX))), // type=18 + + /** Pseudo animation type: fallback option for non-existing animations */ + PLACEHOLDER(new int[] {0x0000, 0x1000, 0x2000, 0x3000, 0x4000, 0x5000, 0x6000, 0x7000, + 0x8000, 0x9000, 0xa000, 0xb000, 0xc000, 0xd000, 0xe000, 0xf000}, + "placeholder", + Arrays.asList(Couple.with(TYPE_GAME_ALL, Arrays.asList(new NumberRange(0x0000, 0xffff)))), + null); + + private final EnumMap> rangeMap = new EnumMap<>(Profile.Game.class); + private final List iaRanges; + private final int[] animationTypes; + private final String sectionName; + + /** + * @param type slot base range + * @param sectionName INI section name + * @param entries list of games and their associated slot ranges. + */ + private Type(int type, String sectionName, List, List>> entries) + throws IllegalArgumentException + { + this(new int[] {type}, sectionName, entries, null); + } + + /** + * @param type + * @param sectionName + * @param entries + * @param infinityAnimationRanges + * @throws IllegalArgumentException + */ + private Type(int type, String sectionName, List, List>> entries, + List infinityAnimationRanges) + throws IllegalArgumentException + { + this(new int[] {type}, sectionName, entries, infinityAnimationRanges); + } + + /** + * @param types + * @param sectionName + * @param entries + * @throws IllegalArgumentException + */ + private Type(int[] types, String sectionName, List, List>> entries) + throws IllegalArgumentException + { + this(types, sectionName, entries, null); + } + + /** + * @param type list of slot base ranges + * @param sectionName INI section name + * @param entries list of games and their associated slot ranges. + * @throws IllegalArgumentException + */ + private Type(int[] types, String sectionName, List, List>> entries, + List infinityAnimationRanges) throws IllegalArgumentException + { + try { + Misc.requireCondition(types != null && types.length > 0, "Type cannot be empty", IllegalArgumentException.class); + Misc.requireCondition(sectionName != null && !sectionName.isEmpty(), "Section name cannot be empty", IllegalArgumentException.class); + } catch (IllegalArgumentException iae) { + throw iae; + } catch (Exception e) { + } + this.animationTypes = types; + this.sectionName = sectionName; + for (final Couple, List> entry : entries) { + EnumSet games = entry.getValue0(); + List ranges = entry.getValue1(); + for (final Profile.Game game : games) { + if (ranges.size() > 0) { + List list = this.rangeMap.get(game); + if (list != null) { + list.addAll(ranges); + } else { + this.rangeMap.put(game, new ArrayList<>(ranges)); + } + } + } + } + this.iaRanges = infinityAnimationRanges; + } + + /** Returns the name for the type-specific INI section. */ + public String getSectionName() { return sectionName; } + + /** Returns the first available base animation type associated with the enum instance. */ + public int getType() { return animationTypes[0]; } + + /** Returns the number of defined base animation types associated with the enum instance. */ + public int getTypeCount() { return animationTypes.length; } + + /** Returns the specified base animation type associated with the enum instance. */ + public int getType(int idx) { return animationTypes[idx]; } + + /** + * Returns whether the specified value is covered by the ranges associated with the enum instance + * for the current game. + */ + public boolean contains(int value) { return contains(Profile.getGame(), value); } + + /** + * Returns whether the specified value is covered by the ranges associated with the enum instance + * for the specified game. + */ + public boolean contains(Profile.Game game, int value) + { + if (game == null) { + game = Profile.getGame(); + } + boolean retVal = false; + + // Infinity Animations sprite takes precedence over regular sprite + Type type = containsInfinityAnimations(value); + if (type != null && type != this) { + return retVal; + } + retVal = (type == this); + + if (!retVal) { + retVal = contains(value, rangeMap.get(Profile.getGame())); + } + return retVal; + } + + // Checks whether specified value is covered by the given ranges + private static boolean contains(int value, List ranges) + { + if (ranges != null) { + return ranges + .parallelStream() + .anyMatch(r -> r.contains(value)); + } + return false; + } + + /** + * Determines the {@code AnimationType} enum where a defined Infinity Animations (IA) range covers the specied value. + * @param value the value to check. + * @return {@code AnimationType} enum supporting the specified IA value. + * Returns {@code null} if value is not covered by any IA range. + */ + public static Type containsInfinityAnimations(int value) + { + Type retVal = null; + if (Profile.getProperty(Profile.Key.GET_INFINITY_ANIMATIONS) > 0) { + for (Type type : Type.values()) { + if (contains(value, type.iaRanges)) { + retVal = type; + break; + } + } + } + return retVal; + } + + /** + * Returns the {@code AnimationType} enum covering the specified animation id. + * @param animationId the animation id + * @return {@code AnimationType} enum that covers the specified animation id. Returns {@code null} otherwise. + */ + public static Type typeOfId(int animationId) + { + for (final Type type : values()) { + if (type.contains(animationId)) { + return type; + } + } + return null; + } + } + + + private AnimationInfo() + { + } +} diff --git a/src/org/infinity/resource/cre/decoder/util/CreatureInfo.java b/src/org/infinity/resource/cre/decoder/util/CreatureInfo.java new file mode 100644 index 000000000..f36bbf41e --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/util/CreatureInfo.java @@ -0,0 +1,1147 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.decoder.util; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Objects; +import java.util.function.Predicate; + +import org.infinity.datatype.Flag; +import org.infinity.datatype.IsNumeric; +import org.infinity.datatype.IsTextual; +import org.infinity.resource.AbstractStruct; +import org.infinity.resource.Effect; +import org.infinity.resource.Effect2; +import org.infinity.resource.Profile; +import org.infinity.resource.ResourceFactory; +import org.infinity.resource.StructEntry; +import org.infinity.resource.cre.CreResource; +import org.infinity.resource.cre.Item; +import org.infinity.resource.cre.decoder.MonsterPlanescapeDecoder; +import org.infinity.resource.cre.decoder.SpriteDecoder; +import org.infinity.resource.key.ResourceEntry; +import org.infinity.util.Misc; +import org.infinity.util.Table2da; +import org.infinity.util.Table2daCache; +import org.infinity.util.tuples.Couple; + +/** + * Provides useful information about a creature resource and their equipment. + */ +public class CreatureInfo +{ + /** Value to disable allegiance override. */ + public static final int OVERRIDE_NONE = -1; + + /** + * Identifies the equipment slot for an item. + * Entries in this enumeration are sorted by effect application order. + */ + public enum ItemSlots { + /** Slot for helmets and ioun stones. */ + HELMET, + /** Slot for armor or robes. */ + ARMOR, + /** Slot for shields or left-handed weapons. */ + SHIELD, + /** Slot for gauntlets and bracers. */ + GAUNTLETS, + /** Right slot for rings. */ + RING_RIGHT, + /** Left slot for rings. */ + RING_LEFT, + /** Slot for amulets or necklaces. */ + AMULET, + /** Slot for belts. */ + BELT, + /** Slot for boots. */ + BOOTS, + /** Slot for cloaks and mantles. */ + CLOAK, + /** Slot for the currently active weapon. */ + WEAPON, + } + + private final EnumMap equipment = new EnumMap<>(ItemSlots.class); + private final EffectInfo effectInfo; + private final SpriteDecoder decoder; + private final CreResource cre; + + private int allegianceOverride; + + public CreatureInfo(SpriteDecoder decoder, CreResource cre) throws Exception + { + this.effectInfo = new EffectInfo(); + this.decoder = Objects.requireNonNull(decoder, "SpriteDecoder instance cannot be null"); + this.cre = Objects.requireNonNull(cre, "CRE resource cannot be null"); + this.allegianceOverride = OVERRIDE_NONE; + init(); + } + + /** Returns the {@code SpriteDecoder} instance associated with the creature resource. */ + public SpriteDecoder getDecoder() { return decoder; } + + /** Returns the {@code CreResource} instance of the creature resource. */ + public CreResource getCreResource() { return cre; } + + /** Returns creature flags. */ + public int getFlags() { return ((IsNumeric)cre.getAttribute(CreResource.CRE_FLAGS)).getValue(); } + + /** Returns the creature status. */ + public int getStatus() { return ((IsNumeric)cre.getAttribute(CreResource.CRE_STATUS)).getValue(); } + + /** Returns whether the creature is panicked. */ + public boolean isStatusPanic() + { + return ((getStatus() & (1 << 2)) != 0); + } + + /** + * Returns {@code true} if the creature is under the stoneskin effect or status is set to petrification/stone death. + */ + public boolean isStoneEffect() + { + return ((getStatus() & (1 << 7)) != 0) || isEffectActive(SegmentDef.SpriteType.AVATAR, EffectInfo.FILTER_STONE_EFFECT); + } + + /** Returns {@code true} if the creature status is set to "frozen death". */ + public boolean isFrozenEffect() + { + return ((getStatus() & (1 << 6)) != 0); + } + + /** Returns {@code true} if the creature status is set to "flame death". */ + public boolean isBurnedEffect() + { + return ((getStatus() & (1 << 9)) != 0); + } + + /** Returns {@code true} if the creature is under the effect of blur. */ + public boolean isBlurEffect() + { + return isEffectActive(SegmentDef.SpriteType.AVATAR, EffectInfo.FILTER_BLUR_EFFECT); + } + + /** Returns the creature animation id. */ + public int getAnimationId() { return ((IsNumeric)cre.getAttribute(CreResource.CRE_ANIMATION)).getValue(); } + + /** + * Returns the translucency strength of the creature. Values can range from 0 (fully opaque) to 255 (fully transparent). + * The method takes creature animation attributes and (EE only) creature attributes into account. + */ + public int getTranslucency() + { + int retVal = 0; + if (decoder.getAttribute(SpriteDecoder.KEY_TRANSLUCENT)) { // avoid circular dependencies + retVal = 128; + } + if (Profile.isEnhancedEdition()) { + int v = ((IsNumeric)cre.getAttribute(CreResource.CRE_TRANSLUCENCY)).getValue(); + if (v > 0) { + retVal = 255 - Math.min(v, 255); + } + } + return retVal; + } + + /** + * Returns the translucency strength of the creature after evaluating all potential sources of translucency, + * which includes creature animation properties, creature properties (EE only) and item effects. + * Values can range from 0 (fully opaque) to 255 (fully transparent). + */ + public int getEffectiveTranslucency() + { + int retVal = -1; + + // getting translucency from item effect + for (final ItemSlots slot : ItemSlots.values()) { + ItemInfo info = getItemInfo(slot); + if (info != null) { + EffectInfo.Effect fx = info.getEffectInfo().getFirstEffect(this, SegmentDef.SpriteType.AVATAR, EffectInfo.FILTER_TRANSLUCENCY_EFFECT); + if (fx != null && fx.getParameter2() == 0) { + int amount = Math.max(0, Math.min(255, fx.getParameter1())); + retVal = Math.max(retVal, 255 - amount); + } + } + } + + // getting translucency from creature effect + EffectInfo.Effect fx = getEffectInfo().getFirstEffect(this, SegmentDef.SpriteType.AVATAR, EffectInfo.FILTER_TRANSLUCENCY_EFFECT); + if (fx != null && fx.getParameter2() == 0) { + int amount = Math.max(0, Math.min(255, fx.getParameter1())); + retVal = Math.max(retVal, 255 - amount); + } + + if (retVal < 0) { + // falling back to default translucency + retVal = getTranslucency(); + } + + return retVal; + } + + /** + * Returns the (average or highest) class level of the creature. Returns 0 if level could not be determined. + * @param highestOnly specify {@code false} to determine the average level of all (active and inactive) classes. + * Specify {@code true} to return the highest level of all (active and inactive) classes. + */ + public int getClassLevel(boolean highestOnly) + { + int retVal = 0; + int ofsBase = cre.getExtraOffset(); + + String creVersion = getCreatureVersion(); + if ("V2.2".equals(creVersion)) { + // IWD2 + int level = 0; + int maxLevel = 0; + int numClasses = 0; + for (int i = 0; i < 11; i++) { + int v = ((IsNumeric)cre.getAttribute(ofsBase + 0x8b + i)).getValue(); + if (v > 0) { + level += v; + maxLevel = Math.max(maxLevel, v); + numClasses++; + } + } + if (highestOnly) { + retVal = maxLevel; + } else { + retVal = level; + if (numClasses > 0) { + retVal /= numClasses; + } + } + } else { + // non-IWD2 + int cls = ((IsNumeric)cre.getAttribute(CreResource.CRE_CLASS)).getValue(); + int numClasses; + switch (cls) { + case 7: + case 8: + case 9: + case 13: + case 14: + case 15: + case 16: + case 18: + numClasses = 2; + break; + case 10: + case 17: + numClasses =3; + break; + default: + numClasses = 1; + } + int level = 0; + int maxLevel = 0; + for (int i = 0; i < numClasses; i++) { + int v = ((IsNumeric)cre.getAttribute(ofsBase + 0x234 + i)).getValue(); + level += v; + maxLevel = Math.max(maxLevel, v); + } + if (highestOnly) { + retVal = maxLevel; + } else { + retVal = level / numClasses; + } + } + + return retVal; + } + + /** Returns the allegiance of the creature. May return overridden allegiance if set. */ + public int getAllegiance() + { + return getAllegiance(true); + } + + /** + * Returns the allegiance of the creature. + * @param allowOverride whether allegiance override of the parent sprite decoder is considered. + * @return the (overridden) allegiance value of the creature. + */ + public int getAllegiance(boolean allowOverride) + { + int retVal = OVERRIDE_NONE; + if (allowOverride) { + retVal = getAllegianceOverride(); + } + if (retVal == OVERRIDE_NONE) { + retVal = ((IsNumeric)cre.getAttribute(CreResource.CRE_ALLEGIANCE)).getValue(); + } + return retVal; + } + + /** Returns the overridden allegiance. Returns {@link #OVERRIDE_NONE} if allegiance has not been overridden. */ + public int getAllegianceOverride() + { + return allegianceOverride; + } + + /** + * Overrides the creature's allegiance. + * @param allegiance new allegiance of the creature. Uses the same values as defined in EA.IDS. + * Specify {@link #OVERRIDE_NONE} to disable. + */ + public void setAllegianceOverride(int allegiance) + { + allegiance = Math.max(OVERRIDE_NONE, Math.min(255, allegiance)); + if (allegianceOverride != allegiance) { + allegianceOverride = allegiance; + decoder.allegianceChanged(); + } + } + + /** + * Returns the ability index of the currently selected weapon. + * Returns -1 if no weapon (not even fist) is available or could not be determined. + */ + public int getSelectedWeaponAbility() + { + int retVal = -1; + + ItemInfo itemInfo = equipment.get(ItemSlots.WEAPON); + if (itemInfo != null) { + int numAbils = itemInfo.getAbilityCount(); + int idxAbil = ((IsNumeric)cre.getAttribute(CreResource.CRE_SELECTED_WEAPON_ABILITY)).getValue(); + retVal = Math.max(0, Math.min(numAbils - 1, idxAbil)); + } + return retVal; + } + + /** + * Returns the active weapon of the specified creature. + * @return The {@code ItemInfo} object for the item resource of the active weapon. + * Returns {@code null} if no weapon is active. + */ + public ItemInfo getEquippedWeapon() + { + return getItemInfo(ItemSlots.WEAPON); + } + + /** + * Returns the equipped helmet of the specified creature. + * @return The {@code ItemInfo} object for the item resource of the helmet. + * Returns {@code null} if no helmet is equipped. + */ + public ItemInfo getEquippedHelmet() + { + return getItemInfo(ItemSlots.HELMET); + } + + /** + * Returns the equipped shield or left-handed weapon of the specified creature. + * @return The {@code ItemInfo} object for the item resource of the shield or left-handed weapon. + * Returns {@code null} if left hand is empty. + */ + public ItemInfo getEquippedShield() + { + return getItemInfo(ItemSlots.SHIELD); + } + + /** + * Returns the equipped armor or robe. + * @return The {@code ItemInfo} object for the item resource of armor or robe. + * Returns {@code null} if no armor is equipped. + */ + public ItemInfo getEquippedArmor() + { + return getItemInfo(ItemSlots.ARMOR); + } + + /** + * Returns the {@link ItemInfo} instance associated with the specified item slot. + * @param slot the slot where the item is equipped. + * @return {@code ItemInfo} instance of the item, {@code null} if no item equipped. + */ + public ItemInfo getItemInfo(ItemSlots slot) + { + if (slot == null) { + return null; + } + return equipment.get(slot); + } + + /** + * Returns a list of equipped items in the order of effect application. + * @return List of {@code ItemInfo} instances in the order of effect application. + */ + public ItemInfo[] getEffectiveItemInfo() + { + ArrayList items = new ArrayList<>(); + + for (final ItemSlots slot : ItemSlots.values()) { + ItemInfo info = equipment.get(slot); + if (info != null) { + items.add(info); + } + } + + return items.toArray(new ItemInfo[items.size()]); + } + + /** Provides access to the {@link EffectInfo} instance which manages effects attached to the current creature. */ + public EffectInfo getEffectInfo() { return effectInfo; } + + /** + * Returns the number of defined color entries for the creature. + * Number can vary for PST or PST:EE creatures. Otherwise it always returns 7. + */ + public int getColorCount() + { + int retVal = 7; + String creVersion = getCreatureVersion(); + if ("V1.1".equals(creVersion) || "V1.2".equals(creVersion)) { + if (decoder instanceof MonsterPlanescapeDecoder && decoder.isFalseColor()) { + retVal = ((MonsterPlanescapeDecoder)decoder).getColorLocationCount(); + } else { + retVal = 0; + } + } else if (Profile.getGame() == Profile.Game.PSTEE) { + if (decoder instanceof MonsterPlanescapeDecoder) { + retVal = ((MonsterPlanescapeDecoder)decoder).getColorLocationCount(); + } + } + return retVal; + } + + /** + * Returns the number of defined color entries for the specified sprite overlay type. + * @param type the sprite overlay type. + * @return number of defined color entries for the sprite overlay type. + */ + public int getColorCount(SegmentDef.SpriteType type) + { + int retVal = 0; + + if (type == null) { + type = SegmentDef.SpriteType.AVATAR; + } + + if (type == SegmentDef.SpriteType.AVATAR) { + return getColorCount(); + } + + ItemInfo itemInfo = null; + switch (type) { + case HELMET: + itemInfo = equipment.get(ItemSlots.HELMET); + break; + case SHIELD: + itemInfo = equipment.get(ItemSlots.SHIELD); + break; + case WEAPON: + itemInfo = equipment.get(ItemSlots.WEAPON); + break; + default: + } + + if (itemInfo != null) { + HashSet set = new HashSet<>(); + List fxList = itemInfo.getEffectInfo().getEffects(this, type, EffectInfo.FILTER_COLOR_EFFECT); + for (final EffectInfo.Effect fx : fxList) { + set.add(fx.getParameter2() & 0xf); + } + retVal = set.size(); + } + + return retVal; + } + + /** + * Returns the color entry of the specified location index as defined by the creature resource. + * Color locations range from 0 to 6. For PST and PST:EE range can differ depending on creature animation. + * Returns -1 if color entry is not available. + */ + public int getColorValue(int locationIndex) + { + int retVal = -1; + if (locationIndex >= 0 && locationIndex < getColorCount()) { + String creVersion = getCreatureVersion(); + if ("V1.1".equals(creVersion) || "V1.2".equals(creVersion)) { + retVal = ((IsNumeric)cre.getAttribute(String.format(CreResource.CRE_COLOR_FMT, locationIndex + 1))).getValue(); + } else { + int ofsBase = cre.getExtraOffset(); + retVal = ((IsNumeric)cre.getAttribute(ofsBase + 0x2c + locationIndex)).getValue(); + } + } + return retVal; + } + + /** + * Returns the color entry of the specified location index for the specified sprite overlay type. + * @param type the sprite overlay type. + * @param locationIndex the color location index. + * @return color entry index. Returns -1 if color entry is not available. + */ + public int getColorValue(SegmentDef.SpriteType type, int locationIndex) + { + int retVal = -1; + + if (type == null) { + type = SegmentDef.SpriteType.AVATAR; + } + + if (type == SegmentDef.SpriteType.AVATAR) { + return getColorValue(locationIndex); + } + + ItemInfo itemInfo = null; + switch (type) { + case HELMET: + itemInfo = equipment.get(ItemSlots.HELMET); + break; + case SHIELD: + itemInfo = equipment.get(ItemSlots.SHIELD); + break; + case WEAPON: + itemInfo = equipment.get(ItemSlots.WEAPON); + break; + default: + } + + if (itemInfo != null) { + EffectInfo.Effect fx = itemInfo.getEffectInfo().getColorByLocation(this, type, EffectInfo.OPCODE_SET_COLOR, locationIndex); + if (fx != null) { + retVal = fx.getParameter1(); + } + } + + return retVal; + } + + /** + * Returns the color entry of the specified location index for the avatar sprite + * after applying all equipment and effect colors as well as the source of the color value. + * @param opcode filter by this opcode. + * @param locationIndex the color location index. Available range: [-1, 6] + * @return a tuple with the color entry as well as a {@code Boolean} value indicating whether random colors are allowed. + * Returns {@code -1} for the color entry if value could not be determined. + */ + public Couple getEffectiveColorValue(int locationIndex) + { + return getEffectiveColorValue(SegmentDef.SpriteType.AVATAR, locationIndex); + } + + /** + * Returns the color entry of the location index for the specified sprite overlay type + * after applying all equipment and effect colors as well as the source of the color value. + * @param type the {@link SegmentDef.SpriteType SpriteType} target + * @param locationIndex the color location index. Available range: [-1, 6] + * @return a tuple with the color entry as well as a {@code Boolean} value indicating whether random colors are allowed. + * Returns {@code -1} for the color entry if value could not be determined. + */ + public Couple getEffectiveColorValue(SegmentDef.SpriteType type, int locationIndex) + { + Couple retVal = Couple.with(-1, true); + + // using creature color by default + int value = getColorValue(SegmentDef.SpriteType.AVATAR, locationIndex); + if (value >= 0) { + retVal.setValue0(value); + retVal.setValue1(Boolean.TRUE); + } + + if (type == null) { + type = SegmentDef.SpriteType.AVATAR; + } + + // checking equipped items + ItemInfo[] itemInfos = getEffectiveItemInfo(); + for (final ItemInfo info : itemInfos) { + EffectInfo.Effect fx = info.getEffectInfo().getColorByLocation(this, type, EffectInfo.OPCODE_SET_COLOR, locationIndex); + if (fx != null) { + retVal.setValue0(fx.getParameter1()); + retVal.setValue1(Boolean.FALSE); + } + } + + // checking creature effects + EffectInfo.Effect fx = getEffectInfo().getColorByLocation(this, type, EffectInfo.OPCODE_SET_COLOR, locationIndex); + if (fx != null) { + retVal.setValue0(fx.getParameter1()); + retVal.setValue1(Boolean.FALSE); + } + + return retVal; + } + + /** + * Returns the color value of the specified location index for the specified sprite overlay type. + * @param type the sprite overlay type. + * @param opcode the opcode to filter. + * @param locationIndex the color location index. + * @return color entry index. Returns -1 if color value is not available. + */ + public int getTintValue(SegmentDef.SpriteType type, int opcode, int locationIndex) + { + if (type == null) { + type = SegmentDef.SpriteType.AVATAR; + } + + EffectInfo.Effect fx = getEffectInfo().getColorByLocation(this, type, opcode, locationIndex); + return (fx != null) ? EffectInfo.swapBytes(fx.getParameter1()) : -1; + } + + /** + * Returns the color tint value of the location index for the specified sprite overlay type + * after applying all equipment and effect colors as well as the source of the color value. + * @param type the {@link SegmentDef.SpriteType SpriteType} target + * @param locationIndex the color location index. Available range: [-1, 6] + * @return a tuple with the effect opcode as well as the RGB color value. + * Returns {@code -1} for each of the tuple elements if value could not be determined. + */ + public Couple getEffectiveTintValue(SegmentDef.SpriteType type, int locationIndex) + { + Couple retVal = Couple.with(-1, -1); + + if (type == null) { + type = SegmentDef.SpriteType.AVATAR; + } + + final int[] opcodes = {EffectInfo.OPCODE_TINT_BRIGHT, EffectInfo.OPCODE_TINT_SOLID, EffectInfo.OPCODE_SET_COLOR_GLOW}; + + // checking equipped items + int opcode = -1, value = -1; + ItemInfo[] itemInfos = getEffectiveItemInfo(); + for (final ItemInfo info : itemInfos) { + for (final int code : opcodes) { + final EffectInfo.Effect fx = info.getEffectInfo().getColorByLocation(this, type, code, locationIndex); + if (fx != null) { + opcode = code; + value = EffectInfo.swapBytes(fx.getParameter1()); + break; + } + } + if (value != -1) { + retVal.setValue0(opcode); + retVal.setValue1(value); + } + } + + // checking creature effects + opcode = value = -1; + for (final int code : opcodes) { + final EffectInfo.Effect fx = getEffectInfo().getColorByLocation(this, type, code, locationIndex); + if (fx != null) { + opcode = code; + value = EffectInfo.swapBytes(fx.getParameter1()); + break; + } + } + if (value != -1) { + retVal.setValue0(opcode); + retVal.setValue1(value); + } + + return retVal; + } + + /** + * Returns whether the specified effect is active. + * @param type the {@link SegmentDef.SpriteType SpriteType} target. + * @param opcode the effect opcode to filter. + * @return {@code true} if the effect with matching parameters exists. Returns {@code false} otherwise. + */ + public boolean isEffectActive(SegmentDef.SpriteType type, int opcode) + { + return isEffectActive(type, (fx) -> fx.getOpcode() == opcode); + } + + /** + * Returns whether the specified effect is active with the given parameters. + * @param type the {@link SegmentDef.SpriteType SpriteType} target + * @param pred the predicate to filter. + * @return {@code true} if the effect with matching parameters exists. Returns {@code false} otherwise. + */ + public boolean isEffectActive(SegmentDef.SpriteType type, Predicate pred) + { + boolean retVal = false; + + // checking creature effects + EffectInfo.Effect fx = getEffectInfo().getFirstEffect(this, type, pred); + retVal = (fx != null); + + if (!retVal) { + // checking equipped items + ItemInfo[] itemInfos = getEffectiveItemInfo(); + for (final ItemInfo info : itemInfos) { + fx = info.getEffectInfo().getFirstEffect(this, type, pred); + if (fx != null) { + retVal = true; + break; + } + } + } + + return retVal; + } + + /** Returns the creature resource version. */ + private String getCreatureVersion() + { + String sig = ((IsTextual)cre.getAttribute(CreResource.COMMON_SIGNATURE)).getText().toUpperCase(Locale.ENGLISH); + String versionLabel = sig.equals("CHR ") ? CreResource.CHR_VERSION_2 : CreResource.COMMON_VERSION; + return ((IsTextual)cre.getAttribute(versionLabel)).getText().toUpperCase(Locale.ENGLISH); + } + + private void init() throws Exception + { + // initialize attributes + initCommon(); + + switch (getCreatureVersion()) { + case "V1.0": + initV10(); + break; + case "V1.1": // non-standard PST format + case "V1.2": + initV12(); + break; + case "V2.2": + initV22(); + break; + case "V9.0": + case "V9.1": + initV90(); + break; + default: + throw new Exception("Unsupported creature resource: " + cre.getResourceEntry().getResourceName()); + } + } + + // initialize common section of creature resources + private void initCommon() throws Exception + { + // collecting opcode 7 effects + initEffects(); + } + + // initialize BG/EE-style creature resources + private void initV10() throws Exception + { + // initializing equipment + HashMap slotMap = new HashMap() {{ + put(ItemSlots.HELMET, 0); + put(ItemSlots.ARMOR, 1); + put(ItemSlots.SHIELD, 2); + put(ItemSlots.GAUNTLETS, 3); + put(ItemSlots.RING_LEFT, 4); + put(ItemSlots.RING_RIGHT, 5); + put(ItemSlots.AMULET, 6); + put(ItemSlots.BELT, 7); + put(ItemSlots.BOOTS, 8); + put(ItemSlots.WEAPON, 9); + put(ItemSlots.CLOAK, 17); + }}; + + initEquipment(slotMap); + } + + // initialize PST-style creature resources + private void initV12() throws Exception + { + // initializing equipment + HashMap slotMap = new HashMap() {{ + put(ItemSlots.HELMET, 0); + put(ItemSlots.ARMOR, 1); + put(ItemSlots.SHIELD, 2); + put(ItemSlots.GAUNTLETS, 3); + put(ItemSlots.RING_LEFT, 4); + put(ItemSlots.RING_RIGHT, 5); + put(ItemSlots.AMULET, 6); + put(ItemSlots.BELT, 7); + put(ItemSlots.BOOTS, 8); + put(ItemSlots.WEAPON, 9); + put(ItemSlots.CLOAK, 19); + }}; + + initEquipment(slotMap); + } + + // initialize IWD2-style creature resources + private void initV22() throws Exception + { + // initializing equipment + HashMap slotMap = new HashMap() {{ + put(ItemSlots.HELMET, 0); + put(ItemSlots.ARMOR, 1); + put(ItemSlots.GAUNTLETS, 3); + put(ItemSlots.RING_LEFT, 4); + put(ItemSlots.RING_RIGHT, 5); + put(ItemSlots.AMULET, 6); + put(ItemSlots.BELT, 7); + put(ItemSlots.BOOTS, 8); + put(ItemSlots.WEAPON, 9); + put(ItemSlots.SHIELD, 10); + put(ItemSlots.CLOAK, 21); + }}; + + initEquipment(slotMap); + } + + // initialize IWD-style creature resources + private void initV90() throws Exception + { + // initializing equipment + HashMap slotMap = new HashMap() {{ + put(ItemSlots.HELMET, 0); + put(ItemSlots.ARMOR, 1); + put(ItemSlots.SHIELD, 2); + put(ItemSlots.GAUNTLETS, 3); + put(ItemSlots.RING_LEFT, 4); + put(ItemSlots.RING_RIGHT, 5); + put(ItemSlots.AMULET, 6); + put(ItemSlots.BELT, 7); + put(ItemSlots.BOOTS, 8); + put(ItemSlots.WEAPON, 9); + put(ItemSlots.CLOAK, 17); + }}; + + initEquipment(slotMap); + } + + private void initEffects() + { + int fxType = ((IsNumeric)cre.getAttribute(CreResource.CRE_EFFECT_VERSION)).getValue(); + Class fxClass = (fxType == 1) ? Effect2.class : Effect.class; + List fxList= cre.getFields(fxClass); + if (fxList != null) { + for (final StructEntry se : fxList) { + if (se instanceof AbstractStruct) { + initEffect((AbstractStruct)se); + } + } + } + } + + private void initEffect(AbstractStruct as) + { + if (as instanceof Effect) { + getEffectInfo().add(new EffectInfo.Effect((Effect)as)); + } else if (as instanceof Effect2) { + getEffectInfo().add(new EffectInfo.Effect((Effect2)as)); + } + } + + private void initEquipment(HashMap slotMap) + { + List itemList = cre.getFields(Item.class); + int ofsSlots = cre.getExtraOffset() + ((IsNumeric)cre.getAttribute(CreResource.CRE_OFFSET_ITEM_SLOTS)).getValue(); + for (HashMap.Entry entry : slotMap.entrySet()) { + ItemSlots slot = entry.getKey(); + int slotIdx = entry.getValue().intValue(); + int itemIdx = -1; + if (slot == ItemSlots.WEAPON) { + // special: determine active weapon slot + int selectedWeaponSlot = ((IsNumeric)cre.getAttribute(CreResource.CRE_SELECTED_WEAPON_SLOT)).getValue(); + int weaponIdx = getWeaponSlotIndex(selectedWeaponSlot); + if (weaponIdx == 1000) { + // selected weapon: fists + itemIdx = weaponIdx; + } else if (weaponIdx >= 0) { + weaponIdx = getEffectiveWeaponIndex(weaponIdx); + if (weaponIdx >= 0) { + itemIdx = ((IsNumeric)cre.getAttribute(ofsSlots + weaponIdx * 2)).getValue(); + } + } + } else { + itemIdx = ((IsNumeric)cre.getAttribute(ofsSlots + slotIdx * 2)).getValue(); + } + + initEquipmentItem(slot, itemIdx, itemList); + } + + // Make sure that shield slot is empty when two-handed weapon is equipped + if (getEquippedWeapon() != null) { + if (ItemInfo.FILTER_WEAPON_2H.test(getEquippedWeapon())) { + unsetItemSlot(ItemSlots.SHIELD); + } + } + } + + private void initEquipmentItem(ItemSlots slot, int itemIndex, List itemList) + { + ResourceEntry itmEntry = null; + boolean isUndroppable = false; + if (itemIndex == 1000) { + // weapon: fists + itmEntry = getFistWeapon(); + } else if (itemIndex >= 0 && itemIndex < itemList.size()) { + if (itemList.get(itemIndex) instanceof Item) { + Item itm = (Item)itemList.get(itemIndex); + String itmResref = ((IsTextual)itm.getAttribute(Item.CRE_ITEM_RESREF)).getText(); + itmEntry = ResourceFactory.getResourceEntry(itmResref + ".ITM"); + isUndroppable = ((Flag)itm.getAttribute(Item.CRE_ITEM_FLAGS)).isFlagSet(3); + } + } + + if (itmEntry != null) { + try { + ItemInfo itemInfo = ItemInfo.get(itmEntry); + itemInfo.overrideDroppableFlag(isUndroppable); + equipment.put(slot, itemInfo); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + + // Removes any equipment set in the specified item slot. Returns the removed equipment. + private ItemInfo unsetItemSlot(ItemSlots slot) + { + ItemInfo retVal = null; + if (slot != null) { + retVal = equipment.remove(slot); + } + return retVal; + } + + private ResourceEntry getFistWeapon() + { + ResourceEntry retVal = null; + + if ((getAnimationId() & 0xff00) == 0x6500) { + // hardcoded: monk animation activates special fists + int level = getClassLevel(false); + + // try 2DA table first + Table2da table = Table2daCache.get("MONKFIST.2DA"); + if (table != null) { + level = Math.max(1, Math.min(table.getRowCount(), level)); + String resref = table.get(level, 1); + retVal = ResourceFactory.getResourceEntry(resref + ".ITM"); + } + + // 2. fall back to hardcoded fists + if (retVal == null) { + int[] minLevels = { Integer.MIN_VALUE, 1, 3, 6, 9, 12, 15, 19, 25, Integer.MAX_VALUE }; + for (int i = 0; i < minLevels.length && retVal == null; i++) { + if (level < minLevels[i]) { + retVal = ResourceFactory.getResourceEntry("MFIST" + i + ".ITM"); + } + } + } + } + + // default fists + if (retVal == null) { + retVal = ResourceFactory.getResourceEntry("FIST.ITM"); + } + + return retVal; + } + + /** + * Analyses the item at the specified slot index and returns the same index if the item can be used directly. + * If the item requires a launcher the method scans the weapon slots of the specified CRE resource and + * returns the slot index of a matching launcher item. + * @param slotIndex the absolute slot index of the item to check + * @return the absolute slot index of the effective weapon. Returns {@code -1} if no weapon could be determined. + */ + private int getEffectiveWeaponIndex(int slotIndex) + { + int retVal = -1; + if (slotIndex < 0) { + return retVal; + } + + // getting item entry index + int ofsSlots = cre.getExtraOffset() + ((IsNumeric)cre.getAttribute(CreResource.CRE_OFFSET_ITEM_SLOTS)).getValue(); + int itmIndex = ((IsNumeric)cre.getAttribute(ofsSlots + slotIndex * 2)).getValue(); + int numItems = ((IsNumeric)cre.getAttribute(CreResource.CRE_NUM_ITEMS)).getValue(); + if (itmIndex < 0 || itmIndex >= numItems) { + return retVal; + } + + // loading referenced item + ItemInfo info = null; + int ofsItems = Objects.requireNonNull(cre).getExtraOffset() + ((IsNumeric)cre.getAttribute(CreResource.CRE_OFFSET_ITEMS)).getValue(); + try { + String itmResref = ((IsTextual)cre.getAttribute(ofsItems + itmIndex * 20, true)).getText(); + info = ItemInfo.get(ResourceFactory.getResourceEntry(itmResref + ".ITM")); + } catch (Exception e) { + return retVal; + } + + // check if item requires a launcher + int abilityIndex = ((IsNumeric)cre.getAttribute(CreResource.CRE_SELECTED_WEAPON_ABILITY)).getValue(); + abilityIndex = Math.max(0, abilityIndex); + int numAbil = info.getAbilityCount(); + abilityIndex = Math.min(abilityIndex, numAbil - 1); + if (abilityIndex < 0) { + return retVal; + } + int launcherType = info.getAbility(abilityIndex).getLauncher(); + if (launcherType == 0) { + // item can be used directly + retVal = slotIndex; + } + + if (retVal < 0) { + // launcher required: find a weapon in weapon slots 1-4 with a matching item category + String creVer = getCreatureVersion(); + int idxWeaponSlots = 9; + int slotGroupSize = creVer.equals("V2.2") ? 2 : 1; // IWD2 uses weapon/shield pairs + for (int i = 0; i < 4; i++) { + int ofs = ofsSlots + (idxWeaponSlots + i * slotGroupSize) * 2; + itmIndex = ((IsNumeric)cre.getAttribute(ofs)).getValue(); + if (itmIndex >= 0 && itmIndex < numItems) { + int cat = -1; + try { + String itmResref = ((IsTextual)cre.getAttribute(ofsItems + itmIndex * 20, true)).getText(); + ResourceEntry itmEntry = ResourceFactory.getResourceEntry(itmResref + ".ITM"); + if (itmEntry != null) { + try (InputStream is = itmEntry.getResourceDataAsStream()) { + Misc.requireCondition(is.skip(0x1c) == 0x1c, "Could not read item category", IOException.class); + cat = is.read(); + cat |= is.read() << 8; + } + } + // checking if launcher type corresponds with item category + if (launcherType == 1 && cat == 15 || // Bow + launcherType == 2 && cat == 27 || // Crossbow + launcherType == 3 && cat == 18) { // Sling + retVal = idxWeaponSlots + i * slotGroupSize; + break; + } + } catch (Exception e) { + } + } + } + } + + return retVal; + } + + /** + * Determines the absolute item slot index of the specified weapon-related slot id. + * Special slots that cannot be mapped to slot indices are returned as -1. + * Only weapon-related slots are considered, which includes the actual weapon slot ids as well as the ammo ids. + * @param slotId the slot id (as defined in SLOTS.IDS) + * @return absolute item slot index. Returns 1000 for fist slot. Returns -1 if slot id could not be mapped to a slot index. + */ + private int getWeaponSlotIndex(int slotId) + { + int retVal = -1; + if (slotId == 1000) { + // fist weapon + return slotId; + } + + String creVer = getCreatureVersion(); + slotId += 35; // determine SLOTS.IDS value + switch (slotId) { + case 3: // IWD2: ammo1 + case 4: // IWD2: ammo2 + case 5: // IWD2: ammo3 + case 6: // IWD2: ammo4 + if (creVer.equals("V2.2")) { + retVal = slotId + 14; + } + break; + case 11: // ammo1 + case 12: // ammo2 + case 13: // ammo3 + case 14: // ammo4 + if (!creVer.equals("V2.2")) { + retVal = slotId + 2; + } + break; + case 15: + case 16: + if (creVer.equals("V1.2")) { + retVal = slotId + 2; + } + break; + case 34: // magically created weapon + { + switch (creVer) { + case "V1.2": + retVal = slotId + 11; + break; + case "V2.2": + retVal = slotId + 15; + break; + default: + { + if (Profile.getGame() == Profile.Game.PSTEE) { + // special: PSTEE party members have customized item slots + int numSlots = ((IsNumeric)cre.getAttribute(CreResource.CRE_NUM_ITEM_SLOTS)).getValue(); + if (numSlots > 0) { + Table2da table = Table2daCache.get("ITMSLOTS.2DA"); + if (table != null) { + for (int row = 0, rowCount = table.getRowCount(); row < rowCount; row++) { + if (Misc.toNumber(table.get(row, 1), -1) == 13) { // magic weapon slot? + retVal = Misc.toNumber(table.get(row, 5), -1); + } + } + } + } + } + if (retVal < 0) { + retVal = slotId + 3; + } + } + } + break; + } + case 35: // weapon1 + case 36: // weapon2 (IWD2: shield1) + case 37: // weapon3 (IWD2: weapon2) + case 38: // weapon4 (IWD2: shield2) + case 39: // IWD2: weapon3 + case 40: // IWD2: shield3 + case 41: // IWD2: weapon4 + case 42: // IWD2: shield4 + retVal = slotId - 26; + break; + } + return retVal; + } + + @Override + public int hashCode() + { + int hash = 7; + hash = 31 * hash + ((equipment == null) ? 0 : equipment.hashCode()); + hash = 31 * hash + ((effectInfo == null) ? 0 : effectInfo.hashCode()); + hash = 31 * hash + ((decoder == null) ? 0 : decoder.hashCode()); + hash = 31 * hash + ((cre == null) ? 0 : cre.hashCode()); + hash = 31 * hash + Integer.valueOf(allegianceOverride).hashCode(); + return hash; + } + + @Override + public boolean equals(Object o) + { + if (o == this) { + return true; + } + if (!(o instanceof CreatureInfo)) { + return false; + } + CreatureInfo other = (CreatureInfo)o; + boolean retVal = (this.equipment == null && other.equipment == null) || + (this.equipment != null && this.equipment.equals(other.equipment)); + retVal &= (this.effectInfo == null && other.effectInfo == null) || + (this.effectInfo != null && this.effectInfo.equals(other.effectInfo)); + retVal &= (this.decoder == null && other.decoder == null) || + (this.decoder != null && this.decoder.equals(other.decoder)); + retVal &= (this.cre == null && other.cre == null) || + (this.cre != null && this.cre.equals(other.cre)); + retVal &= (this.allegianceOverride == other.allegianceOverride); + return retVal; + } +} diff --git a/src/org/infinity/resource/cre/decoder/util/CycleDef.java b/src/org/infinity/resource/cre/decoder/util/CycleDef.java new file mode 100644 index 000000000..2043ea9b8 --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/util/CycleDef.java @@ -0,0 +1,210 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.decoder.util; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; + +import org.infinity.resource.cre.decoder.util.SegmentDef.Behavior; +import org.infinity.resource.cre.decoder.util.SegmentDef.SpriteType; +import org.infinity.resource.key.ResourceEntry; + +/** + * Definition of a single cycle for a specific direction in an animation sequence. + */ +public class CycleDef implements Cloneable +{ + private final ArrayList cycles = new ArrayList<>(); + + private DirDef parent; + + /** + * Creates a new independent cycle definition with the specified parameters. + * Sprite type is assumed to be {@link SpriteType#AVATAR}. Behavior is assumed to be {@link Behavior#REPEAT}. + * @param bamResource the BAM resource + * @param cycle the BAM cycle index + */ + public CycleDef(ResourceEntry bamResource, int cycle) + { + this(null, bamResource, cycle, null, null); + } + + /** + * Creates a new independent cycle definition with the specified parameters. + * Behavior is assumed to be {@link Behavior#REPEAT}. + * @param bamResource the BAM resource + * @param cycle the BAM cycle index + * @param type the sprite type of the specified segment. + */ + public CycleDef(ResourceEntry bamResource, int cycle, SegmentDef.SpriteType type) + { + this(null, bamResource, cycle, type, null); + } + + /** + * Creates a new independent cycle definition with the specified parameters. + * Sprite type is assumed to be {@link SpriteType#AVATAR}. + * @param bamResource the BAM resource + * @param cycle the BAM cycle index + * @param behavior the playback behavior of the BAM cycle + */ + public CycleDef(ResourceEntry bamResource, int cycle, SegmentDef.Behavior behavior) + { + this(null, bamResource, cycle, null, behavior); + } + + /** + * Creates a new independent cycle definition with the specified parameters. + * @param bamResource the BAM resource + * @param cycle the BAM cycle index + * @param type the sprite type of the specified segment + * @param behavior the playback behavior of the BAM cycle + */ + public CycleDef(ResourceEntry bamResource, int cycle, SegmentDef.SpriteType type, SegmentDef.Behavior behavior) + { + this(null, bamResource, cycle, type, behavior); + } + + /** + * Creates a new cycle definition with the specified parameters linked to the specified {@link DirDef} instance. + * @param parent the parent {@code DirDef} instance + * @param bamResource the BAM resource + * @param cycle the BAM cycle index + * @param type the sprite type of the specified segment + * @param behavior the playback behavior of the BAM cycle + */ + public CycleDef(DirDef parent, ResourceEntry bamResource, int cycle, SegmentDef.SpriteType type, SegmentDef.Behavior behavior) + { + this.parent = parent; + addCycle(bamResource, cycle, type, behavior); + } + + /** + * Creates a new cycle definition with the specified parameters linked to the specified {@link DirDef} instance. + * @param parent the parent {@code DirDef} instance + * @param cycleInfo collection of fully defined {@code SegmentDef} instances that are added to this cycle definition. + */ + public CycleDef(DirDef parent, Collection cycleInfo) + { + this.parent = parent; + addCycles(cycleInfo); + } + + /** + * Creates a new cycle definition with the attributes defined in the specified {@code CycleDef} argument. + * Parent attribute is set to {@code null}. + * @param sd the {@code CycleDef} object to clone. + */ + public CycleDef(CycleDef cd) + { + Objects.requireNonNull(cd, "CycleDef instance cannot be null"); + this.parent = null; + List list = new ArrayList<>(); + for (final SegmentDef sd : cd.cycles) { + list.add(new SegmentDef(sd)); + } + addCycles(list); + } + + /** Returns the parent {@link DirDef} instance linked to this object. */ + public DirDef getParent() { return parent; } + + /** Updates the parent link. Should not be called directly. */ + void setParent(DirDef parent) { this.parent = parent; } + + /** Provides access to the list of segment definitions for this cycle. */ + public List getCycles() { return cycles; } + + /** Adds a new cycle definition. */ + public void addCycle(ResourceEntry bamResource, int cycle) + { + addCycle(bamResource, cycle, null, null); + } + + /** Adds a new cycle definition. */ + public void addCycle(ResourceEntry bamResource, int cycle, SegmentDef.SpriteType type) + { + addCycle(bamResource, cycle, type, null); + } + + /** Adds a new cycle definition. */ + public void addCycle(ResourceEntry bamResource, int cycle, SegmentDef.SpriteType type, SegmentDef.Behavior behavior) + { + this.cycles.add(new SegmentDef(this, bamResource, cycle, type, behavior)); + } + + /** Adds new cycle definitions. */ + public void addCycles(Collection cycleInfo) + { + for (Iterator iter = cycleInfo.iterator(); iter.hasNext();) { + final SegmentDef sd = Objects.requireNonNull(iter.next(), "Segment definition cannot be null"); + if (!this.cycles.stream().anyMatch(sd2 -> sd2.equals(sd))) { + sd.setParent(this); + this.cycles.add(sd); + } + } + } + + /** Advances the animation by one frame for all segment definitions according to their respective behavior. */ + public void advance() + { + this.cycles.forEach(SegmentDef::advance); + } + + /** Resets BAM cycles in all segment definitions back to the first frame. */ + public void reset() + { + this.cycles.forEach(SegmentDef::reset); + } + + /** Determines the minimum number of frames in the whole list of segment definitions. */ + public int getMinimumFrames() + { + return cycles.stream().mapToInt(v -> v.getFrameCount()).min().orElse(0); + } + + /** Determines the maximum number of frames in the whole list of segment definitions. */ + public int getMaximumFrames() + { + return cycles.stream().mapToInt(v -> v.getFrameCount()).max().orElse(0); + } + + @Override + public CycleDef clone() + { + return new CycleDef(this); + } + + @Override + public String toString() + { + return "cycles=" + cycles.toString(); + } + + @Override + public int hashCode() + { + int hash = 7; + hash = 31 * hash + ((cycles == null) ? 0 : cycles.hashCode()); + return hash; + } + + @Override + public boolean equals(Object o) + { + if (o == this) { + return true; + } + if (!(o instanceof CycleDef)) { + return false; + } + CycleDef other = (CycleDef)o; + return (this.cycles == null && other.cycles == null) || + (this.cycles != null && this.cycles.equals(other.cycles)); + } +} diff --git a/src/org/infinity/resource/cre/decoder/util/DecoderAttribute.java b/src/org/infinity/resource/cre/decoder/util/DecoderAttribute.java new file mode 100644 index 000000000..485f4f09c --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/util/DecoderAttribute.java @@ -0,0 +1,118 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.decoder.util; + +import java.util.Objects; + +/** + * Class for accessing creature animation attributes. + */ +public class DecoderAttribute implements Comparable +{ + /** Supported data types for creature animation attributes. */ + public enum DataType { + /** Specifies an integer attribute. */ + INT(0), + /** Specifies a floating point attribute (double). */ + DECIMAL(0.0), + /** Specifies a boolean attribute. */ + BOOLEAN(false), + /** Specifies a string attribute. */ + STRING(""), + /** Specifies a custom data type of any kind. */ + USERDEFINED(null); + + public Object getDefaultValue() { return defValue; } + + private final Object defValue; + private DataType(Object defValue) { + this.defValue = defValue; + } + } + + private final String name; + private final DataType type; + private final Object defValue; + + /** Creates a new {@code Attribute} instance with the specified arguments. */ + public static DecoderAttribute with(String name, DataType type) + { + return new DecoderAttribute(name, type); + } + + /** Creates a new {@code Attribute} instance with the specified arguments. */ + public static DecoderAttribute with(String name, DataType type, Object defValue) + { + return new DecoderAttribute(name, type, defValue); + } + + public DecoderAttribute(String name, DataType type) + { + this(name, type, Objects.requireNonNull(type, "Attribute type cannot be null").getDefaultValue()); + } + + public DecoderAttribute(String name, DataType type, Object defValue) + { + this.name = Objects.requireNonNull(name, "Attribute name cannot be null"); + this.type = Objects.requireNonNull(type, "Attribute type cannot be null"); + this.defValue = defValue; + } + + /** Returns the attribute key. */ + public String getName() + { + return name; + } + + /** Returns the data type of the attribute value as one of several predefined enums. */ + public DataType getType() + { + return type; + } + + /** Returns a default value that is returned if the actual value is not available. */ + public Object getDefaultValue() + { + return defValue; + } + + @Override + public String toString() + { + return "key=" + name + ", type=" + type.toString(); + } + + @Override + public boolean equals(Object o) + { + if (o == this) { + return true; + } + if (!(o instanceof DecoderAttribute)) { + return false; + } + DecoderAttribute other = (DecoderAttribute)o; + boolean retVal = (this.name == null && other.name == null) || + (this.name != null && this.name.equals(other.name)); + retVal &= (this.type == null && other.type == null) || + (this.type != null && this.type.equals(other.type)); + return retVal; + } + + @Override + public int hashCode() + { + int hash = 7; + hash = 32 * hash + ((name == null) ? 0 : name.hashCode()); + hash = 32 * hash + ((type == null) ? 0 : type.hashCode()); + return hash; + } + + @Override + public int compareTo(DecoderAttribute o) + { + return name.compareTo(o.name); + } +} diff --git a/src/org/infinity/resource/cre/decoder/util/DirDef.java b/src/org/infinity/resource/cre/decoder/util/DirDef.java new file mode 100644 index 000000000..fdf085164 --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/util/DirDef.java @@ -0,0 +1,125 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.decoder.util; + +import java.util.Objects; + +/** + * Definition of a single direction in an animation sequence. + */ +public class DirDef implements Cloneable +{ + private final Direction direction; + private final CycleDef cycle; + private final boolean mirrored; + + private SeqDef parent; + + /** + * Creates a new independent direction definition. + * @param dir the sprite direction + * @param mirrored whether the BAM frames of the sprite should be horizontally mirrored (fake eastern direction) + * @param cycle the cycle definition + */ + public DirDef(Direction dir, boolean mirrored, CycleDef cycle) + { + this(null, dir, mirrored, cycle); + } + + /** + * Creates a new direction definition linked to the specified {@link SeqDef} instance. + * @param parent the parent {@code SeqDef} instance + * @param dir the sprite direction + * @param mirrored whether the BAM frames of the sprite should be horizontally mirrored (fake eastern direction) + * @param cycle the cycle definition + */ + public DirDef(SeqDef parent, Direction dir, boolean mirrored, CycleDef cycle) + { + this.parent = parent; + this.direction = Objects.requireNonNull(dir, "Creature direction cannot be null"); + this.cycle = Objects.requireNonNull(cycle, "Cycle definition cannot be null"); + this.cycle.setParent(this); + this.mirrored = mirrored; + } + + /** + * Creates a new direction definition with the attributes defined in the specified {@code DirDef} argument. + * Parent attribute is set to {@code null}. + * @param dd the {@code DirDef} object to clone. + */ + public DirDef(DirDef dd) + { + Objects.requireNonNull(dd, "DirDef instance cannot be null"); + this.parent = null; + this.direction = dd.direction; + this.cycle = new CycleDef(dd.cycle); + this.cycle.setParent(this); + this.mirrored = dd.mirrored; + } + + /** Returns the parent {@link SeqDef} instance linked to this object. */ + public SeqDef getParent() { return parent; } + + /** Updates the parent link. Should not be called directly. */ + void setParent(SeqDef parent) { this.parent = parent; } + + /** Returns the direction defined by this instance. */ + public Direction getDirection() { return direction; } + + /** Returns the cycle definition associated with this direction. */ + public CycleDef getCycle() { return cycle; } + + /** Returns whether the BAM frames of this direction should be horizontally mirrored (fake eastern direction). */ + public boolean isMirrored() { return mirrored; } + + /** + * Advances the animation by one frame for all segment definitions in the associated cycle + * according to their respective behavior. + */ + public void advance() { cycle.advance(); } + + /** Resets BAM cycles in all segment definitions in the associated cycle back to the first frame. */ + public void reset() { cycle.reset(); } + + @Override + public DirDef clone() + { + return new DirDef(this); + } + + @Override + public String toString() + { + return "direction=" + direction.toString() + ", mirrored=" + Boolean.toString(mirrored) + ", cycle={" + cycle.toString() + "}"; + } + + @Override + public int hashCode() + { + int hash = 7; + hash = 31 * hash + ((direction == null) ? 0 : direction.hashCode()); + hash = 31 * hash + ((cycle == null) ? 0 : cycle.hashCode()); + hash = 31 * hash + Boolean.valueOf(mirrored).hashCode(); + return hash; + } + + @Override + public boolean equals(Object o) + { + if (o == this) { + return true; + } + if (!(o instanceof DirDef)) { + return false; + } + DirDef other = (DirDef)o; + boolean retVal = (this.direction == null && other.direction == null) || + (this.direction != null && this.direction.equals(other.direction)); + retVal &= (this.cycle == null && other.cycle == null) || + (this.cycle != null && this.cycle.equals(other.cycle)); + retVal &= (this.mirrored == other.mirrored); + return retVal; + } +} diff --git a/src/org/infinity/resource/cre/decoder/util/Direction.java b/src/org/infinity/resource/cre/decoder/util/Direction.java new file mode 100644 index 000000000..ad782bcf0 --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/util/Direction.java @@ -0,0 +1,62 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.decoder.util; + +/** + * Available cardinal directions for animation sequences. + */ +public enum Direction { + /** South */ + S(0), + /** South-southwest */ + SSW(1), + /** Southwest */ + SW(2), + /** West-southwest */ + WSW(3), + /** West */ + W(4), + /** West-northwest */ + WNW(5), + /** Northwest */ + NW(6), + /** North-northwest */ + NNW(7), + /** North */ + N(8), + /** North-northeast */ + NNE(9), + /** Northeast */ + NE(10), + /** East-northeast */ + ENE(11), + /** East */ + E(12), + /** East-southeast */ + ESE(13), + /** Southeast */ + SE(14), + /** South-southeast */ + SSE(15); + + private final int dir; + private Direction(int dir) { this.dir = dir; } + + /** Returns the numeric direction value. */ + public int getValue() { return dir; } + + /** + * Determines the {@link Direction} instance associated with the specified numeric value and returns it. + * Return {@code null} if association could not be determined. + */ + public static Direction from(int value) { + for (final Direction d : Direction.values()) { + if (d.getValue() == value) { + return d; + } + } + return null; + } +} diff --git a/src/org/infinity/resource/cre/decoder/util/EffectInfo.java b/src/org/infinity/resource/cre/decoder/util/EffectInfo.java new file mode 100644 index 000000000..fb774afdb --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/util/EffectInfo.java @@ -0,0 +1,1097 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.decoder.util; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.Predicate; + +import org.infinity.datatype.IsNumeric; +import org.infinity.datatype.IsTextual; +import org.infinity.resource.Profile; +import org.infinity.resource.ResourceFactory; +import org.infinity.resource.StructEntry; +import org.infinity.resource.cre.CreResource; +import org.infinity.resource.key.ResourceEntry; +import org.infinity.resource.spl.SplResource; +import org.infinity.util.Misc; +import org.infinity.util.Table2da; +import org.infinity.util.Table2daCache; +import org.infinity.util.io.StreamUtils; + +/** + * Manages effects opcodes for a specific target. + */ +public class EffectInfo +{ + /** Effect opcode 7: Set color */ + public static final int OPCODE_SET_COLOR = 7; + /** Effect opcode 8: Set color glow solid */ + public static final int OPCODE_SET_COLOR_GLOW = 8; + /** Effect opcode 51: Character tint solid */ + public static final int OPCODE_TINT_SOLID = 51; + /** Effect opcode 52: Character tint bright */ + public static final int OPCODE_TINT_BRIGHT = 52; + /** Effect opcode 65: Blur */ + public static final int OPCODE_BLUR = 65; + /** Effect opcode 66: Translucency */ + public static final int OPCODE_TRANSLUCENCY = 66; + /** Effect opcode 134: Petrification */ + public static final int OPCODE_PETRIFICATION = 134; + /** Effect opcode 218: Stoneskin */ + public static final int OPCODE_STONESKIN = 218; + + /** A predicate for filtering opcode 7 effects (set color). */ + public static final Predicate FILTER_COLOR_EFFECT = (fx) -> (fx.getOpcode() == OPCODE_SET_COLOR); + /** A predicate for filtering opcode 8 effects (color glow). */ + public static final Predicate FILTER_COLOR_GLOW_EFFECT = (fx) -> (fx.getOpcode() == OPCODE_SET_COLOR_GLOW); + /** A predicate for filtering opcode 51 effects (tint solid). */ + public static final Predicate FILTER_TINT_SOLID_EFFECT = (fx) -> (fx.getOpcode() == OPCODE_TINT_SOLID); + /** A predicate for filtering opcode 52 effects (tint bright). */ + public static final Predicate FILTER_TINT_BRIGHT_EFFECT = (fx) -> (fx.getOpcode() == OPCODE_TINT_BRIGHT); + /** A predicate for filtering blur effects. */ + public static final Predicate FILTER_BLUR_EFFECT = (fx) -> (fx.getOpcode() == OPCODE_BLUR); + /** A predicate for filtering translucency effects. */ + public static final Predicate FILTER_TRANSLUCENCY_EFFECT = (fx) -> (fx.getOpcode() == OPCODE_TRANSLUCENCY); + /** A predicate for filtering all stone-like effects. */ + public static final Predicate FILTER_STONE_EFFECT = (fx) -> (fx.getOpcode() == OPCODE_PETRIFICATION || + fx.getOpcode() == OPCODE_STONESKIN); + + + private final EnumMap> effectMap = new EnumMap<>(SegmentDef.SpriteType.class); + + /** A helper method that reverses byte order of the specified parameter. */ + public static int swapBytes(int value) + { + int retVal = 0; + retVal |= (value >>> 24) & 0xff; + retVal |= (value >>> 8) & 0xff00; + retVal |= (value << 8) & 0xff0000; + retVal |= (value << 24) & 0xff000000; + return retVal; + } + + /** A helper method that reverses word order of the specified parameter. */ + public static int swapWords(int value) + { + int retVal = 0; + retVal |= (value >> 16) & 0xffff; + retVal |= (value << 16) & 0xffff0000; + return retVal; + } + + public EffectInfo() + { + } + + /** + * Returns a list of {@code SpriteType} definitions containing effect definitions. + * @param creInfo the creature target. + * @return List of sprite type definitions. + */ + public List getSpriteTypes(CreatureInfo creInfo) + { + return getSpriteTypes(creInfo, null); + } + + /** + * Returns a list of {@code SpriteType} definitions with effects matching the specified opcode. + * @param creInfo the creature target. + * @param opcode the opcode to match. + * @return List of sprite type definitions. + */ + public List getSpriteTypes(CreatureInfo creInfo, int opcode) + { + return getSpriteTypes(creInfo, (fx) -> fx.getOpcode() == opcode); + } + + /** + * Returns a list of {@code SpriteType} definitions with effects matching the specified predicate. + * @param creInfo the creature target. + * @param pred the predicate function object. Specify {@code null} to match any effects. + * @return List of sprite type definitions. + */ + public List getSpriteTypes(CreatureInfo creInfo, Predicate pred) + { + final List retVal = new ArrayList<>(); + + if (pred == null) { + pred = (fx) -> true; + } + + for (final Map.Entry> entry : effectMap.entrySet()) { + if (entry.getValue().parallelStream().anyMatch(pred.and(e -> isEffectValid(e, creInfo)))) { + retVal.add(entry.getKey()); + } + } + + return retVal; + } + + /** + * A convenience method that returns the first available effect associated with the + * specified {@code SpriteType}. + * @param creInfo the creature target. + * @param type the sprite type to filter. Specify {@code null} for non-specific effects. + * @return first matching {@code Effect} instance. Returns {@code null} if not available. + */ + public Effect getFirstEffect(CreatureInfo creInfo, SegmentDef.SpriteType type) + { + List list = getEffects(creInfo, type, null); + return !list.isEmpty() ? list.get(0) : null; + } + + /** + * A convenience method that returns the first available effect matching the specified {@code SpriteType} + * and opcode. + * @param creInfo the creature target. + * @param type the sprite type to filter. Specify {@code null} for non-specific effects. + * @param opcode the opcode to filter. + * @return first matching {@code Effect} instance. Returns {@code null} if not available. + */ + public Effect getFirstEffect(CreatureInfo creInfo, SegmentDef.SpriteType type, int opcode) + { + List list = getEffects(creInfo, type, (fx) -> fx.getOpcode() == opcode); + return !list.isEmpty() ? list.get(0) : null; + } + + /** + * A convenience method that returns the first available effect matching the specified {@code SpriteType} + * and opcode. + * @param creInfo the creature target. + * @param type the sprite type to filter. Specify {@code null} for non-specific effects. + * @param pred the predicate function object responsible for filtering. Specify {@code null} to return the + * first available effects without filtering. + * @return first matching {@code Effect} instance. Returns {@code null} if not available. + */ + public Effect getFirstEffect(CreatureInfo creInfo, SegmentDef.SpriteType type, Predicate pred) + { + if (creInfo == null) { + throw new NullPointerException("Creature info cannot be null"); + } + + Effect retVal = null; + + if (type == null) { + type = SegmentDef.SpriteType.AVATAR; + } + + if (pred == null) { + pred = (fx) -> true; + } + + Set set = effectMap.get(type); + if (set != null) { + retVal = set + .parallelStream() + .filter(pred.and(e -> isEffectValid(e, creInfo))) + .findAny() + .orElse(null); + } + + return retVal; + } + + /** + * Returns all available effects associated with the specified {@code SpriteType}. + * @param creInfo the creature target. + * @param type the sprite type to filter. Specify {@code null} for non-specific effects. + * @return list of effects associated with the sprite type. Empty list if no effects available. + */ + public List getEffects(CreatureInfo creInfo, SegmentDef.SpriteType type) + { + return getEffects(creInfo, type, null); + } + + /** + * Returns all available effects matching the specified {@code SpriteType} and opcode. + * @param creInfo the creature target. + * @param type the sprite type to filter. Specify {@code null} for non-specific effects. + * @param opcode the opcode to filter. + * @return list of effects associated with the sprite type and matching opcode. Empty list if no effects available. + */ + public List getEffects(CreatureInfo creInfo, SegmentDef.SpriteType type, int opcode) + { + return getEffects(creInfo, type, (fx) -> fx.getOpcode() == opcode); + } + + /** + * Returns all available effects matching the specified {@code SpriteType} and predicate. + * @param creInfo the creature target. + * @param type the sprite type to filter. Specify {@code null} for non-specific effects. + * @param pred the predicate function object. Effects passing the test are added to the results list. + * Specify {@code null} to return all effects associated with the specified sprite type. + * @return list of effects associated with the sprite type and matching predicate. Empty list if no effects available. + */ + public List getEffects(CreatureInfo creInfo, SegmentDef.SpriteType type, Predicate pred) + { + if (creInfo == null) { + throw new NullPointerException("Creature info cannot be null"); + } + + final List retVal = new ArrayList<>(); + + if (type == null) { + type = SegmentDef.SpriteType.AVATAR; + } + + if (pred == null) { + pred = (fx) -> true; + } + + Set set = effectMap.get(type); + if (set != null) { + set + .stream() + .filter(pred.and(e -> isEffectValid(e, creInfo))) + .forEach(fx -> retVal.add(fx)); + } + + return retVal; + } + + /** + * Returns {@code true} if the specified {@code SpriteType} contains one or more effects matching + * the specified opcode. + * @param type the sprite type to filter. Specify {@code null} for non-specific effects. + * @param opcode the opcode to match. + * @return {@code true} if match is found, {@code false} otherwise. + */ + public boolean hasEffect(SegmentDef.SpriteType type, int opcode) + { + return hasEffect(type, (fx) -> fx.getOpcode() == opcode); + } + + /** + * Returns {@code true} if the specified {@code SpriteType} contains one or more effects matching + * the specified predicate. + * @param type the sprite type to filter. Specify {@code null} for non-specific effects. + * @param pred the predicate function object. Specify {@code null} to match any effects. + * @return {@code true} if match is found, {@code false} otherwise. + */ + public boolean hasEffect(SegmentDef.SpriteType type, Predicate pred) + { + if (type == null) { + type = SegmentDef.SpriteType.AVATAR; + } + + if (pred == null) { + pred = (fx) -> true; + } + + Set set = effectMap.get(type); + if (set != null) { + set.parallelStream().anyMatch(pred); + } + + return false; + } + + /** + * A specialized method that returns the first available color-related effect matching the specified parameters. + * @param creInfo the creature target. + * @param type the sprite type to filter. Specify {@code null} for non-specific effects. + * @param opcode the opcode to filter. + * @param location color location index. See opcode description for more details. + * @return the first matching effect. Returns {@code null} if there is no match available. + */ + public Effect getColorByLocation(CreatureInfo creInfo, SegmentDef.SpriteType type, int opcode, int location) + { + final int locationIndex = (location == 255) ? location : (location & 0x0f); + + final Predicate pred = (fx) -> { + return (fx.getOpcode() == opcode) && + ((fx.getParameter2() & 0xf) == locationIndex); + }; + + return getFirstEffect(creInfo, type, pred); + } + + /** + * Adds the specified effect and associates it with the {@code SpriteType} defined by the effect. + *

    Indirectly created effects (e.g. via opcode 146) are correctly resolved and added. + * @param effect the effect to add. + */ + public void add(Effect effect) + { + if (effect == null) { + throw new NullPointerException("Effect parameter cannot be null"); + } + + List effects = resolveEffect(null, effect); + for (final Effect fx : effects) { + SegmentDef.SpriteType type = getEffectType(fx); + Set set = effectMap.get(type); + if (set == null) { + set = new HashSet<>(); + effectMap.put(type, set); + } + set.add(fx); + } + } + + /** + * Checks if the specified effect is available for the given creature. + * @param effect the effect to check + * @param creInfo the creature target + * @return {@code true} if the effect is valid for the target. Returns {@code false} otherwise. + */ + protected boolean isEffectValid(Effect effect, CreatureInfo creInfo) + { + boolean retVal = true; + if (effect == null) { + return retVal; + } + + switch (effect.getOpcode()) { + case 177: // Use EFF + case 283: // Use EFF as curse + retVal = evaluateIds(creInfo, effect); + break; + case 183: // Use EFF for item type + retVal = evaluateItemCategory(creInfo, effect.getParameter2()); + break; + case 326: // Apply effects list + retVal = evaluateSplProt(creInfo, effect.getParameter2(), effect.getParameter1()); + break; + } + + if (retVal) { + retVal = isEffectValid(effect.getParent(), creInfo); + } + + return retVal; + } + + /** Determines the sprite type target defined by this effect. Only relevant for color-related effects. */ + private SegmentDef.SpriteType getEffectType(Effect effect) + { + switch (effect.getOpcode()) { + case 7: // Set color + case 8: // Set color glow solid + case 9: // Set color glow pulse + case 50: // Character color pulse + case 51: // Character tint solid + case 52: // Character tint bright + switch ((effect.getParameter2() >> 4) & 0xf) { + case 1: + return SegmentDef.SpriteType.WEAPON; + case 2: + return SegmentDef.SpriteType.SHIELD; + case 3: + return SegmentDef.SpriteType.HELMET; + default: + return SegmentDef.SpriteType.AVATAR; + } + default: + return SegmentDef.SpriteType.AVATAR; + } + } + + /** + * Resolves effects which reference additional effects via secondary resources (e.g. SPL, EFF). + * The primary effect is skipped in these cases. + */ + private List resolveEffect(Effect parent, Effect effect) + { + List retVal = new ArrayList<>(); + + effect.setParent(parent); + retVal.add(effect); + + switch (effect.getOpcode()) { + case 146: // Cast spell + resolveSPL(retVal, effect, ResourceFactory.getResourceEntry(effect.getResource() + ".SPL")); + break; + case 177: // Use EFF + resolveEFF(retVal, effect, ResourceFactory.getResourceEntry(effect.getResource() + ".EFF")); + break; + case 183: // Use EFF for item type + if (Profile.getEngine() != Profile.Engine.PST) { + resolveEFF(retVal, effect, ResourceFactory.getResourceEntry(effect.getResource() + ".EFF")); + } + break; + case 283: // Use EFF as curse + switch (Profile.getEngine()) { + case BG2: + case EE: + resolveEFF(retVal, effect, ResourceFactory.getResourceEntry(effect.getResource() + ".EFF")); + break; + default: + } + break; + case 326: // Apply effects list + if (Profile.getEngine() == Profile.Engine.EE) { + resolveSPL(retVal, effect, ResourceFactory.getResourceEntry(effect.getResource() + ".SPL")); + } + break; + default: + } + + return retVal; + } + + /** Resolves all effects (global effects and effects from first available ability) from the specified SPL resource. */ + private void resolveSPL(List list, Effect parent, ResourceEntry entry) + { + if (entry == null) { + return; + } + + try { + SplResource spl = new SplResource(entry); + int ofsAbil = ((IsNumeric)spl.getAttribute(SplResource.SPL_OFFSET_ABILITIES)).getValue(); + int numAbil = ((IsNumeric)spl.getAttribute(SplResource.SPL_NUM_ABILITIES)).getValue(); + int numGlobalFx = ((IsNumeric)spl.getAttribute(SplResource.SPL_NUM_GLOBAL_EFFECTS)).getValue(); + + // evaluating global effects + if (numGlobalFx > 0) { + List fxList = spl.getFields(org.infinity.resource.Effect.class); + if (fxList != null) { + for (final StructEntry se : fxList) { + if (se instanceof org.infinity.resource.Effect) { + final List retList = resolveEffect(parent, new Effect((org.infinity.resource.Effect)se)); + list.addAll(retList); + } + } + } + } + + // evaluating ability effects (first ability only) + if (numAbil > 0) { + StructEntry abil = spl.getField(org.infinity.resource.spl.Ability.class, ofsAbil + spl.getExtraOffset()); + if (abil instanceof org.infinity.resource.spl.Ability) { + List fxList = ((org.infinity.resource.spl.Ability)abil).getFields(org.infinity.resource.Effect.class); + for (final StructEntry se : fxList) { + if (se instanceof org.infinity.resource.Effect) { + final List retList = resolveEffect(parent, new Effect((org.infinity.resource.Effect)se)); + list.addAll(retList); + } + } + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** Resolves the effect from the specified EFF resource. */ + private void resolveEFF(List list, Effect parent, ResourceEntry entry) + { + if (entry == null) { + return; + } + + try { + final List retList = resolveEffect(parent, Effect.fromEffectV2(entry.getResourceBuffer(), 8)); + list.addAll(retList); + } catch (Exception e) { + e.printStackTrace(); + } + } + + /** + * Evaluates the item category filter based on equipped items by the target. + * @param creInfo creature target + * @param cat the item category + * @return whether creature has equipped items with the specified item category. + */ + private boolean evaluateItemCategory(CreatureInfo creInfo, int cat) + { + boolean retVal = false; + + for (final CreatureInfo.ItemSlots slot : CreatureInfo.ItemSlots.values()) { + ItemInfo ii = creInfo.getItemInfo(slot); + if (ii != null) { + if (cat == ii.getCategory()) { + retVal = true; + break; + } + } + } + + return retVal; + } + + /** + * Evaluates the IDS filter specified by type and entry value for the given target. + * @param creInfo creature target + * @param type the IDS resource type + * @param entry the IDS entry index + * @return whether creature stat matches reference stat. + */ + private boolean evaluateIds(CreatureInfo creInfo, Effect effect) + { + boolean retVal = false; + + int type = effect.getParameter2(); + int entry = effect.getParameter1(); + switch (type) { + case 2: // EA.IDS + retVal = (entry == creInfo.getAllegiance()); + break; + case 3: // GENERAL.IDS + retVal = (entry == ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_GENERAL)).getValue()); + break; + case 4: // RACE.IDS + retVal = (entry == ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_RACE)).getValue()); + break; + case 5: // CLASS.IDS + retVal = (entry == ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_CLASS)).getValue()); + break; + case 6: // SPECIFIC.IDS + retVal = (entry == ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_SPECIFICS)).getValue()); + break; + case 7: // GENDER.IDS + retVal = (entry == ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_GENDER)).getValue()); + break; + case 8: // ALIGN.IDS + retVal = (entry == ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_ALIGNMENT)).getValue()); + break; + case 9: // KIT.IDS + if (Profile.isEnhancedEdition()) { + int kit = ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_RACE)).getValue(); + kit = ((kit & 0xffff) << 16) | ((kit >>> 16) & 0xffff); + retVal = (entry == kit); + } + break; + case 11: // Actor's script name + if (Profile.isEnhancedEdition()) { + String name = ((IsTextual)creInfo.getCreResource().getAttribute(CreResource.CRE_SCRIPT_NAME)).getText(); + retVal = (effect.getResource().equalsIgnoreCase(name)); + } + break; + } + + return retVal; + } + + /** + * Evaluates the SPLPROT filter specified by type and value for the given target. + * @param creInfo creature target + * @param type SPLPROT.2DA entry index + * @param refValue an optional reference value used by selected types + * @return whether creature stat matches reference stat. + */ + private boolean evaluateSplProt(CreatureInfo creInfo, int type, int refValue) + { + Table2da table = Table2daCache.get("SPLPROT.2DA"); + if (table == null) { + return true; + } + + String s = table.get(type, 1); + int radix = s.startsWith("0x") ? 16 : 10; + int stat = Misc.toNumber(s, radix, -1); + + s = table.get(type, 2); + radix = s.startsWith("0x") ? 16 : 10; + int value = Misc.toNumber(s, radix, -1); + if (value == -1) { + value = refValue; + } + + s = table.get(type, 3); + radix = s.startsWith("0x") ? 16 : 10; + int rel = Misc.toNumber(s, radix, -1); + + switch (stat) { + case 0x100: // source equals target + // irrelevant + break; + case 0x101: // source is not target + // irrelevant + break; + case 0x102: // circle size + return splProtRelation(rel, creInfo.getDecoder().getPersonalSpace(), value); + case 0x103: // use two rows + return evaluateSplProt(creInfo, value, -1) || evaluateSplProt(creInfo, rel, -1); + case 0x104: // negate 0x103 + return !(evaluateSplProt(creInfo, value, -1) || evaluateSplProt(creInfo, rel, -1)); + case 0x105: // source and target morale match + // not supported + break; + case 0x106: // AREATYPE.IDS + // irrelevant + break; + case 0x107: // time of day (hours) + // irrelevant + break; + case 0x108: // source and target ethical match + // not supported + break; + case 0x109: // evasion + // not supported + break; + case 0x10a: // EA.IDS + return splProtRelation(rel, creInfo.getAllegiance(), value); + case 0x10b: // GENERAL.IDS + return splProtRelation(rel, ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_GENERAL)).getValue(), value); + case 0x10c: // RACE.iDS + return splProtRelation(rel, ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_RACE)).getValue(), value); + case 0x10d: // CLASS.IDS + return splProtRelation(rel, ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_CLASS)).getValue(), value); + case 0x10e: // SPECIFIC.IDS + return splProtRelation(rel, ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_SPECIFICS)).getValue(), value); + case 0x10f: // GENDER.IDS + return splProtRelation(rel, ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_GENDER)).getValue(), value); + case 0x110: // ALIGN.IDS + return splProtRelation(rel, ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_ALIGNMENT)).getValue(), value); + case 0x111: // STATE.IDS + return splProtRelation(rel, creInfo.getStatus(), value); + case 0x112: // SPLSTATE.IDS + // not supported + break; + case 0x113: // source and target allies + // irrelevant + break; + case 0x114: // source and target enemies + // irrelevant + break; + case 0x115: // summon creature limit + // irrelevant + break; + case 0x116: // chapter check + // irrelevant + break; + default: // STATS.IDS + return splProtRelation(rel, getStatValue(creInfo, stat), value); + } + return true; + } + + /** + * Performs a relational operation on the value parameters and returns the result. + * @param relation the relation type + * @param creValue value retrieved from the target + * @param refValue value retrieved either from splprot table or custom effect value + */ + private boolean splProtRelation(int relation, int creValue, int refValue) + { + switch (relation) { + case 0: // less or equal + return (creValue <= refValue); + case 1: // equal + return (creValue == refValue); + case 2: // less + return (creValue < refValue); + case 3: // greater + return (creValue > refValue); + case 4: // greater or equal + return (creValue >= refValue); + case 5: // not equal + return (creValue != refValue); + case 6: // binary less or equal (stat doesn't contain extra bits not in value) + return (creValue & ~refValue) == 0; + case 7: // binary more or equal (stat contains all bits of value) + return (creValue & refValue) == refValue; + case 8: // binary match (at least one bit is common) + return (creValue & refValue) != 0; + case 9: // binary not match (none of the bits are common) + return (creValue & refValue) == 0; + case 10: // binary more (stat contains at least one bit not in value) + return (creValue & ~refValue) != 0; + case 11: // binary less (stat doesn't contain all the bits of value) + return (creValue & refValue) != refValue; + } + return false; + } + + /** Retrieves the specified stat from the given creature if available. Returns 0 otherwise. */ + private int getStatValue(CreatureInfo creInfo, int stat) + { + // only selected stat values are supported + switch (stat) { + case 1: // MAXHITPOINTS + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_HP_MAX)).getValue(); + case 2: // ARMORCLASS + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_AC_EFFECTIVE)).getValue(); + case 7: // THAC0 + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_THAC0)).getValue(); + case 8: // NUMBEROFATTACKS + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_ATTACKS_PER_ROUND)).getValue(); + case 9: // SAVEVSDEATH + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_SAVE_DEATH)).getValue(); + case 10: // SAVEVSWANDS + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_SAVE_WAND)).getValue(); + case 11: // SAVEVSPOLY + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_SAVE_POLYMORPH)).getValue(); + case 12: // SAVEVSBREATH + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_SAVE_BREATH)).getValue(); + case 13: // SAVEVSSPELL + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_SAVE_SPELL)).getValue(); + case 14: // RESISTFIRE + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_RESISTANCE_FIRE)).getValue(); + case 15: // RESISTCOLD + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_RESISTANCE_COLD)).getValue(); + case 16: // RESISTELECTRICITY + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_RESISTANCE_ELECTRICITY)).getValue(); + case 17: // RESISTACID + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_RESISTANCE_ACID)).getValue(); + case 18: // RESISTMAGIC + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_RESISTANCE_MAGIC)).getValue(); + case 19: // RESISTMAGICFIRE + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_RESISTANCE_MAGIC_FIRE)).getValue(); + case 20: // RESISTMAGICCOLD + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_RESISTANCE_MAGIC_COLD)).getValue(); + case 21: // RESISTSLASHING + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_RESISTANCE_SLASHING)).getValue(); + case 22: // RESISTCRUSHING + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_RESISTANCE_CRUSHING)).getValue(); + case 23: // RESISTPIERCING + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_RESISTANCE_PIERCING)).getValue(); + case 24: // RESISTMISSILE + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_RESISTANCE_MISSILE)).getValue(); + case 25: // LORE + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_LORE)).getValue(); + case 26: // LOCKPICKING + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_OPEN_LOCKS)).getValue(); + case 27: // STEALTH + { + int v1 = ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_MOVE_SILENTLY)).getValue(); + int v2; + if (Profile.getEngine() != Profile.Engine.BG1 && Profile.getEngine() != Profile.Engine.IWD) { + v2 = ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_HIDE_IN_SHADOWS)).getValue(); + } else { + v2 = v1; + } + return (v1 + v2) / 2; + } + case 28: // TRAPS + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_FIND_TRAPS)).getValue(); + case 29: // PICKPOCKET + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_PICK_POCKETS)).getValue(); + case 30: // FATIGUE + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_FATIGUE)).getValue(); + case 31: // INTOXICATION + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_INTOXICATION)).getValue(); + case 32: // LUCK + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_LUCK)).getValue(); + case 34: // LEVEL + { + int v1 = ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_LEVEL_FIRST_CLASS)).getValue(); + int v2 = ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_LEVEL_SECOND_CLASS)).getValue(); + int v3 = ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_LEVEL_THIRD_CLASS)).getValue(); + if ((creInfo.getFlags() & 0x1f8) != 0) { + // dual-classed? + return v2; + } else { + return Math.max(v1, Math.max(v2, v3)); + } + } + case 35: // SEX + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_GENDER)).getValue(); + case 36: // STR + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_STRENGTH)).getValue(); + case 37: // STREXTRA + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_STRENGTH_BONUS)).getValue(); + case 38: // INT + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_INTELLIGENCE)).getValue(); + case 39: // WIS + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_WISDOM)).getValue(); + case 40: // DEX + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_DEXTERITY)).getValue(); + case 41: // CON + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_CONSTITUTION)).getValue(); + case 42: // CHR + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_CHARISMA)).getValue(); + case 43: // XPVALUE + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_XP_VALUE)).getValue(); + case 44: // XP + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_XP)).getValue(); + case 45: // GOLD + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_GOLD)).getValue(); + case 46: // MORALEBREAK + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_MORALE_BREAK)).getValue(); + case 47: // MORALERECOVERYTIME + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_MORALE_RECOVERY)).getValue(); + case 48: // REPUTATION + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_REPUTATION)).getValue(); + case 60: // TRANSLUCENT + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_TRANSLUCENCY)).getValue(); + case 68: // LEVEL2 + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_LEVEL_SECOND_CLASS)).getValue(); + case 69: // LEVEL3 + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_LEVEL_THIRD_CLASS)).getValue(); + case 135: // HIDEINSHADOWS + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_HIDE_IN_SHADOWS)).getValue(); + case 136: // DETECTILLUSIONS + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_DETECT_ILLUSION)).getValue(); + case 137: // SETTRAPS + return ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_SET_TRAPS)).getValue(); + case 152: // KIT + { + int v = ((IsNumeric)creInfo.getCreResource().getAttribute(CreResource.CRE_KIT)).getValue(); + v = ((v >>> 16) & 0xffff) | ((v & 0xffff) << 16); + return v; + } + } + return 0; + } + + @Override + public int hashCode() + { + int hashCode = 7; + hashCode = 31 * hashCode + ((effectMap == null) ? 0 : effectMap.hashCode()); + return hashCode; + } + + @Override + public boolean equals(Object o) + { + if (o == this) { + return true; + } + if (!(o instanceof EffectInfo)) { + return false; + } + + EffectInfo other = (EffectInfo)o; + + boolean retVal = (effectMap == null && other.effectMap == null) || + (effectMap != null && effectMap.equals(other.effectMap)); + + return retVal; + } + +//-------------------------- INNER CLASSES -------------------------- + + public static class Effect + { + private final int opcode; + private final int[] probability = new int[2]; + + private int target; + private int power; + private int parameter1; + private int parameter2; + private int timing; + private int dispelResist; + private int duration; + private String resource; + private int diceCount; + private int diceSize; + private int saveFlags; + private int saveBonus; + private int special; + + private Effect parent; + + /** Convenience method for creating an {@code Effect} instance from a byte array containing EFF V1 data. */ + public static Effect fromEffectV1(ByteBuffer buf, int startOfs) + { + Effect retVal = new Effect(buf.order(ByteOrder.LITTLE_ENDIAN).getShort(0x0 + startOfs)); + retVal.initEffectV1(buf, startOfs); + return retVal; + } + + /** Convenience method for creating an {@code Effect} instance from a byte array containing EFF V2 data. */ + public static Effect fromEffectV2(ByteBuffer buf, int startOfs) + { + Effect retVal = new Effect(buf.order(ByteOrder.LITTLE_ENDIAN).getInt(0x8 + startOfs)); + retVal.initEffectV2(buf, startOfs); + return retVal; + } + + /** Initializes a new Effect instance. */ + public Effect(int opcode) + { + this.opcode = opcode; + setProbability(0, 100); + setTarget(1); // Self + setTiming(2); // Instant/While equipped + } + + /** Initializes a new Effect instance from an effect V1 resource structure. */ + public Effect(org.infinity.resource.Effect struct) + { + ByteBuffer bb = Objects.requireNonNull(struct).getDataBuffer().order(ByteOrder.LITTLE_ENDIAN); + + this.opcode = bb.getShort(0x0); + initEffectV1(bb, 0); + } + + /** Initializes a new Effect instance from an effect V2 resource structure. */ + public Effect(org.infinity.resource.Effect2 struct) + { + ByteBuffer bb = Objects.requireNonNull(struct).getDataBuffer().order(ByteOrder.LITTLE_ENDIAN); + + this.opcode = bb.getInt(0x8); + initEffectV2(bb, 0); + } + + /** Returns the parent effect which applies the current effect. */ + public Effect getParent() { return parent; } + /** Sets the parent effect which applies the current effect. */ + public Effect setParent(Effect parent) { this.parent = parent; return this; } + + public int getOpcode() { return opcode; } + + public int getTarget() { return target; } + public Effect setTarget(int v) { target = v; return this; } + + public int getPower() { return power; } + public Effect setPower(int v) { power = v; return this; } + + public int getParameter1() { return parameter1; } + public Effect setParameter1(int v) { parameter1 = v; return this; } + + public int getParameter2() { return parameter2; } + public Effect setParameter2(int v) { parameter2 = v; return this; } + + public int getTiming() { return timing; } + public Effect setTiming(int v) { timing = v; return this; } + + public int getDispelResist() { return dispelResist; } + public Effect setDispelResist(int v) { dispelResist = v; return this; } + + public int getDuration() { return duration; } + public Effect setDuration(int v) { duration = v; return this; } + + public Effect setProbability(int low, int high) + { + probability[0] = low; + probability[1] = high; + return this; + } + + public int getProbabilityLow() { return probability[0]; } + public Effect setProbabilityLow(int v) { probability[0] = v; return this; } + + public int getProbabilityHigh() { return probability[1]; } + public Effect setProbabilityHigh(int v) { probability[1] = v; return this; } + + public String getResource() { return resource; } + public Effect setResource(String s) { resource = (s != null) ? s : ""; return this; } + + public int getDiceCount() { return diceCount; } + public Effect setDiceCount(int v) { diceCount = v; return this; } + + public int getDiceSize() { return diceSize; } + public Effect setDiceSize(int v) { diceSize = v; return this; } + + public int getSaveFlags() { return saveFlags; } + public Effect setSaveFlags(int v) { saveFlags = v; return this; } + public Effect setSaveFlag(int bitIdx, boolean set) + { + if (bitIdx >= 0 && bitIdx < 32) { + if (set) { + saveFlags |= (1 << bitIdx); + } else { + saveFlags &= ~(1 << bitIdx); + } + } + return this; + } + + public int getSaveBonus() { return saveBonus; } + public Effect setSaveBonus(int v) { saveBonus = v; return this; } + + public int getSpecial() { return special; } + public Effect setSpecial(int v) { special = v; return this; } + + @Override + public int hashCode() + { + int hashCode = 7; + hashCode = 31 * hashCode + opcode; + hashCode = 31 * hashCode + probability[0]; + hashCode = 31 * hashCode + probability[1]; + hashCode = 31 * hashCode + target; + hashCode = 31 * hashCode + power; + hashCode = 31 * hashCode + parameter1; + hashCode = 31 * hashCode + parameter2; + hashCode = 31 * hashCode + timing; + hashCode = 31 * hashCode + dispelResist; + hashCode = 31 * hashCode + duration; + hashCode = 31 * hashCode + ((resource == null) ? 0 : resource.hashCode()); + hashCode = 31 * hashCode + diceCount; + hashCode = 31 * hashCode + diceSize; + hashCode = 31 * hashCode + saveFlags; + hashCode = 31 * hashCode + saveBonus; + hashCode = 31 * hashCode + special; + + return hashCode; + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (!(o instanceof Effect)) { + return false; + } + + Effect other = (Effect)o; + boolean retVal = opcode == other.opcode; + retVal &= (probability[0] == other.probability[0]); + retVal &= (probability[1] == other.probability[1]); + retVal &= (target == other.target); + retVal &= (power == other.power); + retVal &= (parameter1 == other.parameter1); + retVal &= (parameter2 == other.parameter2); + retVal &= (timing == other.timing); + retVal &= (dispelResist == other.dispelResist); + retVal &= (duration == other.duration); + retVal &= (resource == null && other.resource == null) || + (resource != null && resource.equals(other.resource)); + retVal &= (diceCount == other.diceCount); + retVal &= (diceSize == other.diceSize); + retVal &= (saveFlags == other.saveFlags); + retVal &= (saveBonus == other.saveBonus); + retVal &= (special == other.special); + + return retVal; + } + + // Initializes effect parameters from EFF V1 structure + private void initEffectV1(ByteBuffer buf, int startOfs) + { + setTarget(buf.get(0x2 + startOfs)); + setPower(buf.get(0x3 + startOfs)); + setParameter1(buf.getInt(0x4 + startOfs)); + setParameter2(buf.getInt(0x8 + startOfs)); + setTiming(buf.get(0xc + startOfs)); + setDispelResist(buf.get(0xd + startOfs)); + setDuration(buf.getInt(0xe + startOfs)); + setProbability(buf.get(0x13 + startOfs), buf.get(0x12 + startOfs)); + setResource(StreamUtils.readString(buf, 0x14 + startOfs, 8)); + setDiceCount(buf.getInt(0x1c + startOfs)); + setDiceSize(buf.getInt(0x20 + startOfs)); + setSaveFlags(buf.getInt(0x24 + startOfs)); + setSaveBonus(buf.getInt(0x28 + startOfs)); + setSpecial(buf.getInt(0x2c + startOfs)); + } + + // Initializes effect parameters from EFF V2 structure. startOfs can be used to adjust offset for EFF resources. + private void initEffectV2(ByteBuffer buf, int startOfs) + { + setTarget(buf.getInt(0xc + startOfs)); + setPower(buf.getInt(0x10 + startOfs)); + setParameter1(buf.getInt(0x14 + startOfs)); + setParameter2(buf.getInt(0x18 + startOfs)); + setTiming(buf.getInt(0x1c + startOfs)); + setDispelResist(buf.get(0x54 + startOfs)); + setDuration(buf.getInt(0x20 + startOfs)); + setProbability(buf.getShort(0x26 + startOfs), buf.getShort(0x24 + startOfs)); + setResource(StreamUtils.readString(buf, 0x28 + startOfs, 8)); + setDiceCount(buf.getInt(0x30 + startOfs)); + setDiceSize(buf.getInt(0x34 + startOfs)); + setSaveFlags(buf.getInt(0x38 + startOfs)); + setSaveBonus(buf.getInt(0x3c + startOfs)); + setSpecial(buf.getInt(0x40 + startOfs)); + } + } +} diff --git a/src/org/infinity/resource/cre/decoder/util/FrameInfo.java b/src/org/infinity/resource/cre/decoder/util/FrameInfo.java new file mode 100644 index 000000000..dd5ad8bcc --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/util/FrameInfo.java @@ -0,0 +1,99 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.decoder.util; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Point; +import java.awt.Stroke; +import java.util.EnumMap; +import java.util.Objects; + +import org.infinity.resource.graphics.BamV1Decoder.BamV1Control; + +/** + * A structure that fully describes the location of a single BAM frame. + */ +public class FrameInfo +{ + /** Predefined stroke instance for drawing bounding boxes around creature sprite elements. */ + public static final Stroke STROKE_BOUNDING_BOX = new BasicStroke(1.0f); + + /** Predefined colors for the bounding box around creature sprite elements. */ + public static final EnumMap SPRITE_COLOR = new EnumMap(SegmentDef.SpriteType.class) {{ + put(SegmentDef.SpriteType.AVATAR, new Color(0x800000ff, true)); // blue + put(SegmentDef.SpriteType.WEAPON, new Color(0x80ff0000, true)); // red + put(SegmentDef.SpriteType.SHIELD, new Color(0x8000ff00, true)); // green + put(SegmentDef.SpriteType.HELMET, new Color(0x8000ffff, true)); // cyan + }}; + + /** Color definition as fallback solution. */ + public static final Color SPRITE_COLOR_DEFAULT = new Color(0x80808080, true); // gray + + private final BamV1Control bamControl; + private final SegmentDef segmentDef; + private final Point centerShift; + + public FrameInfo(BamV1Control bamControl, SegmentDef sd) + { + this(bamControl, sd, null); + } + + public FrameInfo(BamV1Control bamControl, SegmentDef sd, Point centerShift) + { + this.bamControl = Objects.requireNonNull(bamControl, "BAM controller cannot be null"); + this.segmentDef = Objects.requireNonNull(sd, "Segment definition cannot be null"); + this.centerShift = (centerShift != null) ? new Point(centerShift) : new Point(); + } + + /** Returns the BAM control instance. */ + public BamV1Control getController() { return bamControl; } + + public SegmentDef getSegmentDefinition() { return segmentDef; } + + /** Returns the absolute cycle index. */ + public int getCycle() { return segmentDef.getCycleIndex(); } + + /** Returns the frame index relative to the cycle. */ + public int getFrame() { return segmentDef.getCurrentFrame(); } + + /** Returns the amount of pixels the frame center deviates from the original position. */ + public Point getCenterShift() { return centerShift; } + + @Override + public String toString() + { + return "cycle=" + getCycle() + ", frame=" + getFrame() + ", centerShift=" + centerShift.toString(); + } + + @Override + public int hashCode() + { + int hash = 7; + hash = 31 * hash + ((bamControl == null) ? 0 : bamControl.hashCode()); + hash = 31 * hash + ((segmentDef == null) ? 0 : segmentDef.hashCode()); + hash = 31 * hash + ((centerShift == null) ? 0 : centerShift.hashCode()); + return hash; + } + + @Override + public boolean equals(Object o) + { + if (o == this) { + return true; + } + if (!(o instanceof FrameInfo)) { + return false; + } + FrameInfo other = (FrameInfo)o; + boolean retVal = (this.bamControl == null && other.bamControl == null) || + (this.bamControl != null && this.bamControl.equals(other.bamControl)); + retVal &= (this.segmentDef == null && other.segmentDef == null) || + (this.segmentDef != null && this.segmentDef.equals(other.segmentDef)); + retVal &= (this.centerShift == null && other.centerShift == null) || + (this.centerShift != null && this.centerShift.equals(other.centerShift)); + return retVal; + } +} diff --git a/src/org/infinity/resource/cre/decoder/util/ItemInfo.java b/src/org/infinity/resource/cre/decoder/util/ItemInfo.java new file mode 100644 index 000000000..7c9a4c4b8 --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/util/ItemInfo.java @@ -0,0 +1,1075 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.decoder.util; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Objects; +import java.util.function.Predicate; +import java.util.stream.Stream; + +import org.infinity.resource.Profile; +import org.infinity.resource.ResourceFactory; +import org.infinity.resource.key.ResourceEntry; +import org.infinity.util.DynamicArray; +import org.infinity.util.Misc; +import org.infinity.util.StringTable; +import org.infinity.util.io.StreamUtils; + +/** + * Provides useful information about equippable items. + */ +public class ItemInfo implements Comparable +{ + /** Generalized categories based on equipment slots. */ + public enum SlotType { + /** Helmet slot (PST: Right earring/lens/helmet) */ + HELMET, + /** Armor slot */ + ARMOR, + /** Shield slot (PST: left tattoo) */ + SHIELD, + /** Gloves slot (PST: hand) */ + GLOVES, + /** Left/right ring slot */ + RING, + /** Amulet slot (PST: Left earring/eyeball) */ + AMULET, + /** Belt slot (PST: Right lower tattoo) */ + BELT, + /** Boots slot */ + BOOTS, + /** Weapon slots */ + WEAPON, + /** Quiver slots */ + QUIVER, + /** Cloak slot (PST: Right upper tattoo) */ + CLOAK, + /** (PST only) Covers the following slots: {@code SHIELD}, {@code AMULET}, {@code CLOAK} */ + TATTOO, + /** (PST only) Covers the following slots: {@code HELMET}, {@code AMULET} */ + EARRING, + /** Quick item slot */ + QUICK_ITEM, + /** Generic inventory slot (default slot if none other are matching) */ + INVENTORY, + } + + /** + * This predicate simply returns {@code false} for all items. + */ + public static final ItemPredicate FILTER_NONE = (info) -> { return false; }; + + /** + * This predicate simply returns {@code true} for all items. + */ + public static final ItemPredicate FILTER_ALL = (info) -> { return true; }; + + /** + * This predicate returns {@code true} only if the item can be equipped in an equipment slot (except item slots). + */ + public static final ItemPredicate FILTER_EQUIPPABLE = (info) -> { + if ((info.getFlags() & (1 << 2)) == 0) { + // cleared bit 2 (droppable) indicates "meta-equipment" + return true; + } + switch (info.getCategory()) { + case 1: // Amulets + case 2: // Armor + case 3: // Belts + case 4: // Boots + case 5: // Arrows + case 6: // Bracers + case 7: // Headgear + case 10: // Rings + case 12: // Shields + case 14: // Bullets + case 15: // Bows + case 16: // Daggers + case 17: // Maces + case 18: // Slings + case 19: // Small swords + case 20: // Large swords + case 21: // Hammers + case 22: // Morning stars + case 23: // Flails + case 24: // Darts + case 25: // Axes + case 26: // Quarterstaves + case 27: // Crossbows + case 28: // Hand-to-hand weapons + case 29: // Spears + case 30: // Halberds + case 31: // Bolts + case 32: // Cloaks/Robes + case 39: // Tattoos + case 41: // Bucklers + case 44: // Clubs + case 47: // Large shields + case 49: // Medium shields + case 53: // Small shields + case 57: // Greatswords + case 60: // Leather armor + case 61: // Studded leather + case 62: // Chain mail + case 63: // Splint mail + case 64: // Plate mail + case 65: // Full plate + case 66: // Hide armor + case 67: // Robes + case 68: // Scale mail + case 69: // Bastard swords + case 70: // Scarves + case 72: // Hats + case 73: // Gloves + case 74: // Eyeballs + case 75: // Earrings + case 76: // Teeth + case 77: // Bracelets + return true; + default: + return false; + } + }; + + /** + * This predicate returns {@code true} only if the item can be equipped in a weapon slot. + */ + public static final ItemPredicate FILTER_WEAPON = (info) -> { + boolean retVal = FILTER_EQUIPPABLE.test(info); + retVal &= (info.getAbilityCount() > 0); + if (retVal) { + retVal &= (info.getAbility(0).getLocation() == 1); // weapon slot + retVal &= (info.getAbility(0).getLauncher() == 0); // no launcher required + } + return retVal; + }; + + /** + * This predicate returns {@code true} if the item is a two-handed weapon (melee or ranged). + */ + public static final ItemPredicate FILTER_WEAPON_2H = (info) -> { + boolean retVal = FILTER_WEAPON.test(info); + retVal &= (info.getAbilityCount() > 0); + if (retVal) { + int mask = Profile.isEnhancedEdition() ? 0x1002 : 0x2; // two-handed, fake two-handed + retVal &= (info.getFlags() & mask) != 0; + } + return retVal; + }; + + /** + * This predicate returns {@code true} only if the item can be placed in a weapon slot and the default ability + * is defined as melee type. + */ + public static final ItemPredicate FILTER_WEAPON_MELEE = (info) -> { + boolean retVal = FILTER_WEAPON.test(info); + retVal &= (info.getAbilityCount() > 0); + if (retVal) { + AbilityEntry ai = info.getAbility(0); + retVal = (ai.getAbilityType() == 1) && + (ai.getLauncher() == 0); + } + return retVal; + }; + + /** + * This predicate returns {@code true} only if {@link #FILTER_WEAPON_MELEE} passes the test and the item is flagged + * as two-handed or fake two-handed (e.g. monk fists). + */ + public static final ItemPredicate FILTER_WEAPON_MELEE_2H = (info) -> { + boolean retVal = FILTER_WEAPON_MELEE.test(info); + if (retVal) { + int mask = Profile.isEnhancedEdition() ? 0x1002 : 0x2; + retVal &= (info.getFlags() & mask) != 0; + } + return retVal; + }; + + /** + * This predicate returns {@code true} only if {@link #FILTER_WEAPON_MELEE} passes the test and the item can be + * equipped in the shield slot. + */ + public static final ItemPredicate FILTER_WEAPON_MELEE_LEFT_HANDED = (info) -> { + boolean retVal = FILTER_WEAPON_MELEE.test(info); + if (retVal) { + boolean isTwoHanded = (info.getFlags() & 2) != 0; + boolean allowLeftHanded = !Profile.isEnhancedEdition() || ((info.getFlags() & (1 << 13)) == 0); + retVal = !isTwoHanded && allowLeftHanded; + } + return retVal; + }; + + /** + * This predicate returns {@code true} only if the item can be placed in a weapon slot and the default ability + * is defined as ranged or launcher type. + */ + public static final ItemPredicate FILTER_WEAPON_RANGED = (info) -> { + boolean retVal = FILTER_WEAPON.test(info); + retVal &= (info.getAbilityCount() > 0); + if (retVal) { + AbilityEntry ai = info.getAbility(0); + retVal &= (ai.getLauncher() == 0); + retVal &= (ai.getAbilityType() == 2) || (ai.getAbilityType() == 4); + } + return retVal; + }; + + /** + * This predicate returns {@code true} only if the item can be placed in a weapon slot and is a + * ranged launcher-type weapon. + */ + public static final ItemPredicate FILTER_WEAPON_RANGED_LAUNCHER = (info) -> { + boolean retVal = FILTER_WEAPON.test(info); + retVal &= (info.getAbilityCount() > 0); + if (retVal) { + AbilityEntry ai = info.getAbility(0); + retVal &= (ai.getLauncher() == 0); + retVal &= (ai.getAbilityType() == 4); + } + return retVal; + }; + + /** + * This predicate returns {@code true} only if the item is a shield that can be placed in the shield slot. + */ + public static final ItemPredicate FILTER_SHIELD = (info) -> { + boolean retVal = FILTER_EQUIPPABLE.test(info); + if (retVal) { + switch (info.getCategory()) { + case 12: // Shields + case 41: // Bucklers + case 47: // Large shields + case 49: // Medium shields + case 53: // Small shields + break; + default: + retVal = false; + } + } + return retVal; + }; + + /** + * This predicate returns {@code true} only if the item can be placed in the armor slot. + */ + public static final ItemPredicate FILTER_ARMOR = (info) -> { + boolean retVal = FILTER_EQUIPPABLE.test(info); + if (retVal) { + switch (info.getCategory()) { + case 2: // Armor + case 60: // Leather armor + case 61: // Studded leather + case 62: // Chain mail + case 63: // Split mail + case 64: // Plate mail + case 65: // Full plate + case 66: // Hide armor + case 67: // Robes + case 68: // Scale mail + break; + default: + retVal = false; + } + } + return retVal; + }; + + /** + * This predicate returns {@code true} only if the item can be placed in the helmet slot. + */ + public static final ItemPredicate FILTER_HELMET = (info) -> { + boolean retVal = FILTER_EQUIPPABLE.test(info); + if (retVal) { + switch (info.getCategory()) { + case 7: // Headgear + case 72: // Hats + break; + default: + retVal = false; + } + } + return retVal; + }; + + /** Predefined {@code ItemInfo} structure without associated ITM resource. */ + public static final ItemInfo EMPTY = new ItemInfo(); + + private static final HashMap ITEM_CACHE = new HashMap<>(); + + private final EffectInfo effectInfo = new EffectInfo(); + private final List abilityEntries = new ArrayList<>(); + private final List effectsEntries = new ArrayList<>(); + private final ResourceEntry itmEntry; + + private String name; + private String nameIdentified; + private int flags; + private int category; + private int unusable; + private int unusableKits; + private String appearance; + private int proficiency; + private int stack; + private int enchantment; + + /** + * Returns the {@code ItemInfo} structure based on the specified item resource. Entries are retrieved from cache + * for improved performance. + * @param itmEntry the ITM {@code ResourceEntry} + * @return the {@code ItemInfo} structure with relevant item details. + * @throws Exception if the ITM resource could not be loaded. + */ + public static ItemInfo get(ResourceEntry itmEntry) throws Exception + { + ItemInfo retVal = null; + if (itmEntry == null) { + return EMPTY; + } + synchronized (ITEM_CACHE) { + retVal = ITEM_CACHE.get(itmEntry); + if (retVal == null) { + retVal = new ItemInfo(itmEntry); + ITEM_CACHE.put(itmEntry, retVal); + } + } + return retVal; + } + + /** + * Functions the same as {@link #get(ResourceEntry)} excepts that available cache entries will be updated with the + * new item data. + * @param itmEntry the ITM {@code ResourceEntry} + * @return the {@code ItemInfo} structure with relevant item details. + * @throws Exception if the ITM resource could not be loaded. + */ + public static ItemInfo getValidated(ResourceEntry itmEntry) throws Exception + { + ItemInfo retVal = null; + if (itmEntry == null) { + return EMPTY; + } + synchronized (ITEM_CACHE) { + retVal = new ItemInfo(itmEntry); + ITEM_CACHE.put(itmEntry, retVal); + } + return retVal; + } + + /** Clears the item cache. */ + public static void clearCache() + { + synchronized (ITEM_CACHE) { + ITEM_CACHE.clear(); + } + } + + /** + * Returns an {@code ItemInfo} list filtered by the specified predicate. + * @param pred the predicate used to decide whether an item is included in the returned list. + * Specify {@code null} to return all available items. + * @param sorted whether the returned list is sorted by {@code ResourceEntry} in ascending order. + * @return list of matching {@code ItemInfo} structures. + */ + public static List getItemList(ItemPredicate pred, boolean sorted) + { + List retVal = new ArrayList<>(); + + if (pred == null) { + pred = i -> true; + } + + List entries = ResourceFactory.getResources("ITM"); + for (Iterator iter = entries.iterator(); iter.hasNext(); ) { + try { + final ItemInfo ii = ItemInfo.get(iter.next()); + if (pred.test(ii)) { + retVal.add(ii); + } + } catch (Exception e) { + } + } + + if (sorted) { + Collections.sort(retVal); + } + + return retVal; + } + + /** + * Convenience method: Returns {@code true} if the given item passes all the specified tests. + * Returns {@code false} if info is {@code null}. + * Returns {@code true} if no predicate is specified. + */ + public static boolean testAll(ItemInfo info, ItemPredicate... pred) + { + boolean retVal = (info != null); + if (retVal) { + for (final ItemPredicate p : pred) { + if (p != null) { + retVal &= p.test(info); + } + if (!retVal) { + break; + } + } + } + return retVal; + } + + /** + * Convenience method: Returns {@code true} if the given item passes at least one of the specified tests. + * Returns {@code false} if info is {@code null}. + * Returns {@code true} if no predicate is specified. + */ + public static boolean testAny(ItemInfo info, ItemPredicate... pred) + { + boolean retVal = (info != null); + if (retVal && pred.length > 0) { + for (final ItemPredicate p : pred) { + if (p != null) { + retVal |= p.test(info); + } + if (retVal) { + break; + } + } + } + return retVal; + } + + /** + * This is a convenience method to speed up the process. + * Returns the category of the specified item resource. Returns -1 if category could not be determined. + */ + public static int getItemCategory(ResourceEntry itmEntry) + { + if (itmEntry != null) { + try (final InputStream is = itmEntry.getResourceDataAsStream()) { + byte[] sig = new byte[8]; + Misc.requireCondition(is.read(sig) == 8, "Could not read signature field"); + final String signature = new String(sig).toUpperCase(Locale.ENGLISH); + switch (signature) { + case "ITM V1 ": + case "ITM V1.1": + case "ITM V2.0": + break; + default: + throw new Exception("Not an item resource: " + itmEntry.getResourceName()); + } + Misc.requireCondition(is.skip(0x14) == 0x14, "Could not advance in data stream"); + return StreamUtils.readShort(is); + } catch (Exception e) { + e.printStackTrace(); + } + } + return -1; + } + + private ItemInfo() + { + this.itmEntry = null; + initDefault(); + } + + private ItemInfo(ResourceEntry itmEntry) throws Exception + { + this.itmEntry = itmEntry; + init(); + } + + /** Returns whether this {@code ItemInfo} instance contains a valid item. */ + public boolean isEmpty() { return (itmEntry == null); } + + /** Returns the {@code ResourceEntry} instance of the item resource. */ + public ResourceEntry getResourceEntry() { return itmEntry; } + + /** Returns the general name of the item. */ + public String getName() { return name; } + + /** Returns the identified name of the item. */ + public String getIdentifiedName() { return nameIdentified; } + + /** Returns the item flags. */ + public int getFlags() { return flags; } + + /** + * Updates the undroppable flag (bit 2) of the item. The undroppable flag can be overridden by CRE item structures. + * @param override specify whether droppable flag should be overridden. + */ + public void overrideDroppableFlag(boolean override) + { + if (override) { + flags &= ~(1 << 2); // Note: item flags specify "droppable" bit + } + } + + /** Returns the item category. */ + public int getCategory() { return category; } + + /** Returns the general usability flags. */ + public int getUnusable() { return unusable; } + + /** Returns the kit-specific usability flags (packed value, kits #1 at bit 24, ..., kits #4 at bit 0). */ + public int getUnusableKits() { return unusableKits; } + + /** Returns the two-letter appearance code. */ + public String getAppearance() { return appearance; } + + /** Returns the proficiency id associated with the item. */ + public int getProficiency() { return proficiency; } + + /** Returns the max. stack amount available for the the item. */ + public int getStack() { return stack; } + + /** Returns the enchantment value of the item. */ + public int getEnchantment() { return enchantment; } + + /** Provides access to the {@link EffectInfo} instance associated with the item. */ + public EffectInfo getEffectInfo() { return effectInfo; } + + /** Returns number of defined item abilities. */ + public int getAbilityCount() { return abilityEntries.size(); } + + /** Returns the specified ability structure. */ + public AbilityEntry getAbility(int index) throws IndexOutOfBoundsException { return abilityEntries.get(index); } + + /** Returns a sequential {@link Stream} of the {@code AbilityInfo} list. */ + public Stream getAbilityStream() { return abilityEntries.stream(); } + + /** Returns the type of the specified ability. */ + public int getAbilityType(int index) { return (index >= 0 && index < abilityEntries.size()) ? abilityEntries.get(index).getAbilityType() : -1; } + + /** Returns the number of defined global item effects. */ + public int getGlobalEffectsCount() { return effectsEntries.size(); } + + /** Returns the specified global item effect. */ + public EffectEntry getGlobalEffect(int index) throws IndexOutOfBoundsException { return effectsEntries.get(index); } + + /** Returns a sequential {@link Stream} of the {@code EffectInfo} list. */ + public Stream getEffectStream() { return effectsEntries.stream(); } + + /** Returns the most suitable item slot type compatible with the current item. */ + public SlotType getSlotType() + { + return getSlotType(getCategory()); + } + + /** Returns the most suitable item slot type compatible with the specified item category. */ + public static SlotType getSlotType(int category) + { + switch (category) { + case 7: // headgear + case 40: // lenses + case 72: // hats + return SlotType.HELMET; + case 2: // armor + case 60: // leather armor + case 61: // studded leather + case 62: // chain mail + case 63: // splint mail + case 64: // plate mail + case 65: // full plate + case 66: // hide armor + case 67: // robes + case 68: // scale mail + return SlotType.ARMOR; + case 12: // shields + case 41: // bucklers + case 47: // large shields + case 49: // medium shields + case 53: // small shields + return SlotType.SHIELD; + case 6: // bracers/gauntlets + case 70: // scarves + case 73: // gloves + case 77: // bracelets + return SlotType.GLOVES; + case 10: // rings + return SlotType.RING; + case 1: // amulets + case 74: // eyeballs + return SlotType.AMULET; + case 3: // belts + return SlotType.BELT; + case 4: // boots + return SlotType.BOOTS; + case 15: // bows + case 16: // daggers + case 17: // maces + case 18: // slings + case 19: // small swords + case 20: // large swords + case 21: // hammers + case 22: // morning stars + case 23: // flails + case 24: // darts + case 25: // axes + case 26: // quarterstaves + case 27: // crossbows + case 28: // hand-to-hand weapons + case 29: // spears + case 30: // halberds + case 44: // clubs + case 57: // greatswords + case 69: // bastard swords + case 76: // teeth + return SlotType.WEAPON; + case 5: // arrows + case 14: // bullets + case 31: // bolts + return SlotType.QUIVER; + case 32: // cloaks and robes + return SlotType.CLOAK; + case 39: // tattoos + return SlotType.TATTOO; + case 9: // potions + case 11: // scrolls + case 35: // wands + return SlotType.QUICK_ITEM; + case 75: // earrings + return SlotType.EARRING; + default: + return SlotType.INVENTORY; + } + } + + /** Invoked for {@code null} items. */ + private void initDefault() + { + name = nameIdentified = appearance = ""; + flags = category = unusable = unusableKits = proficiency = stack = enchantment = 0; + } + + /** Initializes relevant item attributes. */ + private void init() throws IOException, Exception + { + if (itmEntry == null) { + initDefault(); + return; + } + + try (final InputStream is = itmEntry.getResourceDataAsStream()) { + byte[] sig = new byte[8]; + // offset = 0x00 + Misc.requireCondition(is.read(sig) == 8, "Could not read signature field: " + itmEntry); + final String signature = new String(sig).toUpperCase(Locale.ENGLISH); + switch (signature) { + case "ITM V1 ": + case "ITM V1.1": + case "ITM V2.0": + break; + default: + throw new Exception("Not an item resource: " + itmEntry.getResourceName()); + } + + // general name + int strref = StreamUtils.readInt(is); + this.name = StringTable.isValidStringRef(strref) ? StringTable.getStringRef(strref) : ""; + + // identified name + strref = StreamUtils.readInt(is); + this.nameIdentified = StringTable.isValidStringRef(strref) ? StringTable.getStringRef(strref) : ""; + + if (this.nameIdentified.isEmpty()) { + this.nameIdentified = this.name; + } else if (this.name.isEmpty()) { + this.name = this.nameIdentified; + } + + // flags (common) + Misc.requireCondition(is.skip(0x8) == 0x8, "Could not advance in data stream: " + itmEntry); + // offset = 0x18 + this.flags = StreamUtils.readInt(is); + + // category (common) + // offset = 0x1c + this.category = StreamUtils.readShort(is); + + // unusable (common) + // offset = 0x1e + this.unusable = StreamUtils.readInt(is); + + // appearance (common) + // offset = 0x22 + this.appearance = StreamUtils.readString(is, 2); + + // unusableKits and proficiency (common for V1/V2.0 only) + this.unusableKits = 0; + if (!"ITM V1.1".equals(signature)) { + int v; + Misc.requireCondition(is.skip(5) == 5, "Could not advance in data stream: " + itmEntry); + // offset = 0x29 + Misc.requireCondition((v = is.read()) != -1, "Could not read kits usability field: " + itmEntry); + this.unusableKits |= (v << 24); + Misc.requireCondition(is.skip(1) == 1, "Could not advance in data stream: " + itmEntry); + // offset = 0x2b + Misc.requireCondition((v = is.read()) != -1, "Could not read kits usability field: " + itmEntry); + this.unusableKits |= (v << 16); + Misc.requireCondition(is.skip(1) == 1, "Could not advance in data stream: " + itmEntry); + // offset = 0x2d + Misc.requireCondition((v = is.read()) != -1, "Could not read kits usability field: " + itmEntry); + this.unusableKits |= (v << 8); + Misc.requireCondition(is.skip(1) == 1, "Could not advance in data stream: " + itmEntry); + // offset = 0x2f + Misc.requireCondition((v = is.read()) != -1, "Could not read kits usability field: " + itmEntry); + this.unusableKits |= v; + + // proficiency + Misc.requireCondition(is.skip(0x1) == 0x1, "Could not advance in data stream: " + itmEntry); + this.proficiency = is.read(); + } else { + // to keep stream position synchronized + Misc.requireCondition(is.skip(0xe) == 0xe, "Could not advance in data stream: " + itmEntry); + } + + // stack amount + Misc.requireCondition(is.skip(0x6) == 0x6, "Could not advance in data stream: " + itmEntry); + this.stack = StreamUtils.readShort(is); + + // enchantment + Misc.requireCondition(is.skip(0x26) == 0x26, "Could not advance in data stream: " + itmEntry); + this.enchantment = StreamUtils.readInt(is); + + // abilities (common: ofs, num, header) + // offset = 0x64 + int ofsAbil = StreamUtils.readInt(is); // abilities offset + int numAbil = StreamUtils.readShort(is); // abilities count + int ofsFx = StreamUtils.readInt(is); // effects offset + int idxFx = StreamUtils.readShort(is); // start index of global effects + int numFx = StreamUtils.readShort(is); // total effects count + ofsFx += idxFx * 0x30; // adjusting global effects offset + numFx -= idxFx; // adjusting global effects count + int curOfs = 0x72; // end of main structure + byte[] effect = new byte[48]; // buffer for effect entry + byte[] ability = new byte[56]; // buffer for ability entry + + // reading global color effects (attempt 1) + int skip; + if (numFx > 0 && ofsFx < ofsAbil) { + skip = ofsFx - curOfs; + Misc.requireCondition(is.skip(skip) == skip, "Could not advance in data stream: " + itmEntry); + curOfs += skip; + // offset = [ofsFx] + for (int i = 0; i < numFx; i++) { + Misc.requireCondition(is.read(effect) == effect.length, "Could not read effect " + i + ": " + itmEntry); + curOfs += effect.length; + EffectEntry ei = new EffectEntry(effect); + effectsEntries.add(ei); + parseEffect(effect); + } + } + + // reading ability types + if (numAbil > 0) { + skip = ofsAbil - curOfs; + Misc.requireCondition(skip >= 0 && is.skip(skip) == skip, "Could not advance in data stream: " + itmEntry); + curOfs += skip; + // offset = [ofsAbil] + for (int i = 0; i < numAbil; i++) { + Misc.requireCondition(is.read(ability) == ability.length, "Could not read ability " + i + ": " + itmEntry); + curOfs += ability.length; + abilityEntries.add(new AbilityEntry(ability)); + } + } + + // reading global color effects (attempt 2) + if (numFx > 0 && ofsFx >= ofsAbil) { + skip = ofsFx - curOfs; + Misc.requireCondition(skip >= 0 && is.skip(skip) == skip, "Could not advance in data stream: " + itmEntry); + curOfs += skip; + // offset = [ofsFx] + for (int i = 0; i < numFx; i++) { + Misc.requireCondition(is.read(effect) == effect.length, "Could not read effect " + i + ": " + itmEntry); + curOfs += effect.length; + EffectEntry ei = new EffectEntry(effect); + effectsEntries.add(ei); + parseEffect(effect); + } + } + } + } + + // Processes a global effect + private void parseEffect(byte[] data) + { + getEffectInfo().add(EffectInfo.Effect.fromEffectV1(ByteBuffer.wrap(data), 0)); + } + +//--------------------- Begin Interface Comparable --------------------- + + @Override + public int compareTo(ItemInfo o) + { + if (itmEntry == null) { + return (o.itmEntry == null) ? 0 : -1; + } else { + return (o.itmEntry == null) ? 1 : itmEntry.compareTo(o.itmEntry); + } + } + +//--------------------- End Interface Comparable --------------------- + + @Override + public int hashCode() + { + int hash = 7; + hash = 31 * hash + effectInfo.hashCode(); + hash = 31 * hash + abilityEntries.hashCode(); + hash = 31 * hash + effectsEntries.hashCode(); + hash = 31 * hash + ((itmEntry == null) ? 0 : itmEntry.hashCode()); + hash = 31 * hash + ((name == null) ? 0 : name.hashCode()); + hash = 31 * hash + ((nameIdentified == null) ? 0 : nameIdentified.hashCode()); + hash = 31 * hash + flags; + hash = 31 * hash + category; + hash = 31 * hash + unusable; + hash = 31 * hash + unusableKits; + hash = 31 * hash + ((appearance == null) ? 0 : appearance.hashCode()); + hash = 31 * hash + proficiency; + hash = 31 * hash + stack; + hash = 31 * hash + enchantment; + return hash; + } + + @Override + public boolean equals(Object o) + { + if (o == this) { + return true; + } + if (!(o instanceof ItemInfo)) { + return false; + } + ItemInfo other = (ItemInfo)o; + boolean retVal = (this.effectInfo == null && other.effectInfo == null) || + (this.effectInfo != null && this.effectInfo.equals(other.effectInfo)); + retVal &= (this.abilityEntries == null && other.abilityEntries == null) || + (this.abilityEntries != null && this.abilityEntries.equals(other.abilityEntries)); + retVal &= (this.effectsEntries == null && other.effectsEntries == null) || + (this.effectsEntries != null && this.effectsEntries.equals(other.effectsEntries)); + retVal &= (this.itmEntry == null && other.itmEntry == null) || + (this.itmEntry != null && this.itmEntry.equals(other.itmEntry)); + retVal &= (this.name == null && other.name == null) || + (this.name != null && this.name.equals(other.name)); + retVal &= (this.nameIdentified == null && other.nameIdentified == null) || + (this.nameIdentified != null && this.nameIdentified.equals(other.nameIdentified)); + retVal &= this.flags == other.flags; + retVal &= this.category == other.category; + retVal &= this.unusable == other.unusable; + retVal &= this.unusableKits == other.unusableKits; + retVal &= (this.appearance == null && other.appearance == null) || + (this.appearance != null && this.appearance.equals(other.appearance)); + retVal &= this.proficiency == other.proficiency; + retVal &= this.stack == other.stack; + retVal &= this.enchantment == other.enchantment; + return retVal; + } + + @Override + public String toString() + { + if (isEmpty()) { + return "None"; + } else if (getIdentifiedName().isEmpty()) { + return getResourceEntry().getResourceName(); + } else { + String name = getIdentifiedName(); + if (name.length() > 80) { + name = name.substring(0, 80) + "..."; + } + return getResourceEntry().getResourceName() + " (" + name + ")"; + } + } + +//-------------------------- INNER CLASSES -------------------------- + + /** Storage class for relevant ability attributes. */ + public static class AbilityEntry + { + private final int type; + private final int location; + private final int target; + private final int launcher; + private final int damageType; + private final int flags; + private final int projectile; + private final int probabilitySlash; + private final int probabilityBackslash; + private final int probabilityJab; + private final boolean isArrow; + private final boolean isBolt; + private final boolean isBullet; + + /** Parses the item ability structure described by the byte array. */ + private AbilityEntry(byte[] ability) + { + Objects.requireNonNull(ability); + DynamicArray buf = DynamicArray.wrap(ability, DynamicArray.ElementType.BYTE); + this.type = buf.getByte(0x0); + this.location = buf.getByte(0x2); + this.target = buf.getByte(0xc); + this.launcher = buf.getByte(0x10); + this.damageType = buf.getShort(0x1c); + this.flags = buf.getInt(0x26); + this.projectile = buf.getShort(0x2a); + this.probabilitySlash = buf.getShort(0x2c); + this.probabilityBackslash = buf.getShort(0x2e); + this.probabilityJab = buf.getShort(0x30); + this.isArrow = buf.getShort(0x32) != 0; + this.isBolt = buf.getShort(0x34) != 0; + this.isBullet = buf.getShort(0x36) != 0; + } + + /** Returns the ability type (1=melee, 2=ranged, 3=magical, 4=launcher). */ + public int getAbilityType() { return type; } + + /** Returns the ability location slot (1=weapon, 2=spell, 3=item, 4=ability). */ + public int getLocation() { return location; } + + /** Returns the target (1=living actor, 2=inventory, 3=dead actor, 4=any point, 5=caster, 7=caster immediately). */ + public int getTarget() { return target; } + + /** Returns the required launcher type (0=none, 1=bow, 2=crossbow, 3=sling). */ + public int getLauncher() { return launcher; } + + /** + * Returns the damage type (1=piercing, 2=crushing, 3=slashing, 4=missile, 5=fist, 6=piercing/crushing, + * 7=piercing/slashing, 8=crushing/slashing, 9=blunt missile). + */ + public int getDamageType() { return damageType; } + + /** Returns ability flags. */ + public int getFlags() { return flags; } + + /** Returns the projectile id. */ + public int getProjectile() { return projectile; } + + /** Returns the probability of triggering the slash attack animation. */ + public int getProbabilitySlash() { return probabilitySlash; } + + /** Returns the probability of triggering the backslash attack animation. */ + public int getProbabilityBackslash() { return probabilityBackslash; } + + /** Returns the probability of triggering the jab attack animation. */ + public int getProbabilityJab() { return probabilityJab; } + + /** Indicates whether the ability is an arrow. */ + public boolean isArrow() { return this.isArrow; } + + /** Indicates whether the ability is a bolt. */ + public boolean isBolt() { return this.isBolt; } + + /** Indicates whether the ability is a bullet. */ + public boolean isBullet() { return this.isBullet; } + } + + /** Storage class for relevant global effects attributes. */ + public static class EffectEntry + { + private final int opcode; + private final int target; + private final int power; + private final int parameter1; + private final int parameter2; + private final int timing; + private final int dispelResist; + private final int duration; + private final String resource; + private final int savingThrowFlags; + private final int savingThrow; + private final int special; + + /** Parses the EFF V1 structure described by the byte array. */ + private EffectEntry(byte[] effect) + { + Objects.requireNonNull(effect); + DynamicArray buf = DynamicArray.wrap(effect, DynamicArray.ElementType.BYTE); + this.opcode = buf.getShort(0x0); + this.target = buf.getByte(0x2); + this.power = buf.getByte(0x3); + this.parameter1 = buf.getInt(0x4); + this.parameter2 = buf.getInt(0x8); + this.timing = buf.getByte(0xc); + this.dispelResist = buf.getByte(0xd); + this.duration = buf.getByte(0xe); + this.resource = DynamicArray.getString(effect, 0x14, 8, Charset.forName("windows-1252")); + this.savingThrowFlags = buf.getInt(0x24); + this.savingThrow = buf.getInt(0x28); + this.special = buf.getInt(0x2c); + } + + /** Returns the effect opcode. */ + public int getOpcode() { return opcode; } + + /** Returns the effect target. */ + public int getTarget() { return target; } + + /** Returns the effect power. */ + public int getPower() { return power; } + + /** Returns parameter1 of the effect (meaning depends on opcode). */ + public int getParameter1() { return parameter1; } + + /** Returns parameter2 of the effect (meaning depends on opcode). */ + public int getParameter2() { return parameter2; } + + /** Returns the timing mode. */ + public int getTiming() { return timing; } + + /** Returns the dispel and resistance mode. */ + public int getDispelResist() { return dispelResist; } + + /** Returns the effect duration. */ + public int getDuration() { return duration; } + + /** Returns the resource resref of the effect. Returns empty string otherwise. */ + public String getResource() { return resource; } + + /** Returns the saving throw flags. */ + public int getSavingThrowFlags() { return savingThrowFlags; } + + /** Returns the saving throw bonus. */ + public int getSavingThrow() { return savingThrow; } + + /** Returns the special parameter used by selected effects. */ + public int getSpecial() { return special; } + } + + /** + * Represents a {@link Predicate} about an {@code ItemInfo} object. + */ + public interface ItemPredicate extends Predicate + { + @Override + boolean test(ItemInfo info); + + default ItemPredicate and(ItemPredicate other) + { + Objects.requireNonNull(other); + return (t) -> test(t) && other.test(t); + } + + @Override + default ItemPredicate negate() + { + return (t) -> !test(t); + } + + default ItemPredicate or(ItemPredicate other) + { + Objects.requireNonNull(other); + return (t) -> test(t) || other.test(t); + } + } +} diff --git a/src/org/infinity/resource/cre/decoder/util/NumberRange.java b/src/org/infinity/resource/cre/decoder/util/NumberRange.java new file mode 100644 index 000000000..34b14c7f6 --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/util/NumberRange.java @@ -0,0 +1,82 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.decoder.util; + +import java.util.ArrayList; +import java.util.List; + +import org.infinity.util.tuples.Couple; + +/** + * A class that allows you to define numeric ranges. + */ +public class NumberRange +{ + private final List> ranges = new ArrayList<>(); + + /** + * Defines a range that starts at base and ends at base + range (inclusive). + * @param base start value of the range. + * @param range length of the range. + */ + public NumberRange(int base, int range) + { + this(base, range, 0, 0, 0); + } + + /** + * Defines a set of common ranges. The resolved list of ranges can be defined as:

    + * base + ([subBase, subBase+subRange] << subPos) + [0, range]

    + * where [x, y] defines a range from x to y (inclusive). + * @param base start value of the range. + * @param range length of the range. + */ + public NumberRange(int base, int range, int subBase, int subRange, int subPos) + { + init(base, range, subBase, subRange, subPos); + } + + /** Returns whether the range covers the specified value. */ + public boolean contains(int value) + { + return ranges + .parallelStream() + .anyMatch(c -> (value >= c.getValue0().intValue() && + value <= (c.getValue0().intValue() + c.getValue1().intValue()))); + } + + private void init(int base, int range, int subBase, int subRange, int subPos) + { + range = Math.abs(range); + subRange = Math.abs(subRange); + subPos = Math.max(0, Math.min(32, subPos)); + for (int i = 0; i <= subRange; i++) { + int curBase = base + ((subBase + i) << subPos); + ranges.add(Couple.with(curBase, range)); + } + } + + @Override + public int hashCode() + { + int hash = 7; + hash = 31 * hash + ((ranges == null) ? 0 : ranges.hashCode()); + return hash; + } + + @Override + public boolean equals(Object o) + { + if (o == this) { + return true; + } + if (!(o instanceof NumberRange)) { + return false; + } + NumberRange other = (NumberRange)o; + boolean retVal = ranges.equals(other.ranges); + return retVal; + } +} diff --git a/src/org/infinity/resource/cre/decoder/util/SegmentDef.java b/src/org/infinity/resource/cre/decoder/util/SegmentDef.java new file mode 100644 index 000000000..08e120400 --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/util/SegmentDef.java @@ -0,0 +1,393 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.decoder.util; + +import java.awt.AlphaComposite; +import java.awt.Composite; +import java.util.EnumMap; +import java.util.Objects; + +import org.infinity.resource.key.ResourceEntry; + +/** + * Definition of a single segment within a cycle definition. + */ +public class SegmentDef implements Cloneable +{ + /** Indicates the sprite type for this particular BAM segment (such as avatar, weapon, shield or helmet). */ + public enum SpriteType { + /** Indicate that the current segment belongs to the creature animation. */ + AVATAR, + /** Indicate that the current segment belongs to the weapon overlay. */ + WEAPON, + /** Indicate that the current segment belongs to the shield overlay. */ + SHIELD, + /** Indicate that the current segment belongs to the helmet overlay. */ + HELMET, + } + + /** + * Indicates the playback behavior of this particular BAM segment. + */ + public enum Behavior { + /** + * Segment runs from start to end and repeats the process if other segments of the current cycle contain + * more frames. This is the default if no behavior has been specified. + */ + REPEAT, + /** + * Segment runs from start to end and stops even if other segments of the current cycle contain more frames. + */ + SINGLE, + /** + * Segment runs from start to end and continues to provide the last frame if other segments of the + * current cycle contain more frames. + */ + FREEZE, + /** + * Segment runs from start to the minimum number of cycle frames available in all segment definitions and stops. + */ + CUT, + /** The same as {@link #REPEAT} but runs from end to start. */ + REVERSE_REPEAT, + /** The same as {@link #SINGLE} but runs from end to start. */ + REVERSE_SINGLE, + /** The same as {@link #FREEZE} but runs from end to start. */ + REVERSE_FREEZE, + /** The same as {@link #CUT} but runs from end to start. */ + REVERSE_CUT; + + private static final EnumMap opposites = new EnumMap(Behavior.class) {{ + put(REPEAT, REVERSE_REPEAT); + put(SINGLE, REVERSE_SINGLE); + put(FREEZE, REVERSE_FREEZE); + put(CUT, REVERSE_CUT); + put(REVERSE_REPEAT, REPEAT); + put(REVERSE_SINGLE, SINGLE); + put(REVERSE_FREEZE, FREEZE); + put(REVERSE_CUT, CUT); + }}; + + /** Returns the opposite value of the specified behavior regarding frame playback. */ + public static Behavior getOpposite(Behavior b) + { + return opposites.getOrDefault(b, REVERSE_REPEAT); + } + } + + private final ResourceEntry bamEntry; + private final int cycleIndex; + private final SpriteType type; + private final Behavior behavior; + private final int numFrames; + private final Composite composite; + + private CycleDef parent; + private int curFrame; + + /** + * Determines the behavior variant specified by the given BAM animation suffix. + * If an exclamation mark is found it returns the opposite of the default behavior. + * Otherwise it will return the default behavior. + */ + public static Behavior getBehaviorOf(String suffix) + { + return getBehaviorOf(suffix, null); + } + + /** + * Determines the behavior variant specified by the given BAM animation suffix. + * If an exclamation mark is found it returns the opposite of the specified behavior. + * Otherwise it will return the specified behavior. + */ + public static Behavior getBehaviorOf(String suffix, Behavior behavior) + { + Behavior retVal = (behavior != null) ? behavior : Behavior.REPEAT; + if (suffix != null && suffix.indexOf('!') >= 0) { + retVal = Behavior.getOpposite(retVal); + } + return retVal; + } + + /** Returns the specified string without any behavior markers. */ + public static String fixBehaviorSuffix(String suffix) + { + String retVal = suffix; + if (retVal != null && retVal.indexOf('!') >= 0) { + retVal = retVal.replace("!", ""); + } + return retVal; + } + + /** + * Creates a new independent segment definition with the specified parameters. + * Behavior is assumed to be {@link Behavior#REPEAT}. Composite object is initialized with {@link AlphaComposite#SrcOver}. + * @param entry the BAM resource + * @param cycleIdx the BAM cycle index + * @param type the sprite type of this segment. + */ + public SegmentDef(ResourceEntry entry, int cycleIdx, SpriteType type) + { + this(null, entry, cycleIdx, type, null, null); + } + + /** + * Creates a new independent segment definition with the specified parameters. + * Composite object is initialized with {@link AlphaComposite#SrcOver}. + * @param entry the BAM resource + * @param cycleIdx the BAM cycle index + * @param type the sprite type of this segment. + * @param behavior the playback behavior of this segment. + */ + public SegmentDef(ResourceEntry entry, int cycleIdx, SpriteType type, Behavior behavior) + { + this(null, entry, cycleIdx, type, behavior, null); + } + + /** + * Creates a new independent segment definition with the specified parameters. + * @param entry the BAM resource + * @param cycleIdx the BAM cycle index + * @param type the sprite type of this segment. + * @param behavior the playback behavior of this segment. + * @param composite the {@link Composite} object used for rendering sprite frames onto the canvas. + */ + public SegmentDef(ResourceEntry entry, int cycleIdx, SpriteType type, Behavior behavior, Composite composite) + { + this(null, entry, cycleIdx, type, behavior, composite); + } + + /** + * Creates a new segment definition with the specified parameters linked to the specified {@link CycleDef} instance. + * Behavior is assumed to be {@link Behavior#REPEAT}. Composite object is initialized with {@link AlphaComposite#SrcOver}. + * @param parent the parent {@code CycleDef} instance. + * @param entry the BAM resource + * @param cycleIdx the BAM cycle index + * @param type the sprite type of this segment. + */ + public SegmentDef(CycleDef parent, ResourceEntry entry, int cycleIdx, SpriteType type) + { + this(parent, entry, cycleIdx, type, null, null); + } + + /** + * Creates a new segment definition with the specified parameters linked to the specified {@link CycleDef} instance. + * Composite object is initialized with {@link AlphaComposite#SrcOver}. + * @param parent the parent {@code CycleDef} instance. + * @param entry the BAM resource + * @param cycleIdx the BAM cycle index + * @param type the spriter type of this segment. + * @param behavior the playback behavior of this segment. + */ + public SegmentDef(CycleDef parent, ResourceEntry entry, int cycleIdx, SpriteType type, Behavior behavior) + { + this(parent, entry, cycleIdx, type, behavior, null); + } + + /** + * Creates a new segment definition with the specified parameters linked to the specified {@link CycleDef} instance. + * @param parent the parent {@code CycleDef} instance. + * @param entry the BAM resource + * @param cycleIdx the BAM cycle index + * @param type the spriter type of this segment. + * @param behavior the playback behavior of this segment. + * @param composite the {@link Composite} object used for rendering sprite frames onto the canvas. + */ + public SegmentDef(CycleDef parent, ResourceEntry entry, int cycleIdx, SpriteType type, Behavior behavior, Composite composite) + { + this.parent = parent; + this.bamEntry = Objects.requireNonNull(entry, "BAM resource entry cannot be null"); + this.cycleIndex = cycleIdx; + this.type = (type != null) ? type : SpriteType.AVATAR; + this.behavior = (behavior != null) ? behavior : Behavior.REPEAT; + this.numFrames = SpriteUtils.getBamCycleFrames(this.bamEntry, this.cycleIndex); + this.composite = (composite != null) ? composite : AlphaComposite.SrcOver; + reset(); + } + + /** + * Creates a new segment definition with the attributes defined in the specified {@code SegmentDef} argument. + * Parent attribute is set to {@code null}. + * @param sd the {@code SegmentDef} object to clone. + */ + public SegmentDef(SegmentDef sd) + { + Objects.requireNonNull(sd, "SegmentDef instance cannot be null"); + this.parent = null; + this.bamEntry = sd.bamEntry; + this.cycleIndex = sd.cycleIndex; + this.type = sd.type; + this.behavior = sd.behavior; + this.numFrames = sd.numFrames; + this.curFrame = sd.curFrame; + this.composite = sd.composite; + } + + /** Returns the parent {@link CycleDef} instance linked with this object. */ + public CycleDef getParent() { return parent; } + + /** Updates the parent link. Should not be called directly. */ + void setParent(CycleDef parent) { this.parent = parent; } + + /** Returns the BAM resource entry associated with the segment. */ + public ResourceEntry getEntry() { return bamEntry; } + + /** Returns the {@link Composite} object used to render the sprite frames onto a canvas. */ + public Composite getComposite() { return composite; } + + /** Returns the cycle index associated with the segment */ + public int getCycleIndex() { return cycleIndex; } + + /** Returns the sprite type assigned to the segment. */ + public SpriteType getSpriteType() { return type; } + + /** Returns the playback behavior for the current segment. */ + public Behavior getBehavior() { return behavior; } + + /** Returns the total number of frames in this cycle. */ + public int getFrameCount() { return numFrames; } + + /** Returns the current cycle frame. Returns -1 if no frame is currently selected. */ + public int getCurrentFrame() { return curFrame; } + + /** Advances the segment animation by one frame according to {@link Behavior}. */ + public void advance() + { + switch (behavior) { + case SINGLE: + if (curFrame >= 0 && curFrame < numFrames - 1) { + curFrame++; + } else { + curFrame = -1; + } + break; + case FREEZE: + if (curFrame < numFrames - 1) { + curFrame++; + } + break; + case CUT: + if (curFrame < getMinimumFrames() - 1) { + curFrame++; + } else { + curFrame = -1; + } + break; + case REVERSE_REPEAT: + if (curFrame > 0) { + curFrame--; + } else if (curFrame <= 0) { + curFrame = numFrames - 1; + } else if (curFrame >= getMaximumFrames() - 1) { + curFrame = numFrames - 1; + } + break; + case REVERSE_SINGLE: + if (curFrame > 0 && curFrame < numFrames) { + curFrame--; + } else { + curFrame = -1; + } + break; + case REVERSE_FREEZE: + if (curFrame > 0) { + curFrame--; + } else { + curFrame = -1; + } + break; + case REVERSE_CUT: + if (curFrame >= numFrames - getMinimumFrames() + 1) { + curFrame--; + } else { + curFrame = -1; + } + break; + default: // Repeat + if (curFrame < numFrames - 1) { + curFrame++; + } else if (curFrame >= numFrames - 1) { + curFrame = 0; + } else if (curFrame >= getMaximumFrames() - 1) { + curFrame = 0; + } + } + } + + /** Resets the BAM cycle back to the "first" frame as defined by {@link Behavior}. */ + public void reset() + { + switch (behavior) { + case REVERSE_REPEAT: + case REVERSE_SINGLE: + case REVERSE_FREEZE: + case REVERSE_CUT: + curFrame = numFrames - 1; + break; + default: + curFrame = 0; + } + } + + @Override + public SegmentDef clone() + { + return new SegmentDef(this); + } + + @Override + public String toString() + { + return "entry=" + bamEntry.toString() + ", cycle=" + cycleIndex + ", type=" + type.toString() + + ", behavior=" + behavior.toString() + ", numFrames=" + numFrames + ", curFrame=" + curFrame; + } + + @Override + public int hashCode() + { + int hash = 7; + hash = 31 * hash + ((bamEntry == null) ? 0 : bamEntry.hashCode()); + hash = 31 * hash + cycleIndex; + hash = 31 * hash + ((type == null) ? 0 : type.hashCode()); + hash = 31 * hash + ((behavior == null) ? 0 : behavior.hashCode()); + hash = 31 * hash + numFrames; + hash = 31 * hash + curFrame; + return hash; + } + + @Override + public boolean equals(Object o) + { + if (o == this) { + return true; + } + if (!(o instanceof SegmentDef)) { + return false; + } + SegmentDef other = (SegmentDef)o; + boolean retVal = (this.bamEntry == null && other.bamEntry == null) || + (this.bamEntry != null && this.bamEntry.equals(other.bamEntry)); + retVal &= (this.cycleIndex == other.cycleIndex); + retVal &= (this.type == null && other.type == null) || + (this.type != null && this.type.equals(other.type)); + retVal &= (this.behavior == null && other.behavior == null) || + (this.behavior != null && this.behavior.equals(other.behavior)); + retVal &= (this.numFrames == other.numFrames); + retVal &= (this.curFrame == other.curFrame); + return retVal; + } + + // Determines the minimum number of frames in the whole list of segment definitions. + private int getMinimumFrames() + { + return (parent != null) ? parent.getMinimumFrames() : 0; + } + + // Determines the maximum number of frames in the whole list of segment definitions. + private int getMaximumFrames() + { + return (parent != null) ? parent.getMaximumFrames() : numFrames; + } +} diff --git a/src/org/infinity/resource/cre/decoder/util/SeqDef.java b/src/org/infinity/resource/cre/decoder/util/SeqDef.java new file mode 100644 index 000000000..bb464f7ca --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/util/SeqDef.java @@ -0,0 +1,255 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.decoder.util; + +import java.awt.AlphaComposite; +import java.awt.Composite; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; + +import org.infinity.resource.cre.decoder.SpriteDecoder; +import org.infinity.resource.key.ResourceEntry; + +/** + * Definition of an animation sequence. + */ +public class SeqDef implements Cloneable +{ + /** Definition of an empty {@code SeqDef} object. */ + public static final SeqDef DEFAULT = new SeqDef(Sequence.NONE, new DirDef[0]); + + /** Full set of 16 directions (east & west). */ + public static final Direction[] DIR_FULL = Direction.values(); + /** Reduced set of 8 directions (east & west) */ + public static final Direction[] DIR_REDUCED = { Direction.S, Direction.SW, Direction.W, Direction.NW, + Direction.N, Direction.NE, Direction.E, Direction.SE }; + /** Full set of 9 non-eastern directions. */ + public static final Direction[] DIR_FULL_W = { Direction.S, Direction.SSW, Direction.SW, Direction.WSW, + Direction.W, Direction.WNW, Direction.NW, Direction.NNW, + Direction.N }; + /** Full set of 7 eastern directions. */ + public static final Direction[] DIR_FULL_E = { Direction.NNE, Direction.NE, Direction.ENE, Direction.E, + Direction.ESE, Direction.SE, Direction.SSE }; + /** Reduced set of 5 non-eastern directions. */ + public static final Direction[] DIR_REDUCED_W = { Direction.S, Direction.SW, Direction.W, Direction.NW, Direction.N }; + /** Reduced set of 3 eastern directions. */ + public static final Direction[] DIR_REDUCED_E = { Direction.NE, Direction.E, Direction.SE }; + + private final Sequence sequence; + private final ArrayList directions; + + /** + * Creates a new sequence definition with the specified directions. + * @param seq The defined sequence + * @param dirs One or more direction definitions + */ + public SeqDef(Sequence seq, DirDef... dirs) + { + this.sequence = Objects.requireNonNull(seq, "Sequence cannot be null"); + this.directions = new ArrayList<>(); + addDirections(dirs); + } + + public SeqDef(SeqDef sd) + { + Objects.requireNonNull(sd, "SeqDef instance cannot be null"); + this.sequence = sd.sequence; + this.directions = new ArrayList<>(); + for (final DirDef dd : sd.directions) { + addDirections(new DirDef(dd)); + } + } + + /** Returns the sequence type this definition is assigned to. */ + public Sequence getSequence() { return sequence; } + + /** Provides access to the list of available direction definitions associated with this sequence. */ + public List getDirections() { return directions; } + + /** + * Appends list of direction definitions. + * Cycles of existing directions are appended. + */ + public void addDirections(DirDef... directions) + { + for (final DirDef dd : directions) { + DirDef dir = this.directions.stream().filter(d -> d.getDirection() == dd.getDirection()).findAny().orElse(null); + if (dir != null) { + dir.getCycle().addCycles(dd.getCycle().getCycles()); + } else { + dd.setParent(this); + this.directions.add(dd); + } + } + } + + /** Returns whether the sequence contains any directions with cycle definitions. */ + public boolean isEmpty() + { + boolean retVal = true; + if (!directions.isEmpty()) { + for (final DirDef dd : directions) { + if (!dd.getCycle().getCycles().isEmpty()) { + retVal = false; + break; + } + } + } + return retVal; + } + + @Override + public SeqDef clone() + { + return new SeqDef(this); + } + + @Override + public String toString() + { + return "sequence=" + sequence.toString() + ", directions={" + directions.toString() + "}"; + } + + @Override + public int hashCode() + { + int hash = 7; + hash = 31 * hash + ((sequence == null) ? 0 : sequence.hashCode()); + hash = 31 * hash + ((directions == null) ? 0 : directions.hashCode()); + return hash; + } + + @Override + public boolean equals(Object o) + { + if (o == this) { + return true; + } + if (!(o instanceof SeqDef)) { + return false; + } + SeqDef other = (SeqDef)o; + boolean retVal = (this.sequence == null && other.sequence == null) || + (this.sequence != null && this.sequence.equals(other.sequence)); + retVal &= (this.directions == null && other.directions == null) || + (this.directions != null && this.directions.equals(other.directions)); + return retVal; + } + + /** Convenience method: Returns a list of all BAM resources associated with the specified sequence definitions. */ + public static ResourceEntry[] getBamResourceList(SeqDef... sequences) + { + return getBamResourceList(null, sequences); + } + + /** + * Convenience method: Returns a list of all BAM resources associated with the specified sequence definitions + * matching the specified segment type. + */ + public static ResourceEntry[] getBamResourceList(SegmentDef.SpriteType type, SeqDef... sequences) + { + HashSet resources = new HashSet<>(); + for (final SeqDef sd : sequences) { + for (final DirDef dd : sd.directions) { + for (int i = 0, cnt = dd.getCycle().getCycles().size(); i < cnt; i++) { + if (type == null || dd.getCycle().getCycles().get(i).getSpriteType() == type) { + resources.add(dd.getCycle().getCycles().get(i).getEntry()); + } + } + } + } + return resources.toArray(new ResourceEntry[resources.size()]); + } + + /** + * Convenience method: Creates a fully defined sequence if specified directions are found within a single BAM resource. + * Behavior is assumed to be {@link SegmentDef.Behavior#REPEAT}. Composite object is initialized with {@link AlphaComposite#SrcOver}. + * @param seq the animation {@link SpriteDecoder.Sequence}. + * @param directions List of directions to add. Cycle indices are advanced accordingly for each direction. + * @param mirrored indicates whether cycle indices are calculated in reversed direction + * @param bamResource the BAM resource used for all cycle definitions. + * @param cycleOfs the first BAM cycle index. Advanced by one for each direction. + * Cycle indices are processed in reversed direction if {@code mirrored} is {@code true}. + * @param type the {@link SegmentDef.SpriteType} assigned to all cycle definitions. + */ + public static SeqDef createSequence(Sequence seq, Direction[] directions, boolean mirrored, + ResourceEntry bamResource, int cycleOfs, SegmentDef.SpriteType type) + { + return createSequence(seq, directions, mirrored, + new ArrayList() {{ add(new SegmentDef(null, bamResource, cycleOfs, type)); }}); + } + + /** + * Convenience method: Creates a fully defined sequence if specified directions are found within a single BAM resource. + * Composite object is initialized with {@link AlphaComposite#SrcOver}. + * @param seq the animation {@link SpriteDecoder.Sequence}. + * @param directions List of directions to add. Cycle indices are advanced accordingly for each direction. + * @param mirrored indicates whether cycle indices are calculated in reversed direction + * @param bamResource the BAM resource used for all cycle definitions. + * @param cycleOfs the first BAM cycle index. Advanced by one for each direction. + * Cycle indices are processed in reversed direction if {@code mirrored} is {@code true}. + * @param type the {@link SegmentDef.SpriteType} assigned to all cycle definitions. + * @param behavior the {@link SegmentDef.Behavior} assigned to all cycle definitions. + */ + public static SeqDef createSequence(Sequence seq, Direction[] directions, boolean mirrored, + ResourceEntry bamResource, int cycleOfs, SegmentDef.SpriteType type, + SegmentDef.Behavior behavior) + { + return createSequence(seq, directions, mirrored, + new ArrayList() {{ add(new SegmentDef(null, bamResource, cycleOfs, type, behavior)); }}); + } + + /** + * Convenience method: Creates a fully defined sequence if specified directions are found within a single BAM resource. + * @param seq the animation {@link SpriteDecoder.Sequence}. + * @param directions List of directions to add. Cycle indices are advanced accordingly for each direction. + * @param mirrored indicates whether cycle indices are calculated in reversed direction + * @param bamResource the BAM resource used for all cycle definitions. + * @param cycleOfs the first BAM cycle index. Advanced by one for each direction. + * Cycle indices are processed in reversed direction if {@code mirrored} is {@code true}. + * @param type the {@link SegmentDef.SpriteType} assigned to all cycle definitions. + * @param behavior the {@link SegmentDef.Behavior} assigned to all cycle definitions. + * @param composite the {@link Composite} object used for rendering sprite frames onto the canvas. + */ + public static SeqDef createSequence(Sequence seq, Direction[] directions, boolean mirrored, + ResourceEntry bamResource, int cycleOfs, SegmentDef.SpriteType type, + SegmentDef.Behavior behavior, Composite composite) + { + return createSequence(seq, directions, mirrored, + new ArrayList() {{ add(new SegmentDef(null, bamResource, cycleOfs, type, behavior, composite)); }}); + } + + /** + * Convenience method: Creates a fully defined sequence for all directions and their associated segment definitions. + * @param seq the animation {@link SpriteDecoder.Sequence}. + * @param directions List of directions to add. Cycle indices are advanced accordingly for each direction. + * @param mirrored indicates whether cycle indices are calculated in reversed direction + * @param cycleInfo collection of {@link SegmentDef} instances that are to be associated with the directions. + */ + public static SeqDef createSequence(Sequence seq, Direction[] directions, boolean mirrored, + Collection cycleInfo) + { + SeqDef retVal = new SeqDef(seq); + + DirDef[] dirs = new DirDef[Objects.requireNonNull(directions, "Array of creature directions cannot be null").length]; + List cycleDefs = new ArrayList<>(); + for (int i = 0; i < directions.length; i++) { + cycleDefs.clear(); + int inc = mirrored ? (directions.length - i - 1) : i; + for (Iterator iter = cycleInfo.iterator(); iter.hasNext();) { + SegmentDef sd = iter.next(); + cycleDefs.add(new SegmentDef(null, sd.getEntry(), sd.getCycleIndex() + inc, sd.getSpriteType(), sd.getBehavior())); + } + dirs[i] = new DirDef(retVal, directions[i], mirrored, new CycleDef(null, cycleDefs)); + } + retVal.addDirections(dirs); + + return retVal; + } +} diff --git a/src/org/infinity/resource/cre/decoder/util/Sequence.java b/src/org/infinity/resource/cre/decoder/util/Sequence.java new file mode 100644 index 000000000..dde47d3ef --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/util/Sequence.java @@ -0,0 +1,157 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.decoder.util; + +import java.util.ArrayList; +import java.util.List; + +/** + * Available animation sequences. Note: PST-specific animation sequences are prefixed by "PST_". + */ +public enum Sequence { + /** Special value: Used when no animation sequence is loaded. */ + NONE, + STAND("Stand"), + STAND2("Stand 2"), + STAND3("Stand 3"), + /** For buried creatures only: stand sequence when emerged. */ + STAND_EMERGED("Stand (emerged)"), + /** For buried creatures only: stand sequence when hidden. */ + STAND_HIDDEN("Stand (hidden)"), + STANCE("Combat ready"), + STANCE2("Combat ready"), + GET_HIT("Get hit"), + /** Dying sequence; may also be used for sleep sequence. */ + DIE("Die"), + TWITCH("Twitch"), + /** The animation sequence used while chanting a spell. */ + SPELL("Conjure spell"), + /** The animation sequence used when releasing a spell. */ + CAST("Cast spell"), + SPELL1("Conjure spell 1"), + CAST1("Cast spell 1"), + SPELL2("Conjure spell 2"), + CAST2("Cast spell 2"), + SPELL3("Conjure spell 3"), + CAST3("Cast spell 3"), + SPELL4("Conjure spell 4"), + CAST4("Cast spell 4"), + SLEEP("Sleep"), + GET_UP("Get up"), + SLEEP2("Sleep 2"), + GET_UP2("Get up 2"), + WALK("Walk"), + ATTACK("Attack"), + ATTACK_2("Attack 2"), + ATTACK_3("Attack 3"), + ATTACK_4("Attack 4"), + ATTACK_5("Attack 5"), + ATTACK_2H("Attack (2-h)"), + /** 1-h slash attack; also used for throwing/sling in BG1/IWD */ + ATTACK_SLASH_1H("Attack (slash)"), + /** 1-h backslash attack */ + ATTACK_BACKSLASH_1H("Attack (backslash)"), + /** 1-h thrust/jab attack */ + ATTACK_JAB_1H("Attack (jab)"), + /** 2-h slash attack */ + ATTACK_SLASH_2H("Attack (slash)"), + /** 2-h backslash attack */ + ATTACK_BACKSLASH_2H("Attack (backslash)"), + /** 2-h thrust/jab attack */ + ATTACK_JAB_2H("Attack (jab)"), + /** two-weapon attack */ + ATTACK_2WEAPONS1("Attack (two-weapons) 1"), + /** two-weapon attack */ + ATTACK_2WEAPONS2("Attack (two-weapons) 2"), + /** Generic overhead attack; may be used for throwing weapons and weapons without equipped appearance. */ + ATTACK_OVERHEAD("Attack (overhead)"), + ATTACK_BOW("Attack (bow)"), + ATTACK_SLING("Attack (sling)"), + ATTACK_CROSSBOW("Attack (crossbow)"), + /** Generic ranged attack animation (e.g. for innate powers/breath weapons) */ + SHOOT("Attack (ranged)"), + /** For buried creatures only: monster emerges from underground. */ + EMERGE("Emerge"), + /** For buried creatures only: monster retreats to underground. */ + HIDE("Hide"), + + PST_ATTACK1("Attack"), + PST_ATTACK2("Attack 2"), + PST_ATTACK3("Attack 3"), + PST_GET_HIT("Get hit"), + PST_RUN("Run"), + PST_WALK("Walk"), + PST_SPELL1("Cast spell"), + PST_SPELL2("Cast spell 2"), + PST_SPELL3("Cast spell 3"), + PST_GET_UP("Get up"), + PST_DIE_FORWARD("Die (fall forward)"), + PST_DIE_BACKWARD("Die (fall backwards)"), + PST_DIE_COLLAPSE("Die (collapse)"), + PST_TALK1("Talk"), + PST_TALK2("Talk 2"), + PST_TALK3("Talk 3"), + PST_STAND_FIDGET1("Stand (fidget)"), + PST_STAND_FIDGET2("Stand (fidget 2)"), + PST_STANCE_FIDGET1("Combat ready (fidget)"), + PST_STANCE_FIDGET2("Combat ready (fidget 2)"), + PST_STAND("Stand"), + PST_STANCE("Combat ready"), + PST_STANCE_TO_STAND("Combat ready to stand"), + PST_STAND_TO_STANCE("Stand to combat ready"), + PST_MISC1("Custom sequence 1"), + PST_MISC2("Custom sequence 2"), + PST_MISC3("Custom sequence 3"), + PST_MISC4("Custom sequence 4"), + PST_MISC5("Custom sequence 5"), + PST_MISC6("Custom sequence 6"), + PST_MISC7("Custom sequence 7"), + PST_MISC8("Custom sequence 8"), + PST_MISC9("Custom sequence 9"), + PST_MISC10("Custom sequence 10"), + PST_MISC11("Custom sequence 11"), + PST_MISC12("Custom sequence 12"), + PST_MISC13("Custom sequence 13"), + PST_MISC14("Custom sequence 14"), + PST_MISC15("Custom sequence 15"), + PST_MISC16("Custom sequence 16"), + PST_MISC17("Custom sequence 17"), + PST_MISC18("Custom sequence 18"), + PST_MISC19("Custom sequence 19"), + PST_MISC20("Custom sequence 20"); + + private static final List DEFAULT_SEQUENCES = new ArrayList() {{ + add(Sequence.STAND); + add(Sequence.STAND2); + add(Sequence.STAND3); + add(Sequence.STAND_EMERGED); + add(Sequence.PST_STAND); + add(Sequence.STANCE); + add(Sequence.STANCE2); + add(Sequence.PST_STANCE); + add(Sequence.WALK); + add(Sequence.PST_WALK); + }}; + + private final String desc; + + /** Creates a new {@code AnimationSequence} with an empty label. */ + private Sequence() { this(null); } + + /** Creates a new {@code AnimationSequence} with the specified label. */ + private Sequence(String desc) { this.desc = desc; } + + @Override + public String toString() + { + return (desc != null) ? desc : super.toString(); + } + + /** + * Returns a list of default animation sequences that are safe for initializing a new + * creature animation in order of preference. + */ + public static List getDefaultSequences() { return DEFAULT_SEQUENCES; } +} diff --git a/src/org/infinity/resource/cre/decoder/util/SpriteUtils.java b/src/org/infinity/resource/cre/decoder/util/SpriteUtils.java new file mode 100644 index 000000000..3fb3a2804 --- /dev/null +++ b/src/org/infinity/resource/cre/decoder/util/SpriteUtils.java @@ -0,0 +1,1574 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.cre.decoder.util; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Image; +import java.awt.Rectangle; +import java.awt.image.BufferedImage; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.EnumMap; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.Random; + +import org.infinity.resource.Profile; +import org.infinity.resource.ResourceFactory; +import org.infinity.resource.cre.CreResource; +import org.infinity.resource.cre.browser.icon.Icons; +import org.infinity.resource.cre.decoder.AmbientDecoder; +import org.infinity.resource.cre.decoder.AmbientStaticDecoder; +import org.infinity.resource.cre.decoder.CharacterDecoder; +import org.infinity.resource.cre.decoder.CharacterOldDecoder; +import org.infinity.resource.cre.decoder.PlaceholderDecoder; +import org.infinity.resource.cre.decoder.EffectDecoder; +import org.infinity.resource.cre.decoder.FlyingDecoder; +import org.infinity.resource.cre.decoder.MonsterAnkhegDecoder; +import org.infinity.resource.cre.decoder.MonsterDecoder; +import org.infinity.resource.cre.decoder.MonsterIcewindDecoder; +import org.infinity.resource.cre.decoder.MonsterLarge16Decoder; +import org.infinity.resource.cre.decoder.MonsterLargeDecoder; +import org.infinity.resource.cre.decoder.MonsterLayeredDecoder; +import org.infinity.resource.cre.decoder.MonsterLayeredSpellDecoder; +import org.infinity.resource.cre.decoder.MonsterMultiDecoder; +import org.infinity.resource.cre.decoder.MonsterMultiNewDecoder; +import org.infinity.resource.cre.decoder.MonsterOldDecoder; +import org.infinity.resource.cre.decoder.MonsterPlanescapeDecoder; +import org.infinity.resource.cre.decoder.MonsterQuadrantDecoder; +import org.infinity.resource.cre.decoder.SpriteDecoder; +import org.infinity.resource.cre.decoder.TownStaticDecoder; +import org.infinity.resource.cre.decoder.tables.SpriteTables; +import org.infinity.resource.graphics.BamV1Decoder; +import org.infinity.resource.graphics.ColorConvert; +import org.infinity.resource.graphics.GraphicsResource; +import org.infinity.resource.graphics.BamDecoder.FrameEntry; +import org.infinity.resource.graphics.BamV1Decoder.BamV1Control; +import org.infinity.resource.key.BufferedResourceEntry; +import org.infinity.resource.key.ResourceEntry; +import org.infinity.util.IdsMap; +import org.infinity.util.IdsMapCache; +import org.infinity.util.IdsMapEntry; +import org.infinity.util.IniMap; +import org.infinity.util.IniMapCache; +import org.infinity.util.IniMapEntry; +import org.infinity.util.IniMapSection; +import org.infinity.util.Misc; +import org.infinity.util.Table2da; +import org.infinity.util.Table2daCache; +import org.infinity.util.io.StreamUtils; +import org.infinity.util.tuples.Couple; + +/** + * Collection of helpful methods for Sprite rendering. + */ +public class SpriteUtils +{ + /** Cache for source BAM resources (decoder and attached controller). */ + private static final HashMap> bamCache = new HashMap<>(); + /** Cache for replacement palettes. */ + private static final HashMap paletteCache = new HashMap<>(); + /** Cache for color gradients. */ + private static final HashMap colorGradients = new HashMap<>(350); + /** Cache for randomized color gradients. */ + private static final HashMap randomGradientIndices = new HashMap<>(); + /** A random number generator for general use */ + private static final Random random = new Random(); + + /** Mappings between animation types and compatible sprite classes. */ + private static final EnumMap> typeAssociations = + new EnumMap>(AnimationInfo.Type.class) {{ + put(AnimationInfo.Type.EFFECT, EffectDecoder.class); + put(AnimationInfo.Type.MONSTER_QUADRANT, MonsterQuadrantDecoder.class); + put(AnimationInfo.Type.MONSTER_MULTI, MonsterMultiDecoder.class); + put(AnimationInfo.Type.MONSTER_MULTI_NEW, MonsterMultiNewDecoder.class); + put(AnimationInfo.Type.MONSTER_LAYERED_SPELL, MonsterLayeredSpellDecoder.class); + put(AnimationInfo.Type.MONSTER_ANKHEG, MonsterAnkhegDecoder.class); + put(AnimationInfo.Type.TOWN_STATIC, TownStaticDecoder.class); + put(AnimationInfo.Type.CHARACTER, CharacterDecoder.class); + put(AnimationInfo.Type.CHARACTER_OLD, CharacterOldDecoder.class); + put(AnimationInfo.Type.MONSTER, MonsterDecoder.class); + put(AnimationInfo.Type.MONSTER_OLD, MonsterOldDecoder.class); + put(AnimationInfo.Type.MONSTER_LAYERED, MonsterLayeredDecoder.class); + put(AnimationInfo.Type.MONSTER_LARGE, MonsterLargeDecoder.class); + put(AnimationInfo.Type.MONSTER_LARGE_16, MonsterLarge16Decoder.class); + put(AnimationInfo.Type.AMBIENT_STATIC, AmbientStaticDecoder.class); + put(AnimationInfo.Type.AMBIENT, AmbientDecoder.class); + put(AnimationInfo.Type.FLYING, FlyingDecoder.class); + put(AnimationInfo.Type.MONSTER_ICEWIND, MonsterIcewindDecoder.class); + put(AnimationInfo.Type.MONSTER_PLANESCAPE, MonsterPlanescapeDecoder.class); + }}; + + /** A stable pool of random numbers. */ + private static int[] randomPool; + + /** Clears cached resources. */ + public static void clearCache() + { + clearBamCache(); + clearColorCache(); + } + + /** Clears BAM cache only. */ + public static void clearBamCache() + { + bamCache.clear(); + } + + /** Clears all palette-related caches. */ + public static void clearColorCache() + { + paletteCache.clear(); + colorGradients.clear(); + randomGradientIndices.clear(); + } + + /** + * Creates a pseudo CRE resource with the specified animation id and an optional set of creature colors. + * @param animationId The animation id for the CRE. + * @param colors Optional creature colors. Each entry uses the CRE_COLOR_xxx field names defined in {@link CreResource} + * associated with the numeric color value. + * @param equipment Optional equipment. Each entry uses inventory slot index (as defined in CRE) as key and + * item resref as value. + * These items are only used as source for overlay bams (i.e. weapons, shields, helmets). + * @return A {@code CreResource} instance with the virtual creature data. + */ + public static CreResource getPseudoCre(int animationId, HashMap colors, HashMap equipment) throws Exception + { + CreResource entry = null; + ByteBuffer buffer = null; + if ((Boolean)Profile.getProperty(Profile.Key.IS_SUPPORTED_CRE_V90)) { + // IWD + int sizeBase = 0x33c; + int slotsSize = 0x50; // item slots + buffer = StreamUtils.getByteBuffer(sizeBase + slotsSize); + buffer.position(0); + buffer.put("CRE V9.0".getBytes(Misc.CHARSET_ASCII)); + // creature colors + if (colors != null) { + for (final HashMap.Entry e : colors.entrySet()) { + switch (e.getKey()) { + case CreResource.CRE_COLOR_METAL: + buffer.put(0x2c, e.getValue().byteValue()); + break; + case CreResource.CRE_COLOR_MINOR: + buffer.put(0x2d, e.getValue().byteValue()); + break; + case CreResource.CRE_COLOR_MAJOR: + buffer.put(0x2e, e.getValue().byteValue()); + break; + case CreResource.CRE_COLOR_SKIN: + buffer.put(0x2f, e.getValue().byteValue()); + break; + case CreResource.CRE_COLOR_LEATHER: + buffer.put(0x30, e.getValue().byteValue()); + break; + case CreResource.CRE_COLOR_ARMOR: + buffer.put(0x31, e.getValue().byteValue()); + break; + case CreResource.CRE_COLOR_HAIR: + buffer.put(0x32, e.getValue().byteValue()); + break; + } + } + } + // Enemy-Ally + buffer.put(0x2d8, (byte)128); + // setting valid offsets + for (int ofs : new int[] {0x308, 0x310, 0x318, 0x320, 0x324, 0x32c}) { + buffer.putInt(ofs, sizeBase); + } + for (int i = 0; i < slotsSize - 2; i++) { + // marking item slots as empty + buffer.put(sizeBase + i, (byte)0xff); + } + } else if ((Boolean)Profile.getProperty(Profile.Key.IS_SUPPORTED_CRE_V22)) { + // IWD2 + int sizeBase = 0x62e; + int slotsSize = 0x68; // item slots + buffer = StreamUtils.getByteBuffer(sizeBase + slotsSize); + buffer.position(0); + buffer.put("CRE V2.2".getBytes(Misc.CHARSET_ASCII)); + // creature colors + if (colors != null) { + for (final HashMap.Entry e : colors.entrySet()) { + switch (e.getKey()) { + case CreResource.CRE_COLOR_METAL: + buffer.put(0x2c, e.getValue().byteValue()); + break; + case CreResource.CRE_COLOR_MINOR: + buffer.put(0x2d, e.getValue().byteValue()); + break; + case CreResource.CRE_COLOR_MAJOR: + buffer.put(0x2e, e.getValue().byteValue()); + break; + case CreResource.CRE_COLOR_SKIN: + buffer.put(0x2f, e.getValue().byteValue()); + break; + case CreResource.CRE_COLOR_LEATHER: + buffer.put(0x30, e.getValue().byteValue()); + break; + case CreResource.CRE_COLOR_ARMOR: + buffer.put(0x31, e.getValue().byteValue()); + break; + case CreResource.CRE_COLOR_HAIR: + buffer.put(0x32, e.getValue().byteValue()); + break; + } + } + } + // Enemy-Ally + buffer.put(0x384, (byte)128); + // setting valid offsets + for (int i = 0; i < 63; i++) { + buffer.putInt(0x3ba + (i * 4), sizeBase); + } + for (int i = 0; i < 9; i++) { + buffer.putInt(0x5b2+ (i * 4), sizeBase); + } + for (int ofs : new int[] {0x5fa, 0x602, 0x60a, 0x612, 0x616, 0x61e}) { + buffer.putInt(ofs, sizeBase); + } + for (int i = 0; i < slotsSize - 2; i++) { + // marking item slots as empty + buffer.put(sizeBase + i, (byte)0xff); + } + } else if ((Boolean)Profile.getProperty(Profile.Key.IS_SUPPORTED_CRE_V12)) { + // PST + int sizeBase = 0x378; + int slotsSize = 0x60; // item slots + buffer = StreamUtils.getByteBuffer(sizeBase + slotsSize); + buffer.position(0); + buffer.put("CRE V1.2".getBytes(Misc.CHARSET_ASCII)); + // creature colors + int numColors = 0; + if (colors != null) { + for (final HashMap.Entry e : colors.entrySet()) { + int value = e.getValue().intValue(); + for (int i = 0; i < 7; i++) { + String labelColor = String.format(CreResource.CRE_COLOR_FMT, i + 1); + String labelColorPlacement = String.format(CreResource.CRE_COLOR_PLACEMENT_FMT, i + 1); + if (labelColor.equals(e.getKey())) { + buffer.putShort(0x2e4 + (i * 2), (short)value); + } else if (labelColorPlacement.equals(e.getKey())) { + buffer.put(0x2f5 + i, (byte)value); + numColors++; + } + } + } + } + buffer.put(0x2df, (byte)numColors); + // Enemy-Ally + buffer.put(0x314, (byte)128); + // setting valid offsets + for (int ofs : new int[] {0x344, 0x34c, 0x354, 0x35c, 0x360, 0x368}) { + buffer.putInt(ofs, sizeBase); + } + for (int i = 0; i < slotsSize - 2; i++) { + // marking item slots as empty + buffer.put(sizeBase + i, (byte)0xff); + } + } else { + // BG, BG2, EE + int sizeBase = 0x2d4; + int slotsSize = 0x50; // item slots + buffer = StreamUtils.getByteBuffer(sizeBase + slotsSize); + buffer.position(0); + buffer.put("CRE V1.0".getBytes(Misc.CHARSET_ASCII)); + // creature colors + if (colors != null) { + for (final HashMap.Entry e : colors.entrySet()) { + switch (e.getKey()) { + case CreResource.CRE_COLOR_METAL: + buffer.put(0x2c, e.getValue().byteValue()); + break; + case CreResource.CRE_COLOR_MINOR: + buffer.put(0x2d, e.getValue().byteValue()); + break; + case CreResource.CRE_COLOR_MAJOR: + buffer.put(0x2e, e.getValue().byteValue()); + break; + case CreResource.CRE_COLOR_SKIN: + buffer.put(0x2f, e.getValue().byteValue()); + break; + case CreResource.CRE_COLOR_LEATHER: + buffer.put(0x30, e.getValue().byteValue()); + break; + case CreResource.CRE_COLOR_ARMOR: + buffer.put(0x31, e.getValue().byteValue()); + break; + case CreResource.CRE_COLOR_HAIR: + buffer.put(0x32, e.getValue().byteValue()); + break; + } + } + } + // Enemy-Ally + buffer.put(0x270, (byte)128); + // setting valid offsets + for (int ofs : new int[] {0x2a0, 0x2a8, 0x2b0, 0x2b8, 0x2bc, 0x2c4}) { + buffer.putInt(ofs, sizeBase); + } + for (int i = 0; i < slotsSize - 2; i++) { + // marking item slots as empty + buffer.put(sizeBase + i, (byte)0xff); + } + } + + if (buffer != null) { + buffer.putInt(0x08, -1); // creature name + buffer.putInt(0x0c, -1); // creature tooltip + buffer.putInt(0x28, animationId); + if (equipment != null) { + for (final HashMap.Entry itm : equipment.entrySet()) { + addPseudoCreItem(buffer, itm.getKey().intValue(), itm.getValue()); + } + } + + entry = new CreResource(null, String.format("%04X", animationId & 0xffff), buffer, 0); + } + + return entry; + } + + /** + * Adds a new item to the pseudo CRE resource and assigns it to an item slot. + * @param buffer the source CRE buffer. + * @param slot slot to assign the new item. Specify negative value to skip. + * @param resref The item resref. + * @return the updated CRE buffer. + */ + private static ByteBuffer addPseudoCreItem(ByteBuffer buffer, int slot, String resref) + { + ByteBuffer outBuffer = buffer; + if (buffer == null || resref == null || resref.isEmpty()) { + return outBuffer; + } + + // preparing item entry + ByteBuffer item = StreamUtils.getByteBuffer(20); + item.position(0); + item.put(resref.getBytes(Misc.CHARSET_ASCII)); + + int numSlots = 0, ofsSlotsOffset = 0, ofsItemsCount = 0; + String ver = StreamUtils.readString(buffer, 4, 4); + switch (ver) { + case "V9.0": + numSlots = 38; + ofsSlotsOffset = 0x320; + ofsItemsCount = 0x328; + break; + case "V2.2": + numSlots = 50; + ofsSlotsOffset = 0x612; + ofsItemsCount = 0x61a; + break; + case "V1.2": + numSlots = 46; + ofsSlotsOffset = 0x35c; + ofsItemsCount = 0x364; + break; + case "V1.0": + numSlots = 38; + ofsSlotsOffset = 0x2b8; + ofsItemsCount = 0x2c0; + break; + } + + if (ofsSlotsOffset > 0) { + outBuffer = StreamUtils.getByteBuffer(buffer.limit() + item.limit()); + int ofsSlots = buffer.getInt(ofsSlotsOffset); + int numItems = buffer.getInt(ofsItemsCount); + outBuffer.position(0); + // adding CRE base + outBuffer.put(buffer.array(), 0, ofsSlots); + // adding new item + outBuffer.put(item.array(), 0, item.limit()); + // adding CRE inventory slots + outBuffer.put(buffer.array(), ofsSlots, buffer.limit() - ofsSlots); + // updating items count + numItems++; + outBuffer.putInt(ofsItemsCount, numItems); + // updating slots offset + ofsSlots += item.limit(); + outBuffer.putInt(ofsSlotsOffset, ofsSlots); + // assigning item to slot + if (slot >= 0 && slot < numSlots) { + outBuffer.putShort(ofsSlots + (slot * 2), (short)(numItems - 1)); + } + } + + return outBuffer; + } + + /** + * Loads the decoder instance for the specified BAM V1 resource. Retrieves the decoder from cache if available. + * @param entry the BAM resource entry. + * @return the {@code BamV1Decoder} instance created from the BAM resource. + * Returns {@code null} if decoder could not be retrieved. + */ + public static BamV1Decoder loadBamDecoder(ResourceEntry entry) + { + Couple retVal = loadBamDecoderController(entry); + return (retVal != null) ? retVal.getValue0() : null; + } + + /** + * Loads the BAM controller instance for the specified BAM V1 resource. Retrieves the object from cache if available. + * @param entry the BAM resource entry. + * @return the {@code BamV1Control} instance created from the BAM resource. + * Returns {@code null} if the controller could not be retrieved. + */ + public static BamV1Decoder.BamV1Control loadBamController(ResourceEntry entry) + { + Couple retVal = loadBamDecoderController(entry); + return (retVal != null) ? retVal.getValue1() : null; + } + + /** + * Loads the decoder instance and an associated controller for the specified BAM V1 resource. Retrieves the objects + * from cache if available. + * @param entry the BAM resource entry. + * @return {@code BamV1Decoder} and {@code BamV1Control} instances created from the BAM resource. + * Returns {@code null} if the objects could not be retrieved. + */ + public static Couple loadBamDecoderController(ResourceEntry entry) + { + Couple retVal = bamCache.getOrDefault(entry, null); + if (retVal == null) { + try { + BamV1Decoder decoder = new BamV1Decoder(entry); + BamV1Decoder.BamV1Control control = decoder.createControl(); + retVal = Couple.with(decoder, control); + bamCache.put(entry, retVal); + } catch (Exception e) { + e.printStackTrace(); + } + } + return retVal; + } + + /** + * Returns whether the specified BAM V1 resource exists and contains the specified range of cycles. + * @param entry the BAM resource to check. + * @param cycle start index of cycle range. + * @param length number of cycles. + * @return {@code true} if the BAM resource exists, is paletted and contains the specified cycles. + */ + public static boolean bamCyclesExist(ResourceEntry entry, int cycle, int length) + { + try { + BamV1Decoder.BamV1Control control = loadBamController(entry); + int numCycles = control.cycleCount(); + return (numCycles >= cycle + length && control.cycleFrameCount(cycle) > 0); + } catch (Exception e) { + } + return false; + } + + /** + * Returns the number of cycles available in the specified BAM resource. + * @param entry BAM resource to check. + * @return number of cycles. Returns 0 if number of cycles could not be determined. + */ + public static int getBamCycles(ResourceEntry entry) + { + int retVal = 0; + try { + BamV1Decoder.BamV1Control control = loadBamController(entry); + retVal = control.cycleCount(); + } catch (Exception e) { + } + return retVal; + } + + /** + * Returns the number of frames available in the specified BAM cycle. + * @param entry the BAM resource + * @param cycleIdx the BAM cycle to check. + * @return number of frames in the specified BAM cycle. Returns 0 if number of frames could not be determined. + */ + public static int getBamCycleFrames(ResourceEntry entry, int cycleIdx) + { + int retVal = 0; + try { + BamV1Decoder.BamV1Control control = loadBamController(entry); + if (cycleIdx >= 0 && cycleIdx < control.cycleCount()) { + retVal = control.cycleFrameCount(cycleIdx); + } + } catch (Exception e) { + } + return retVal; + } + + /** + * Returns whether the specified BAM contains a palette that is most likely replaced by external color ranges. + * @param entry BAM resource to check + * @return {@code true} if BAM contains a false color palette, {@code false} otherwise. + */ + public static boolean bamHasFalseColors(ResourceEntry entry) + { + boolean retVal = false; + try { + BamV1Decoder.BamV1Control control = loadBamController(entry); + int[] palette = control.getPalette(); + if (Profile.getGame() == Profile.Game.PST || Profile.getGame() == Profile.Game.PSTEE) { + retVal = (palette[224] & 0xffffff) == 0x0000ff; + retVal &= (palette[240] & 0xffffff) == 0x00009f; + } else { + retVal = (palette[15] & 0xffffff) == 0x1e1e1e; + retVal &= (palette[27] & 0xffffff) == 0x004040; + retVal &= (palette[39] & 0xffffff) == 0x400040; + retVal &= (palette[51] & 0xffffff) == 0x404000; + retVal &= (palette[63] & 0xffffff) == 0x400000; + retVal &= (palette[75] & 0xffffff) == 0x000040; + retVal &= (palette[87] & 0xffffff) == 0x004000; + } + } catch (Exception e) { + } + return retVal; + } + + /** + * Ensures that "transparent" and "shadow" palette entries of the animation are properly set. + * @param control the BAM controller associated with the animation. + * @param isTransparentShadow indicates whether shadow color is semi-transparent + */ + public static void fixShadowColor(BamV1Control control, boolean isTransparentShadow) + { + if (control != null) { + int[] palette = control.getCurrentPalette(); + palette[0] = 0x0000FF00; + palette[1] = isTransparentShadow ? 0x80000000 : 0xFF000000; + control.setExternalPalette(palette); + } + } + + /** + * Applies the specified palette to the BAM animation associated with the specified controller. + * @param control the BAM controller associated with the animation. + * @param palette the new palette data. + */ + public static void applyNewPalette(BamV1Control control, int[] palette) + { + if (palette == null) { + return; + } + if (palette.length < 256) { + palette = Arrays.copyOf(palette, 256); + } + control.setExternalPalette(palette); + } + + /** + * Loads the palette specified by the resource reference. + * It looks for a palette in the following order: [resref].BMP, [resref].BAM. + * @param resref the resource reference to use for loading a palette. + * @return palette data with 256 entries. Returns {@code null} if palette could not be loaded. + */ + public static int[] loadReplacementPalette(String resref) + { + return loadReplacementPalette(resref, -1); + } + + /** + * Loads the palette specified by the resource reference and index. + * It looks for a palette in the following order: [resref+suffix].BMP, [resref+suffix].BAM, [resref].BMP, [resref].BAM. + * @param resref the resource reference to use for loading a palette. + * @param index a numeric suffix added to the resref. + * @return palette data with 256 entries. Returns {@code null} if palette could not be loaded. + */ + public static int[] loadReplacementPalette(String resref, int index) + { + int[] retVal = null; + if (resref == null || resref.isEmpty()) { + return retVal; + } + + String resName = resref; + String suffix = (index >= 0) ? Integer.toString(index) : ""; + String[] suffixList = (suffix.isEmpty()) ? new String[] {""} : new String[] {suffix, ""}; + ResourceEntry entry = null; + for (final String s : suffixList) { + if (ResourceFactory.resourceExists(resName + s + ".BMP")) { + entry = ResourceFactory.getResourceEntry(resName + s + ".BMP"); + break; + } + } + if (entry == null) { + return retVal; + } + + retVal = paletteCache.getOrDefault(entry, null); + if (retVal == null) { + try { + retVal = ColorConvert.loadPaletteBMP(entry); + if (retVal != null) { + if (retVal.length < 256) { + retVal = Arrays.copyOf(retVal, 256); + } + paletteCache.put(entry, retVal); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + return retVal; + } + + /** + * Replaces a range of color entries in the palette with new entries. + * @param palette the palette to modify. + * @param newColors array of colors to add. + * @param startOffset first color entry in {@code palette} to update. + * @param len number of color entries to update. Parameter is adjusted if {@code newColors} array contains + * less entries than {@code len} or range would exceed palette size. + * @param allowAlpha {@code true} to use alpha from {@code newColors}; {@code false} to discard alpha values. + * @return the updated palette. + */ + public static int[] replaceColors(int[] palette, int[] newColors, int startOffset, int len, boolean allowAlpha) + { + int[] retVal = palette; + if (palette != null && newColors != null) { + retVal = Arrays.copyOf(palette, palette.length); + if (newColors.length > 0 && startOffset >= 0 && startOffset < retVal.length) { + len = Math.min(len, newColors.length); + len = Math.min(len, retVal.length - startOffset); + int mask = allowAlpha ? 0xffffffff : 0x00ffffff; + for (int i = 0; i < len; i++) { + retVal[startOffset + i] = newColors[i] & mask; + } + } + } + return retVal; + } + + /** + * Interpolates two palette ranges and stores the result in a third palette range. + * @param palette the palette to modify. + * @param srcOfs1 start offset of first source color range. + * @param srcOfs2 start offset of second source color range. + * @param srcLen number of source color entries to read. + * @param dstOfs start offset of target color range. + * @param dstLen number of interpolated color entries to write. + * @param allowAlpha {@code true} to interpolate alpha, {@code false} to discard alpha values. + * @return the updated palette. + */ + public static int[] interpolateColors(int[] palette, int srcOfs1, int srcOfs2, int srcLen, + int dstOfs, int dstLen, boolean allowAlpha) + { + int[] retVal = palette; + if (palette != null && srcLen > 0 && dstLen > 0 && + srcOfs1 >= 0 && srcOfs1 + srcLen <= palette.length && + srcOfs2 >= 0 && srcOfs2 + srcLen <= palette.length && + dstOfs >= 0 && dstOfs + dstLen <= palette.length) { + retVal = Arrays.copyOf(palette, palette.length); + for (int dstIdx = 0; dstIdx < dstLen; dstIdx++) { + int srcIdx = dstIdx * srcLen / dstLen; + int r = ((retVal[srcOfs1 + srcIdx] & 0xff) + (retVal[srcOfs2 + srcIdx] & 0xff)) >>> 1; + int g = (((retVal[srcOfs1 + srcIdx] >> 8) & 0xff) + ((retVal[srcOfs2 + srcIdx] >> 8) & 0xff)) >>> 1; + int b = (((retVal[srcOfs1 + srcIdx] >> 16) & 0xff) + ((retVal[srcOfs2 + srcIdx] >> 16) & 0xff)) >>> 1; + int a = 0xff000000; + if (allowAlpha) { + a = (((retVal[srcOfs1 + srcIdx] >> 24) & 0xff) + ((retVal[srcOfs2 + srcIdx] >> 24) & 0xff)) >>> 1; + } + retVal[dstOfs + dstIdx] = (a << 24) | (b << 16) | (g << 8) | r; + } + } + return retVal; + } + + /** + * Returns the specified color gradient. Colors are retrieved from the game-specific gradient resource. + * Optionally takes random color entry definitions into account. + * @param index Index of the color gradient. + * @param allowRandom whether random color entries are taken into account. + * @return the gradient as array of colors. Returns {@code null} if color index does not exist. + */ + public static int[] getColorGradient(int index, boolean allowRandom) + { + if (colorGradients.isEmpty()) { + // initializing color gradient map on demand + ResourceEntry palFile = null; + if (Profile.getGame() == Profile.Game.PST || Profile.getGame() == Profile.Game.PSTEE) { + palFile = ResourceFactory.getResourceEntry("PAL32.BMP"); + } else if (ResourceFactory.resourceExists("RANGES12.BMP")) { + palFile = ResourceFactory.getResourceEntry("RANGES12.BMP"); + } else if (ResourceFactory.resourceExists("MPALETTE.BMP")) { + palFile = ResourceFactory.getResourceEntry("MPALETTE.BMP"); + } + + if (palFile != null) { + try { + BufferedImage image = new GraphicsResource(palFile).getImage(); + for (int y = 0; y < image.getHeight(); y++) { + int[] pixels = image.getRGB(0, y, image.getWidth(), 1, null, 0, image.getWidth()); + colorGradients.put(y, pixels); + } + } catch (Exception e) { + e.printStackTrace(); + } + } else { + // dummy entry to skip continuous gradient initialization attempts if gradient bitmap isn't available + colorGradients.put(Integer.MIN_VALUE, null); + } + } + + int[] retVal = allowRandom ? getRandomColorGradient(index) : null; + if (retVal == null) { + retVal = colorGradients.getOrDefault(index, null); + } + + return retVal; + } + + /** + * Attempts to resolve a random color entry to a real color entry. + * @param index random color entry index. + * @return a randomly chosen color gradient from the random color entry list. + * Returns {@code null} if no real color gradient could be determined. + */ + public static int[] getRandomColorGradient(int index) + { + if (randomGradientIndices.isEmpty()) { + if (ResourceFactory.resourceExists("RANDCOLR.2DA")) { + Table2da table = Table2daCache.get("RANDCOLR.2DA"); + if (table != null && table.getRowCount() > 1) { + for (int col = 1, numCols = table.getColCount(); col < numCols; col++) { + // random entry index + int randIdx = Misc.toNumber(table.get(0, col), -1); + if (randIdx >= 0) { + int[] indices = new int[table.getRowCount() - 1]; + for (int row = 1, numRows = table.getRowCount(); row < numRows; row++) { + indices[row - 1] = Misc.toNumber(table.get(row, col), 0); + } + randomGradientIndices.put(randIdx, indices); + } + } + } + } else { + // dummy entry to skip continuous random gradient initialization attempts if RANDCOLR.2DA isn't available + randomGradientIndices.put(Integer.MIN_VALUE, null); + } + } + + int failCounter = 100; + int[] retVal = null; + int[] indices = randomGradientIndices.getOrDefault(index, null); + while (retVal == null && indices != null && indices.length > 0 && failCounter-- > 0) { + int idx = indices[getRandomInt(index, false) % indices.length]; + if (randomGradientIndices.containsKey(idx)) { + // random color entries may refer to other random color entries + indices = randomGradientIndices.getOrDefault(index, null); + } else { + retVal = getColorGradient(idx, false); + } + } + + return retVal; + } + + /** + * Returns a random number from a predefined pool of random numbers. + * @param index Index into the random number pool. + * @param allowNegative whether negative return values are allowed. + * @return the pseudo-random number specified by the index. + */ + public static int getRandomInt(int index, boolean allowNegative) + { + if (randomPool == null) { + updateRandomPool(); + } + int retVal = randomPool[index % randomPool.length]; + if (!allowNegative && retVal < 0) { + retVal = -retVal; + } + return retVal; + } + + /** Recreates the pool of random numbers. */ + public static void updateRandomPool() + { + if (randomPool == null) { + randomPool = new int[256]; + } + for (int i = 0; i < randomPool.length; i++) { + randomPool[i] = random.nextInt(); + } + } + + /** + * Modifies the specified range of palette entries according to the opcode and color specifications. + * @param palette ARGB palette to modify. + * @param startOfs index of first color entry to modify. + * @param length number of color entries to modify. + * @param opcode effect opcode to apply. Supported opcodes: 8, 51, 52 + * @param color color value associated with the opcode. + * @return the modified palette. + */ + public static int[] tintColors(int[] palette, int startOfs, int length, int opcode, int color) + { + int[] retVal = palette; + + switch (opcode) { + case EffectInfo.OPCODE_SET_COLOR_GLOW: + case EffectInfo.OPCODE_TINT_SOLID: + case EffectInfo.OPCODE_TINT_BRIGHT: + break; + default: + return retVal; + } + + if (palette != null) { + retVal = Arrays.copyOf(palette, palette.length); + startOfs = Math.max(0, Math.min(retVal.length - 1, startOfs)); + length = Math.max(0, Math.min(retVal.length - startOfs, length)); + + int dr = (color >> 16) & 0xff; + int dg = (color >> 8) & 0xff; + int db = color & 0xff; + for (int i = 0; i < length; i++) { + int sr = (retVal[startOfs + i] >> 16) & 0xff; + int sg = (retVal[startOfs + i] >> 8) & 0xff; + int sb = retVal[startOfs + i] & 0xff; + switch (opcode) { + case EffectInfo.OPCODE_SET_COLOR_GLOW: + sr = Math.min(255, sr + dr - (sr >>> 2)); + sg = Math.min(255, sg + dg - (sg >>> 2)); + sb = Math.min(255, sb + db - (sb >>> 2)); + break; + case EffectInfo.OPCODE_TINT_SOLID: + sr = Math.min(255, dr * sr / 255); + sg = Math.min(255, dg * sg / 255); + sb = Math.min(255, db * sb / 255); + break; + case EffectInfo.OPCODE_TINT_BRIGHT: + sr = Math.min(255, sr + (dr * (sr >>> 3))); + sg = Math.min(255, sg + (dg * (sg >>> 3))); + sb = Math.min(255, sb + (db * (sb >>> 3))); + break; + } + retVal[startOfs + i] = (retVal[startOfs + i] & 0xff000000) | (sr << 16) | (sg << 8) | sb; + } + } + + return retVal; + } + + + /** + * Calculates a dimension that can contain all the specified source frames. + * @param frames one or more source frames. + * @return A rectangle object where x and y indicate the top-left corner relative to the center point. + * Width and height specify frame dimension. + */ + public static Rectangle getTotalFrameDimension(FrameInfo... frames) + { + Rectangle retVal = new Rectangle(); + + if (frames.length > 0) { + int left = Integer.MAX_VALUE, top = Integer.MAX_VALUE, right = Integer.MIN_VALUE, bottom = Integer.MIN_VALUE; + for (final FrameInfo fi : frames) { + BamV1Control ctrl = fi.getController(); + int frameIdx = fi.getFrame(); + frameIdx = ctrl.cycleGetFrameIndexAbsolute(fi.getCycle(), frameIdx); + FrameEntry entry = fi.getController().getDecoder().getFrameInfo(frameIdx); + left = Math.min(left, -entry.getCenterX() + fi.getCenterShift().x); + top = Math.min(top, -entry.getCenterY() + fi.getCenterShift().y); + right = Math.max(right, entry.getWidth() - entry.getCenterX() + fi.getCenterShift().x); + bottom = Math.max(bottom, entry.getHeight() - entry.getCenterY() + fi.getCenterShift().y); + } + + retVal.x = left; + retVal.y = top; + retVal.width = right - left; + retVal.height = bottom - top; + } + + return retVal; + } + + /** Expands the rectangle to fit the specified dimension. */ + public static Rectangle updateFrameDimension(Rectangle rect, Dimension dim) + { + Rectangle retVal = new Rectangle(Objects.requireNonNull(rect, "Bounding box cannot be null")); + if (dim != null) { + int w2 = dim.width / 2; + int h2 = dim.height / 2; + int left = retVal.x; + int top = retVal.y; + int right = left + retVal.width; + int bottom = top + retVal.height; + left = Math.min(left, -w2); + top = Math.min(top, -h2); + right = Math.max(right, w2); + bottom = Math.max(bottom, h2); + retVal.x = left; + retVal.y = top; + retVal.width = right - left; + retVal.height = bottom - top; + } + return retVal; + } + + + /** + * Determines the right allegiance color for selection circles and returns it as {@code Color} object. + * A negative value will enable the "panic" color. + * @param value numeric allegiance value. Specify a negative value to override allegiance by the "panic" status. + * @return a {@code Color} object with the associated allegiance or status color. + */ + public static Color getAllegianceColor(int value) + { + Color c = null; + if (value < 0) { + // treat as panic + c = new Color(0xffff20, false); + } else if (value >= 2 && value <= 4 || value == 201) { + // ally + c = new Color(0x20ff20, false); + } else if (value == 255 || value == 254 || value == 28 || value == 6 || value == 5) { + // enemy + c = new Color(0xff2020, false); + } else { + // neutral + c = new Color(0x20ffff, false); + } + + return c; + } + + /** + * Determines the right selection circle bitmap based on the specified allegiance value and returns it + * as {@code Image} object. A negative value will enable the "panic" bitmap. + * @param value numeric allegiance value. Specify a negative value to override allegiance by the "panic" status. + * @return + */ + public static Image getAllegianceImage(int value) + { + Image retVal = null; + if (value < 0) { + // treat as panic + retVal = Icons.getImage(Icons.ICON_CIRCLE_YELLOW); + } else if (value >= 2 && value <= 4 || value == 201) { + // ally + retVal = Icons.getImage(Icons.ICON_CIRCLE_GREEN); + } else if (value == 255 || value == 254 || value == 28 || value == 6 || value == 5) { + // enemy + retVal = Icons.getImage(Icons.ICON_CIRCLE_RED); + } else { + // neutral + retVal = Icons.getImage(Icons.ICON_CIRCLE_BLUE); + } + + return retVal; + } + + + /** + * Returns the {@code SpriteClass} class associated with the specified {@code AnimationType} enum. + * @param type the {@code AnimationType} + * @return the associated {@code SpriteClass} class object. Returns {@code null} if class could not be determined. + */ + public static Class getSpriteDecoderClass(AnimationInfo.Type type) + { + return typeAssociations.get(type); + } + + /** + * Returns the {@code SpriteClass} class associated with the specified animation id. + * @param animationId the animation id + * @return a class type compatible with the specified animation id. + * Returns {@code null} if no class could be determined. + */ + public static Class getSpriteDecoderClass(int animationId) + { + Class retVal = null; + + // Testing Infinity Animation range first + AnimationInfo.Type animType = AnimationInfo.Type.containsInfinityAnimations(animationId); + if (animType != null) { + retVal = typeAssociations.get(animType); + } + + // Testing regular ranges + if (retVal == null) { + for (final AnimationInfo.Type type : AnimationInfo.Type.values()) { + if (type.contains(animationId)) { + retVal = typeAssociations.get(type); + if (retVal != null) { + break; + } + } + } + } + + return retVal; + } + + /** + * Returns creature animation info in INI format. Section and field format is based on the EE v2.0 INI format. + * The method will first look for existing INI data in the game resources. Failing that it will look up data in + * hardcoded tables and fill in missing data from associated 2DA file if available. Failing that it will guess + * the correct format based on animation type and available resources. + * @param animationId the 16-bit animation id. + * @return An IniMap structure containing necessary data for rendering creature animation. Returns {@code null} if no + * animation info could be assembled. + */ + public static IniMap getAnimationInfo(int animationId) + { + List retVal = new ArrayList<>(); + + // 1. look up existing INI resource + retVal.addAll(getAnimationInfoByIni(animationId)); + + if (retVal.isEmpty()) { + // 2. look up hardcoded tables + retVal.addAll(getAnimationInfoByTable(animationId)); + } + + if (retVal.isEmpty()) { + // 3. guess animation info based on anisnd.2da entry and available sprite classes + retVal.addAll(getAnimationInfoByGuess(animationId)); + } + + if (!retVal.isEmpty()) { + return retVal.get(0); + } else { + return null; + } + } + + /** + * Attempts to determine the animation type assigned to the specified creature. + * @return Class instance responsible for handling the detected animation type. {@code null} if type could not be determined. + */ + public static Class detectAnimationType(int animationId) + { + Class retVal = null; + + List iniList = new ArrayList<>(); + iniList.addAll(getAnimationInfoByIni(animationId)); + + if (iniList.isEmpty()) { + iniList.addAll(getAnimationInfoByTable(animationId)); + } + + if (iniList.isEmpty()) { + iniList.addAll(getAnimationInfoByGuess(animationId)); + } + + if (!iniList.isEmpty()) { + // trying recommended sprite decoder class first + Class cls = getSpriteDecoderClass(animationId); + if (isSpriteDecoderAvailable(cls, animationId, iniList)) { + retVal = cls; + } + + if (retVal == null) { + // trying out all available sprite decoder classes otherwise + if (Profile.getGame() == Profile.Game.PST || Profile.getGame() == Profile.Game.PSTEE) { + if (isSpriteDecoderAvailable(MonsterPlanescapeDecoder.class, animationId, iniList)) { + retVal = cls; + } + } else { + for (final AnimationInfo.Type type : AnimationInfo.Type.values()) { + if (type != AnimationInfo.Type.MONSTER_PLANESCAPE) { + cls = typeAssociations.get(type); + if (isSpriteDecoderAvailable(cls, animationId, iniList)) { + retVal = cls; + break; + } + } + } + } + } + + if (retVal == null) { + // No luck yet? Fall back to placeholder animation! + retVal = PlaceholderDecoder.class; + } + } + + return retVal; + } + + /** + * A helper method that parses the specified data array and generates a list of INI lines + * related to the "general" section. + * @param data the String array containing data for a specific table entry. + * @param type the animation type. + * @return the initialized "general" INI section as list of strings. An empty list otherwise. + */ + public static List processTableDataGeneral(String[] data, AnimationInfo.Type type) + { + List retVal = new ArrayList<>(); + if (data == null || type == null) { + return retVal; + } + + int id = SpriteTables.valueToAnimationId(data, SpriteTables.COLUMN_ID, -1); + if (id < 0) { + return retVal; + } + int ellipse = SpriteTables.valueToInt(data, SpriteTables.COLUMN_ELLIPSE, 16); + int space = SpriteTables.valueToInt(data, SpriteTables.COLUMN_SPACE, 3); + int blending = SpriteTables.valueToInt(data, SpriteTables.COLUMN_BLENDING, 0); + String palette = SpriteTables.valueToString(data, SpriteTables.COLUMN_PALETTE, ""); + + int animIndex = SpriteTables.valueToInt(data, SpriteTables.COLUMN_TYPE, -1); + if (animIndex < 0 || animIndex >= AnimationInfo.Type.values().length || AnimationInfo.Type.values()[animIndex] != type) { + return retVal; + } + + int animType = -1; + for (int i = 0; i < type.getTypeCount(); i++) { + if (animType < 0 || (id & 0xf000) == type.getType(i)) { + animType = type.getType(i); + } + } + + retVal.add("[general]"); + retVal.add(String.format("animation_type=%04X", animType)); + retVal.add("ellipse=" + ellipse); + retVal.add("personal_space=" + space); + if ((blending & 1) == 1) { + retVal.add("brightest=1"); + } + if ((blending & 2) == 2) { + retVal.add("multiply_blend=1"); + } + if ((blending & 4) == 4) { + retVal.add("light_source=1"); + } + if (!palette.isEmpty()) { + retVal.add("new_palette=" + palette); + } + + return retVal; + } + + /** + * A helper method for PST animations that parses the specified data array and generates a list of INI lines + * related to the "general" section. + * @param data the String array containing data for a specific table entry. + * @return the initialized "general" INI section as list of strings. An empty list otherwise. + */ + public static List processTableDataGeneralPst(String[] data) + { + List retVal = new ArrayList<>(); + if (data == null) { + return retVal; + } + + int id = SpriteTables.valueToInt(data, SpriteTables.COLUMN_ID, -1); + if (id < 0) { + return retVal; + } + int ellipse = SpriteTables.valueToInt(data, SpriteTables.COLUMN_PST_ELLIPSE, 16); + int space = SpriteTables.valueToInt(data, SpriteTables.COLUMN_PST_SPACE, 3); + + retVal.add("[general]"); + retVal.add("animation_type=F000"); + retVal.add("ellipse=" + ellipse); + retVal.add("personal_space=" + space); + + return retVal; + } + + /** + * Returns whether the specified {@code SpriteDecoder} class is compatible with the given animation id + * and any of the IniMap definitions. + */ + private static boolean isSpriteDecoderAvailable(Class spriteClass, int animationId, List iniList) + { + boolean retVal = false; + if (spriteClass == null || iniList == null) { + return retVal; + } + + try { + Constructor ctor = spriteClass.getConstructor(int.class, IniMap.class); + if (ctor != null) { + for (final IniMap ini : iniList) { + try { + retVal = (ctor.newInstance(animationId, ini).getClass() != null); + } catch (IllegalAccessException | InvocationTargetException | InstantiationException e) { + } + } + } + } catch (NoSuchMethodException e) { + } + + return retVal; + } + + /** + * Returns creature animation info from an existing INI file. + * @param animationId the creature animation id + * @return an list of {@link IniMap} instances with potential creature animation data. + * Returns {@code null} if no matching INI was found. + */ + private static List getAnimationInfoByIni(int animationId) + { + List retVal = new ArrayList<>(); + + animationId &= 0xffff; + String iniFile = String.format("%04X.INI", animationId); + if (ResourceFactory.resourceExists(iniFile)) { + retVal.add(new IniMap(ResourceFactory.getResourceEntry(iniFile), true)); + } + + return retVal; + } + + /** + * Returns creature animation info from hardcoded creature data. + * @param animationId the creature animation id + * @return an list of {@link IniMap} instance with potential creature animation data. + * Returns empty list if no creature data was found. + */ + private static List getAnimationInfoByTable(int animationId) + { + return SpriteTables.createIniMaps(animationId & 0xffff); + } + + /** + * Returns creature animation info based on ANISND.2DA data and analyzing potential slot ranges. + * May return false positives. + * @param animationId the creature animation id + * @return a list of {@link IniMap} instances with potential creature animation data. + * Returns {@code null} if no potential match was found. + */ + private static List getAnimationInfoByGuess(int animationId) + { + if (Profile.getGame() == Profile.Game.PST || Profile.getGame() == Profile.Game.PSTEE) { + return guessIniMapsPst(animationId); + } else { + return guessIniMaps(animationId); + } + } + + // Attempts to find potential non-PST-specific IniMap instances + private static List guessIniMaps(int animationId) + { + List retVal = new ArrayList<>(); + String resref = null; + String palette = null; + + // evaluate ANIMATE.SRC if available + ResourceEntry resEntry = ResourceFactory.getResourceEntry("ANIMATE.SRC"); + if (resEntry != null) { + IniMap anisrc = IniMapCache.get(resEntry); + if (anisrc != null) { + IniMapSection iniSection = anisrc.getUnnamedSection(); + if (iniSection != null) { + for (final Iterator iter = iniSection.iterator(); iter.hasNext(); ) { + IniMapEntry entry = iter.next(); + try { + String key = entry.getKey(); + int id = (key.startsWith("0x") || key.startsWith("0X")) ? Misc.toNumber(key.substring(2, key.length()), 16, -1) + : Misc.toNumber(key, -1); + if (id == animationId) { + String value = entry.getValue(); + if (id > 0x1000 && value.length() > 4) { + value = value.substring(0, 4); + } + resref = value; + break; + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + } + + if (resref == null) { + // evaluate ANISND.IDS if available + IdsMap anisnd = IdsMapCache.get("ANISND.IDS"); + if (anisnd != null) { + IdsMapEntry anisndEntry = anisnd.get(animationId); + if (anisndEntry != null) { + String[] elements = anisndEntry.getSymbol().split("\\s+"); + if (elements.length > 0 && elements[0].length() <= 8) { + resref = elements[0]; + int pos = resref.indexOf('_'); + if (pos > 0) { + // assuming underscore indicates a palette resref + palette = resref; + resref = resref.substring(0, pos); + } else if (animationId >= 0x1000 && resref.length() > 4) { + resref = resref.substring(0, 4); + } + } + } + } + } + + if (resref == null) { + return retVal; + } + + if (palette == null) { + palette = "*"; + } + + List tableEntries = new ArrayList<>(); + AnimationInfo.Type type = AnimationInfo.Type.typeOfId(animationId); + if (type == null) { + return retVal; + } + + ResourceEntry bamEntry; + switch (type) { + case EFFECT: + tableEntries.add(String.format("0x%04x %s 0 0 0 * %s * * * * * * * * *", animationId, resref, palette)); + break; + case MONSTER_QUADRANT: + if (ResourceFactory.resourceExists(resref + "G14.BAM")) { + tableEntries.add(String.format("0x%04x %s 1 32 5 * %s * * * * * * * * *", animationId, resref, palette)); + } + break; + case MONSTER_MULTI: + if (ResourceFactory.resourceExists(resref + "1908.BAM")) { + tableEntries.add(String.format("0x%04x %s 2 72 13 * %s * * * * 1 * * * *", animationId, resref, palette)); + } + break; + case MONSTER_MULTI_NEW: + if (ResourceFactory.resourceExists(resref + "G145.BAM")) { + tableEntries.add(String.format("0x%04x %s 2 32 5 * %s * * * * 1 * * * *", animationId, resref, palette)); + } else if (ResourceFactory.resourceExists(resref + "G1.BAM")) { + tableEntries.add(String.format("0x%04x %s 2 32 5 * %s * * * * 0 * * * *", animationId, resref, palette)); + } + break; + case MONSTER_LAYERED_SPELL: + resref = guessResourceRef(resref, "G1"); + bamEntry = ResourceFactory.getResourceEntry(resref + "G1.BAM"); + if (bamEntry != null) { + int falseColor = SpriteUtils.bamHasFalseColors(bamEntry) ? 1 : 0; + tableEntries.add(String.format("0x%04x %s 4 16 3 * %s * * * %d * * * * *", animationId, resref, palette, falseColor)); + } + break; + case MONSTER_ANKHEG: + resref = guessResourceRef(resref, "DG1"); + bamEntry = ResourceFactory.getResourceEntry(resref + "DG1.BAM"); + if (bamEntry != null) { + tableEntries.add(String.format("0x%04x %s 6 24 5 * %s * * * * * * * * *", animationId, resref, palette)); + } + break; + case TOWN_STATIC: + resref = guessResourceRef(resref, ""); + bamEntry = ResourceFactory.getResourceEntry(resref + ".BAM"); + if (bamEntry != null) { + int falseColor = SpriteUtils.bamHasFalseColors(bamEntry) ? 1 : 0; + tableEntries.add(String.format("0x%04x %s 7 16 3 * %s * * * %d * * * * *", animationId, resref, palette, falseColor)); + } + break; + case CHARACTER: + bamEntry = ResourceFactory.getResourceEntry(resref + "1G1.BAM"); + if (bamEntry != null) { + int falseColor = SpriteUtils.bamHasFalseColors(bamEntry) ? 1 : 0; + int split = ResourceFactory.resourceExists(resref + "1G15.BAM") ? 1 : 0; + tableEntries.add(String.format("0x%04x %s 8 16 3 * * * * * %d %d 1 * * *", animationId, resref, falseColor, split)); + } + break; + case CHARACTER_OLD: + bamEntry = ResourceFactory.getResourceEntry(resref + "1G1.BAM"); + if (bamEntry != null) { + int falseColor = SpriteUtils.bamHasFalseColors(bamEntry) ? 1 : 0; + tableEntries.add(String.format("0x%04x %s 9 16 3 * * * * * %d * * * * *", animationId, resref, falseColor)); + } + break; + case MONSTER: + resref = guessResourceRef(resref, "G1"); + bamEntry = ResourceFactory.getResourceEntry(resref + "G1.BAM"); + if (bamEntry != null) { + int split = ResourceFactory.resourceExists(resref + "G15.BAM") ? 1 : 0; + int falseColor = SpriteUtils.bamHasFalseColors(bamEntry) ? 1 : 0; + tableEntries.add(String.format("0x%04x %s 10 16 3 * %s * * * %d %d * * * *", animationId, resref, palette, falseColor, split)); + tableEntries.add(String.format("0x%04x %s 11 16 3 * %s * * * %d * * * * *", animationId, resref, palette, falseColor)); + } + break; + case MONSTER_OLD: + resref = guessResourceRef(resref, "G1"); + bamEntry = ResourceFactory.getResourceEntry(resref + "G1.BAM"); + if (bamEntry != null) { + int falseColor = SpriteUtils.bamHasFalseColors(bamEntry) ? 1 : 0; + tableEntries.add(String.format("0x%04x %s 11 16 3 * %s * * * %d * * * * *", animationId, resref, palette, falseColor)); + } + break; + case MONSTER_LAYERED: + resref = guessResourceRef(resref, "G1"); + bamEntry = ResourceFactory.getResourceEntry(resref + "G1.BAM"); + if (bamEntry != null) { + tableEntries.add(String.format("0x%04x %s 4 16 3 * * * * * * * * * * *", animationId, resref)); + } + break; + case MONSTER_LARGE: + resref = guessResourceRef(resref, "G1"); + bamEntry = ResourceFactory.getResourceEntry(resref + "G1.BAM"); + if (bamEntry != null) { + int falseColor = SpriteUtils.bamHasFalseColors(bamEntry) ? 1 : 0; + tableEntries.add(String.format("0x%04x %s 12 16 3 * %s * * * %d * * * * *", animationId, resref, palette, falseColor)); + } + break; + case MONSTER_LARGE_16: + resref = guessResourceRef(resref, "G1"); + bamEntry = ResourceFactory.getResourceEntry(resref + "G1.BAM"); + if (bamEntry != null) { + int falseColor = SpriteUtils.bamHasFalseColors(bamEntry) ? 1 : 0; + tableEntries.add(String.format("0x%04x %s 13 16 3 * %s * * * %d * * * * *", animationId, resref, palette, falseColor)); + } + break; + case AMBIENT_STATIC: + resref = guessResourceRef(resref, "G1"); + bamEntry = ResourceFactory.getResourceEntry(resref + "G1.BAM"); + if (bamEntry != null) { + int falseColor = SpriteUtils.bamHasFalseColors(bamEntry) ? 1 : 0; + tableEntries.add(String.format("0x%04x %s 14 16 3 * %s * * * %d * * * * *", animationId, resref, palette, falseColor)); + } + break; + case AMBIENT: + resref = guessResourceRef(resref, "G1"); + bamEntry = ResourceFactory.getResourceEntry(resref + "G1.BAM"); + if (bamEntry != null) { + int falseColor = SpriteUtils.bamHasFalseColors(bamEntry) ? 1 : 0; + tableEntries.add(String.format("0x%04x %s 15 16 3 * %s * * * %d * * * * *", animationId, resref, palette, falseColor)); + } + break; + case FLYING: + resref = guessResourceRef(resref, ""); + bamEntry = ResourceFactory.getResourceEntry(resref + ".BAM"); + if (bamEntry != null) { + int falseColor = SpriteUtils.bamHasFalseColors(bamEntry) ? 1 : 0; + tableEntries.add(String.format("0x%04x %s 16 16 3 * %s * * * %d * * * * *", animationId, resref, palette, falseColor)); + } + break; + case MONSTER_ICEWIND: + { + boolean found = false; + if (resref.length() >= 4 && !found) { + for (final String suffix : new String[] { "A1", "A2", "A3", "A4", "CA", "DE", "GH", "GU", "SC", "SD", "SL", "SP", "TW", "WK" }) { + if (ResourceFactory.resourceExists(resref + suffix + ".BAM")) { + found = true; + break; + } + } + } + if (found) { + tableEntries.add(String.format("0x%04x %s 17 24 3 * %s * * * * * * * * *", animationId, resref, palette)); + } + break; + } + default: + } + + if (!tableEntries.isEmpty()) { + for (final String line : tableEntries) { + StringBuilder sb = new StringBuilder(); + sb.append("2DA V1.0").append('\n'); + sb.append("*").append('\n'); + sb.append(" RESREF TYPE ELLIPSE SPACE BLENDING PALETTE PALETTE2 RESREF2 TRANSLUCENT CLOWN SPLIT HELMET WEAPON HEIGHT HEIGHT_SHIELD").append('\n'); + sb.append(line).append('\n'); + ResourceEntry entry = new BufferedResourceEntry(ByteBuffer.wrap(sb.toString().getBytes()), Integer.toString(animationId, 16) + ".2DA"); + Table2da table = new Table2da(entry); + retVal.addAll(SpriteTables.processTable(Profile.getGame(), table, animationId)); + } + } + + return retVal; + } + + // Helper method: attempts to find an existing resource with the specified name parts. + // Returns the resref of the matching resource. Returns the original resref otherwise. + private static String guessResourceRef(String resref, String suffix) + { + String retVal = resref; + if (retVal == null) { + return retVal; + } + + if (suffix == null) { + suffix = ""; + } + + while (retVal.length() >= 4) { + if (ResourceFactory.resourceExists(retVal + suffix + ".BAM")) { + return retVal; + } + retVal = retVal.substring(0, resref.length() - 1); + } + + return resref; + } + + // Attempts to find potential PST-specific IniMap instances + private static List guessIniMapsPst(int animationId) + { + List retVal = new ArrayList<>(); + String resref = null; + + IniMap resIni = IniMapCache.get("RESDATA.INI", true); + if (resIni == null) { + return retVal; + } + + // only regular animations are considered + int id = animationId & 0x0fff; + IniMapSection iniSection = resIni.getSection(Integer.toString(id)); + if (iniSection == null) { + iniSection = resIni.getSection("0x" + Integer.toString(id, 16)); + } + if (iniSection == null) { + return retVal; + } + + int clown = 0; + for (final Sequence seq : Sequence.values()) { + String cmd = MonsterPlanescapeDecoder.getActionCommand(seq); + if (cmd != null) { + String key = iniSection.getAsString(cmd); + if (key != null && key.length() >= 7) { + ResourceEntry bamEntry = ResourceFactory.getResourceEntry(key + "b.bam"); + if (bamEntry != null) { + clown = SpriteUtils.bamHasFalseColors(bamEntry) ? 1 : 0; + resref = key.substring(0, 1) + key.substring(4, key.length()) + "b"; + break; + } + } + } + } + + if (resref != null) { + int armor = iniSection.getAsInteger("armor", 0); + int bestiary = iniSection.getAsInteger("bestiary", 0); + + StringBuilder sb = new StringBuilder(); + sb.append("2DA V1.0").append('\n'); + sb.append("*").append('\n'); + sb.append(" RESREF RESREF2 TYPE ELLIPSE SPACE CLOWN ARMOR BESTIARY").append('\n'); + sb.append(String.format("0x%04x %s * 18 16 3 %d %d %d", id, resref, clown, armor, bestiary)).append('\n'); + ResourceEntry entry = new BufferedResourceEntry(ByteBuffer.wrap(sb.toString().getBytes()), Integer.toString(animationId, 16) + ".2DA"); + Table2da table = new Table2da(entry); + retVal = SpriteTables.processTable(Profile.getGame(), table, animationId); + } + + return retVal; + } + + + private SpriteUtils() + { + } +} diff --git a/src/org/infinity/resource/dlg/DlgItem.java b/src/org/infinity/resource/dlg/DlgItem.java index 5ed963f88..a5ce144a3 100644 --- a/src/org/infinity/resource/dlg/DlgItem.java +++ b/src/org/infinity/resource/dlg/DlgItem.java @@ -15,6 +15,7 @@ import javax.swing.tree.TreeNode; import org.infinity.datatype.Flag; +import org.infinity.datatype.IsNumeric; import org.infinity.datatype.SectionCount; import org.infinity.gui.BrowserMenuBar; import org.infinity.icon.Icons; @@ -87,7 +88,6 @@ public DlgItem(DlgTreeModel parent, DlgResource dlg) @Override public void traverseChildren(Consumer action) { states.forEach(action); } - // @Override public StateItem getChildAt(int childIndex) { return states.get(childIndex); } @@ -108,12 +108,9 @@ public DlgItem(DlgTreeModel parent, DlgResource dlg) @Override public Enumeration children() { return enumeration(states); } - // - // @Override public Iterator iterator() { return states.iterator(); } - // /** * Extracts specified {@link SectionCount} attribute from dialog. @@ -124,8 +121,8 @@ public DlgItem(DlgTreeModel parent, DlgResource dlg) private int getAttribute(String attrName) { final StructEntry entry = getDialog().getAttribute(attrName, false); - if (entry instanceof SectionCount) { - return ((SectionCount)entry).getValue(); + if (entry instanceof IsNumeric) { + return ((IsNumeric)entry).getValue(); } return 0; } diff --git a/src/org/infinity/resource/dlg/DlgResource.java b/src/org/infinity/resource/dlg/DlgResource.java index 455cd1ca9..c7e1a36ac 100644 --- a/src/org/infinity/resource/dlg/DlgResource.java +++ b/src/org/infinity/resource/dlg/DlgResource.java @@ -122,7 +122,6 @@ public DlgResource(ResourceEntry entry) throws Exception super(entry); } - // @Override public AddRemovable[] getPrototypes() throws Exception { @@ -135,9 +134,7 @@ public AddRemovable confirmAddEntry(AddRemovable entry) throws Exception { return entry; } - // - // @Override public int getViewerTabCount() { @@ -181,9 +178,7 @@ public boolean viewerTabAddedBefore(int index) { return (index == 0); } - // - // @Override public void write(OutputStream os) throws IOException { @@ -209,9 +204,7 @@ public void write(OutputStream os) throws IOException } } } - // - // @Override public void actionPerformed(ActionEvent e) { @@ -238,9 +231,7 @@ public void actionPerformed(ActionEvent e) } } } - // - // @Override protected void viewerInitialized(StructViewer viewer) { @@ -278,9 +269,7 @@ protected void datatypeRemoved(AddRemovable datatype) { updateReferences(datatype, false); } - // - // @Override public int read(ByteBuffer buffer, int offset) throws Exception { @@ -357,7 +346,6 @@ public int read(ByteBuffer buffer, int offset) throws Exception } return offset + textSize; } - // /** * Returns state with specified number from this dialog. @@ -471,9 +459,9 @@ private void updateReferences(AddRemovable datatype, boolean added) { if (datatype instanceof StateTrigger) { StateTrigger trigger = (StateTrigger)datatype; - int ofsStates = ((SectionOffset)getAttribute(DLG_OFFSET_STATES)).getValue(); - int numStates = ((SectionCount)getAttribute(DLG_NUM_STATES)).getValue(); - int ofsTriggers = ((SectionOffset)getAttribute(DLG_OFFSET_STATE_TRIGGERS)).getValue(); + int ofsStates = ((IsNumeric)getAttribute(DLG_OFFSET_STATES)).getValue(); + int numStates = ((IsNumeric)getAttribute(DLG_NUM_STATES)).getValue(); + int ofsTriggers = ((IsNumeric)getAttribute(DLG_OFFSET_STATE_TRIGGERS)).getValue(); int idxTrigger = (trigger.getOffset() - ofsTriggers) / trigger.getSize(); // adjusting state trigger references @@ -500,9 +488,9 @@ private void updateReferences(AddRemovable datatype, boolean added) } } else if (datatype instanceof ResponseTrigger) { ResponseTrigger trigger = (ResponseTrigger)datatype; - int ofsTrans = ((SectionOffset)getAttribute(DLG_OFFSET_RESPONSES)).getValue(); - int numTrans = ((SectionCount)getAttribute(DLG_NUM_RESPONSES)).getValue(); - int ofsTriggers = ((SectionOffset)getAttribute(DLG_OFFSET_RESPONSE_TRIGGERS)).getValue(); + int ofsTrans = ((IsNumeric)getAttribute(DLG_OFFSET_RESPONSES)).getValue(); + int numTrans = ((IsNumeric)getAttribute(DLG_NUM_RESPONSES)).getValue(); + int ofsTriggers = ((IsNumeric)getAttribute(DLG_OFFSET_RESPONSE_TRIGGERS)).getValue(); int idxTrigger = (trigger.getOffset() - ofsTriggers) / trigger.getSize(); // adjusting response trigger references @@ -533,9 +521,9 @@ private void updateReferences(AddRemovable datatype, boolean added) } } else if (datatype instanceof Action) { Action action = (Action)datatype; - int ofsTrans = ((SectionOffset)getAttribute(DLG_OFFSET_RESPONSES)).getValue(); - int numTrans = ((SectionCount)getAttribute(DLG_NUM_RESPONSES)).getValue(); - int ofsActions = ((SectionOffset)getAttribute(DLG_OFFSET_ACTIONS)).getValue(); + int ofsTrans = ((IsNumeric)getAttribute(DLG_OFFSET_RESPONSES)).getValue(); + int numTrans = ((IsNumeric)getAttribute(DLG_NUM_RESPONSES)).getValue(); + int ofsActions = ((IsNumeric)getAttribute(DLG_OFFSET_ACTIONS)).getValue(); int idxAction = (action.getOffset() - ofsActions) / action.getSize(); // adjusting action references diff --git a/src/org/infinity/resource/dlg/DlgTreeModel.java b/src/org/infinity/resource/dlg/DlgTreeModel.java index cbb0f2d66..eb18ce482 100644 --- a/src/org/infinity/resource/dlg/DlgTreeModel.java +++ b/src/org/infinity/resource/dlg/DlgTreeModel.java @@ -57,7 +57,6 @@ final class DlgTreeModel implements TreeModel, TreeNode, TableModelListener, Pro @Override public String toString() { return "Dialogues"; } - // @Override public DlgTreeModel getRoot() { return this; } @@ -129,9 +128,7 @@ public void removeTreeModelListener(TreeModelListener l) listeners.remove(l); } } - // - // @Override public ItemBase getChildAt(int childIndex) { @@ -181,9 +178,7 @@ public ItemBase nextElement() } }; } - // - // @Override public void tableChanged(TableModelEvent e) { @@ -213,9 +208,7 @@ public void tableChanged(TableModelEvent e) } } } - // - // @Override public void propertyChange(PropertyChangeEvent e) { @@ -265,9 +258,7 @@ public void propertyChange(PropertyChangeEvent e) } } } - // - // /** * Updates tree when specified state or transition entry changed. * @@ -337,7 +328,6 @@ private void removeItem(ItemBase item) } } - // /** * Changes tree structure accourding to the changes in the * {@link State#DLG_STATE_FIRST_RESPONSE_INDEX} property of the state. @@ -481,9 +471,7 @@ private void removeChildTransitions(StateItem parent, int fromIndex, int toIndex items.clear(); fireTreeNodesRemoved(parent.getPath(), childIndices, children); } - // - // /** * Changes tree structure accourding to the changes in the * {@link Transition#DLG_TRANS_NEXT_DIALOG}, @@ -507,9 +495,7 @@ private void changeTransition(List items) fireTreeStructureChanged(item.getPath()); } } - // - // private void nodeChanged(ItemBase node) { final TreeNode parent = node.getParent(); @@ -561,8 +547,6 @@ private void fireTreeStructureChanged(TreePath path) } } } - // - // /** * Translates child struct of the dialog that this tree represents, to GUI item. diff --git a/src/org/infinity/resource/dlg/ItemBase.java b/src/org/infinity/resource/dlg/ItemBase.java index 4392dafc4..834f85400 100644 --- a/src/org/infinity/resource/dlg/ItemBase.java +++ b/src/org/infinity/resource/dlg/ItemBase.java @@ -88,13 +88,11 @@ public TreePath getPath() */ public abstract void traverseChildren(Consumer action); - // @Override public abstract ItemBase getChildAt(int childIndex); @Override public abstract Enumeration children(); - // /** * Returns string that can be used to display in the tree. @@ -129,23 +127,19 @@ private String getText(StringRef value) /** Auxiliary class, being the parent for states, for a type safety. */ abstract class StateOwnerItem extends ItemBase { - // @Override public abstract StateItem getChildAt(int childIndex); @Override public abstract Enumeration children(); - // } /** Auxiliary class, being the parent for transitions, for a type safety. */ abstract class TransitionOwnerItem extends ItemBase implements Iterable { - // @Override public abstract TransitionItem getChildAt(int childIndex); @Override public abstract Enumeration children(); - // } \ No newline at end of file diff --git a/src/org/infinity/resource/dlg/OrphanStates.java b/src/org/infinity/resource/dlg/OrphanStates.java index 1386a4790..17ce2f361 100644 --- a/src/org/infinity/resource/dlg/OrphanStates.java +++ b/src/org/infinity/resource/dlg/OrphanStates.java @@ -30,7 +30,6 @@ final class OrphanStates extends StateOwnerItem implements Iterable @Override public String toString() { return "Orphan states"; } - // @Override public StateItem getChildAt(int childIndex) { return states.get(childIndex); } @@ -51,14 +50,10 @@ final class OrphanStates extends StateOwnerItem implements Iterable @Override public Enumeration children() { return enumeration(states); } - // - // @Override public Iterator iterator() { return states.iterator(); } - // - // @Override public TreeItemEntry getEntry() { return null; } @@ -76,5 +71,4 @@ final class OrphanStates extends StateOwnerItem implements Iterable @Override public void traverseChildren(Consumer action) { states.forEach(action); } - // } diff --git a/src/org/infinity/resource/dlg/OrphanTransitions.java b/src/org/infinity/resource/dlg/OrphanTransitions.java index 7e4d0c893..dee179edd 100644 --- a/src/org/infinity/resource/dlg/OrphanTransitions.java +++ b/src/org/infinity/resource/dlg/OrphanTransitions.java @@ -30,7 +30,6 @@ final class OrphanTransitions extends TransitionOwnerItem @Override public String toString() { return "Orphan responses"; } - // @Override public TransitionItem getChildAt(int childIndex) { return trans.get(childIndex); } @@ -51,14 +50,10 @@ final class OrphanTransitions extends TransitionOwnerItem @Override public Enumeration children() { return enumeration(trans); } - // - // @Override public Iterator iterator() { return trans.iterator(); } - // - // @Override public TreeItemEntry getEntry() { return null; } @@ -76,5 +71,4 @@ final class OrphanTransitions extends TransitionOwnerItem @Override public void traverseChildren(Consumer action) { trans.forEach(action); } - // } diff --git a/src/org/infinity/resource/dlg/State.java b/src/org/infinity/resource/dlg/State.java index c8022adba..6a71e95a4 100644 --- a/src/org/infinity/resource/dlg/State.java +++ b/src/org/infinity/resource/dlg/State.java @@ -7,6 +7,7 @@ import java.nio.ByteBuffer; import org.infinity.datatype.DecNumber; +import org.infinity.datatype.IsNumeric; import org.infinity.datatype.StringRef; import org.infinity.resource.AbstractStruct; import org.infinity.resource.AddRemovable; @@ -40,7 +41,6 @@ public final class State extends AbstractStruct implements AddRemovable, TreeIte nr = count; } - // @Override public DlgResource getParent() { return (DlgResource)super.getParent(); } @@ -52,11 +52,10 @@ public StringRef getAssociatedText() { return (StringRef)getAttribute(DLG_STATE_RESPONSE, false); } - // public int getFirstTrans() { - return ((DecNumber)getAttribute(DLG_STATE_FIRST_RESPONSE_INDEX, false)).getValue(); + return ((IsNumeric)getAttribute(DLG_STATE_FIRST_RESPONSE_INDEX, false)).getValue(); } public int getNumber() @@ -66,12 +65,12 @@ public int getNumber() public int getTransCount() { - return ((DecNumber)getAttribute(DLG_STATE_NUM_RESPONSES, false)).getValue(); + return ((IsNumeric)getAttribute(DLG_STATE_NUM_RESPONSES, false)).getValue(); } public int getTriggerIndex() { - return ((DecNumber)getAttribute(DLG_STATE_TRIGGER_INDEX, false)).getValue(); + return ((IsNumeric)getAttribute(DLG_STATE_TRIGGER_INDEX, false)).getValue(); } //--------------------- Begin Interface AddRemovable --------------------- diff --git a/src/org/infinity/resource/dlg/StateItem.java b/src/org/infinity/resource/dlg/StateItem.java index a72fcf687..d96c4e6c4 100644 --- a/src/org/infinity/resource/dlg/StateItem.java +++ b/src/org/infinity/resource/dlg/StateItem.java @@ -80,7 +80,6 @@ public void traverseChildren(Consumer action) /** Returns technical name of state item which uniquely identifying it within dialog. */ public String getName() { return state.getName(); } - // @Override public TransitionItem getChildAt(int childIndex) { @@ -113,12 +112,9 @@ public Enumeration children() { return enumeration(getAllowsChildren() ? trans : emptyList()); } - // - // @Override public Iterator iterator() { return trans.iterator(); } - // @Override public String toString() { return getText(state); } diff --git a/src/org/infinity/resource/dlg/Transition.java b/src/org/infinity/resource/dlg/Transition.java index ecce51b3d..e73d9b50d 100644 --- a/src/org/infinity/resource/dlg/Transition.java +++ b/src/org/infinity/resource/dlg/Transition.java @@ -8,6 +8,7 @@ import org.infinity.datatype.DecNumber; import org.infinity.datatype.Flag; +import org.infinity.datatype.IsNumeric; import org.infinity.datatype.ResourceRef; import org.infinity.datatype.StringRef; import org.infinity.resource.AbstractStruct; @@ -46,7 +47,6 @@ public final class Transition extends AbstractStruct implements AddRemovable, Tr this.nr = nr; } - // @Override public DlgResource getParent() { return (DlgResource)super.getParent(); } @@ -59,12 +59,11 @@ public StringRef getAssociatedText() { return (StringRef)getAttribute(DLG_TRANS_TEXT, false); } - // public int getActionIndex() { if (getFlag().isFlagSet(2)) { - return ((DecNumber)getAttribute(DLG_TRANS_ACTION_INDEX, false)).getValue(); + return ((IsNumeric)getAttribute(DLG_TRANS_ACTION_INDEX, false)).getValue(); } else { return -1; } @@ -87,7 +86,7 @@ public ResourceRef getNextDialog() public int getNextDialogState() { - return ((DecNumber)getAttribute(DLG_TRANS_NEXT_DIALOG_STATE, false)).getValue(); + return ((IsNumeric)getAttribute(DLG_TRANS_NEXT_DIALOG_STATE, false)).getValue(); } public int getNumber() @@ -98,7 +97,7 @@ public int getNumber() public int getTriggerIndex() { if (getFlag().isFlagSet(1)) { - return ((DecNumber)getAttribute(DLG_TRANS_TRIGGER_INDEX, false)).getValue(); + return ((IsNumeric)getAttribute(DLG_TRANS_TRIGGER_INDEX, false)).getValue(); } else { return -1; } diff --git a/src/org/infinity/resource/dlg/TransitionItem.java b/src/org/infinity/resource/dlg/TransitionItem.java index 3abd05fd1..4015ec67f 100644 --- a/src/org/infinity/resource/dlg/TransitionItem.java +++ b/src/org/infinity/resource/dlg/TransitionItem.java @@ -86,7 +86,6 @@ public void traverseChildren(Consumer action) /** Returns technical name of transition item which uniquely identifying it within dialog. */ public String getName() { return trans.getName(); } - // @Override public StateItem getChildAt(int childIndex) { return isMain() && childIndex == 0 ? nextState : null; } @@ -111,7 +110,6 @@ public Enumeration children() { return enumeration(isLeaf() ? emptyList() : singletonList(nextState)); } - // @Override public String toString() diff --git a/src/org/infinity/resource/dlg/TreeViewer.java b/src/org/infinity/resource/dlg/TreeViewer.java index f54932e35..59974bf6c 100644 --- a/src/org/infinity/resource/dlg/TreeViewer.java +++ b/src/org/infinity/resource/dlg/TreeViewer.java @@ -208,7 +208,6 @@ private void maybeShowPopup(MouseEvent e) add(splitv, BorderLayout.CENTER); } - // @Override public void actionPerformed(ActionEvent e) { @@ -277,9 +276,7 @@ public void actionPerformed(ActionEvent e) } } } - // - // @Override public void valueChanged(TreeSelectionEvent e) { @@ -297,9 +294,7 @@ public void valueChanged(TreeSelectionEvent e) } } } - // - // @Override public void propertyChange(PropertyChangeEvent event) { @@ -314,7 +309,6 @@ public void propertyChange(PropertyChangeEvent event) } } } - // /** * Selects specified dialog state or transition in the tree. If such entry not diff --git a/src/org/infinity/resource/dlg/Viewer.java b/src/org/infinity/resource/dlg/Viewer.java index a81df11e8..fda071300 100644 --- a/src/org/infinity/resource/dlg/Viewer.java +++ b/src/org/infinity/resource/dlg/Viewer.java @@ -72,7 +72,6 @@ final class Viewer extends JPanel implements ActionListener, ItemListener, Table private static final Color NORMAL_COLOR = Color.BLACK; private static final Color ERROR_COLOR = Color.RED; - // private final DlgResource dlg; /** List of all states, found in {@link #dlg}. */ private final List stateList = new ArrayList<>(); @@ -93,14 +92,12 @@ final class Viewer extends JPanel implements ActionListener, ItemListener, Table * will be do when game process entering to related state. */ private final List actionList = new ArrayList<>(); - // /** State that editor shows right now. */ private State currentState; /** Transition that editor shows right now. */ private Transition currentTrans; - // /** * Stack of states, that were selected by the {@link #CtrlSelect} button. The * {@link #CtrlReturn} button allows return to one of this states together with @@ -115,9 +112,7 @@ final class Viewer extends JPanel implements ActionListener, ItemListener, Table private final ArrayDeque lastTransitions = new ArrayDeque<>(); private DlgResource undoDlg; private boolean alive = true; - // - // private final ButtonPanel buttonPanel = new ButtonPanel(); private final DlgPanel stateTextPanel, stateTriggerPanel, transTextPanel, transTriggerPanel, transActionPanel; private final JMenuItem ifindall = new JMenuItem("in all DLG files"); @@ -127,7 +122,6 @@ final class Viewer extends JPanel implements ActionListener, ItemListener, Table private final JTextField tfResponse = new JTextField(4); private final TitledBorder bostate = new TitledBorder("State"); private final TitledBorder botrans = new TitledBorder("Response"); - // Viewer(DlgResource dlg) { @@ -317,7 +311,7 @@ public void itemStateChanged(ItemEvent event) List files = ResourceFactory.getResources("DLG"); new DialogSearcher(files, getTopLevelAncestor()); } else if (bpmFind.getSelectedItem() == ifindthis) { - List files = new ArrayList(); + List files = new ArrayList<>(); files.add(dlg.getResourceEntry()); new DialogSearcher(files, getTopLevelAncestor()); } diff --git a/src/org/infinity/resource/gam/GamResource.java b/src/org/infinity/resource/gam/GamResource.java index 9cc6fb137..3ff8a389c 100644 --- a/src/org/infinity/resource/gam/GamResource.java +++ b/src/org/infinity/resource/gam/GamResource.java @@ -121,7 +121,7 @@ public final class GamResource extends AbstractStruct implements Resource, HasCh "Normal windows", "Party AI disabled", "Larger text window", "Largest text window", null, "Fullscreen mode", "Left pane hidden", "Right pane hidden", "Unsupported"}; public static final String[] s_configuration_iwd2 = { - "Normal windows", "Party AI disabled", null, null, null, "Fullscreen mode", null, + "Normal windows", "Party AI enabled", null, null, null, "Fullscreen mode", "Toolbar hidden", "Console hidden", "Automap notes hidden"}; public static final String[] s_version_bg1 = {"Restrict XP to BG1 limit", "Restrict XP to TotSC limit"}; public static final String[] s_familiar_owner = { @@ -136,7 +136,6 @@ public GamResource(ResourceEntry entry) throws Exception super(entry); } - // @Override public AddRemovable[] getPrototypes() throws Exception { @@ -169,9 +168,7 @@ public AddRemovable confirmAddEntry(AddRemovable entry) throws Exception } return entry; } - // - // @Override public int getViewerTabCount() { @@ -226,17 +223,13 @@ public boolean viewerTabAddedBefore(int index) { return index == 0 || Profile.getEngine() == Profile.Engine.PST && index == 1; } - // - // @Override public void write(OutputStream os) throws IOException { super.writeFlatFields(os); } - // - // @Override protected void viewerInitialized(StructViewer viewer) { @@ -284,9 +277,7 @@ protected void datatypeRemovedInChild(AbstractStruct child, AddRemovable datatyp hexViewer.dataModified(); } } - // - // @Override public int read(ByteBuffer buffer, int offset) throws Exception { @@ -306,7 +297,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 HashBitmap(buffer, offset + 28, 2, GAM_VIEW_PLAYER_AREA, PartyNPC.m_partyOrder)); + addField(new HashBitmap(buffer, offset + 28, 2, GAM_VIEW_PLAYER_AREA, PartyNPC.m_partyOrder, true, true)); 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); @@ -404,7 +395,7 @@ else if (Profile.getEngine() == Profile.Engine.BG2 || Profile.isEnhancedEdition( } } else if (Profile.getEngine() == Profile.Engine.IWD2) { // V2.2 (V1.1 & V2.0 in BIFF) - addField(new Unknown(buffer, offset + 84, 4)); + addField(new DecNumber(buffer, offset + 84, 4, GAM_REPUTATION)); addField(new ResourceRef(buffer, offset + 88, GAM_MASTER_AREA, "ARE")); addField(new Flag(buffer, offset + 96, 4, GAM_CONFIGURATION, s_configuration_iwd2)); numIWD2 = new SectionCount(buffer, offset + 100, 4, GAM_NUM_UNKNOWN, UnknownSection3.class); @@ -553,7 +544,6 @@ else if (Profile.getEngine() == Profile.Engine.IWD2) { // V2.2 (V1.1 & V2.0 in B return offset; } - // private void updateOffsets() { diff --git a/src/org/infinity/resource/gam/JournalEntry.java b/src/org/infinity/resource/gam/JournalEntry.java index 9571a4adf..534a52fd6 100644 --- a/src/org/infinity/resource/gam/JournalEntry.java +++ b/src/org/infinity/resource/gam/JournalEntry.java @@ -5,6 +5,7 @@ package org.infinity.resource.gam; import java.nio.ByteBuffer; +import java.util.TreeMap; import org.infinity.datatype.DecNumber; import org.infinity.datatype.Flag; @@ -15,7 +16,6 @@ import org.infinity.resource.AbstractStruct; import org.infinity.resource.AddRemovable; import org.infinity.resource.Profile; -import org.infinity.util.LongIntegerHashMap; import org.infinity.util.io.StreamUtils; public final class JournalEntry extends AbstractStruct implements AddRemovable @@ -28,7 +28,7 @@ public final class JournalEntry extends AbstractStruct implements AddRemovable public static final String GAM_JOURNAL_SECTION = "Section"; public static final String GAM_JOURNAL_SOURCE = "Text source"; - private static final LongIntegerHashMap m_source = new LongIntegerHashMap(); + private static final TreeMap m_source = new TreeMap<>(); public static final String s_section[] = new String[]{"User notes", "Quests", "Done quests", "Journal"}; diff --git a/src/org/infinity/resource/gam/KillVariable.java b/src/org/infinity/resource/gam/KillVariable.java index 70c52d92f..6d91d1d8a 100644 --- a/src/org/infinity/resource/gam/KillVariable.java +++ b/src/org/infinity/resource/gam/KillVariable.java @@ -7,10 +7,9 @@ import java.nio.ByteBuffer; import org.infinity.resource.AbstractStruct; -import org.infinity.resource.AddRemovable; import org.infinity.util.io.StreamUtils; -public final class KillVariable extends Variable implements AddRemovable +public final class KillVariable extends Variable { // GAM/KillVariable-specific field labels public static final String GAM_KILLVAR = "Kill variable"; diff --git a/src/org/infinity/resource/gam/PartyNPC.java b/src/org/infinity/resource/gam/PartyNPC.java index 7744ddad7..1978ef1ae 100644 --- a/src/org/infinity/resource/gam/PartyNPC.java +++ b/src/org/infinity/resource/gam/PartyNPC.java @@ -5,6 +5,7 @@ package org.infinity.resource.gam; import java.nio.ByteBuffer; +import java.util.TreeMap; import javax.swing.JComponent; @@ -26,7 +27,7 @@ import org.infinity.resource.Profile; import org.infinity.resource.StructEntry; import org.infinity.resource.cre.CreResource; -import org.infinity.util.LongIntegerHashMap; +import org.infinity.util.IdsMapEntry; import org.infinity.util.io.StreamUtils; public class PartyNPC extends AbstractStruct implements HasViewerTabs, AddRemovable @@ -84,10 +85,11 @@ public class PartyNPC extends AbstractStruct implements HasViewerTabs, AddRemova 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"; - public static final LongIntegerHashMap m_partyOrder = new LongIntegerHashMap(); -// private static final LongIntegerHashMap m_selected = new LongIntegerHashMap(); + public static final TreeMap m_partyOrder = new TreeMap<>(); +// private static final TreeMap m_selected = new TreeMap<>(); - private static final String[] s_selected = {"Not selected", "Selected", null, null, null, null, null, null, null, null, null, null, null, null, null, null, "Dead" }; + 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 { m_partyOrder.put(0L, "Slot 1"); @@ -96,8 +98,7 @@ public class PartyNPC extends AbstractStruct implements HasViewerTabs, AddRemova m_partyOrder.put(3L, "Slot 4"); m_partyOrder.put(4L, "Slot 5"); m_partyOrder.put(5L, "Slot 6"); -// partyOrder.put(0x8000L, "In party, dead"); - m_partyOrder.put(0xffffL, "Not in party"); + m_partyOrder.put(-1L, "Not in party"); // m_selected.put(0L, "Not selected"); // m_selected.put(1L, "Selected"); @@ -194,7 +195,7 @@ void updateCREOffset() public int read(ByteBuffer buffer, int offset) throws Exception { 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)); + addField(new HashBitmap(buffer, offset + 2, 2, GAM_NPC_PARTY_POSITION, m_partyOrder, true, true)); 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)); @@ -210,13 +211,16 @@ public int read(ByteBuffer buffer, int offset) throws Exception addField(new DecNumber(buffer, offset + 36, 2, GAM_NPC_VIEWPORT_X)); addField(new DecNumber(buffer, offset + 38, 2, GAM_NPC_VIEWPORT_Y)); + IdsBitmap bitmap; + final IdsMapEntry entryNone = new IdsMapEntry(-1L, "NONE"); if (Profile.getEngine() == Profile.Engine.BG1) { addField(new DecNumber(buffer, offset + 40, 2, GAM_NPC_MODAL_STATE)); addField(new DecNumber(buffer, offset + 42, 2, GAM_NPC_HAPPINESS)); addField(new Unknown(buffer, offset + 44, 96, COMMON_UNUSED)); for (int i = 0; i < 4; i++) { - addField(new IdsBitmap(buffer, offset + 140 + (i * 2), 2, - String.format(GAM_NPC_QUICK_WEAPON_SLOT_FMT, i+1), "SLOTS.IDS")); + bitmap = addField(new IdsBitmap(buffer, offset + 140 + (i * 2), 2, + String.format(GAM_NPC_QUICK_WEAPON_SLOT_FMT, i+1), "SLOTS.IDS", true, false, true)); + bitmap.addIdsMapEntry(entryNone); } for (int i = 0; i < 4; i++) { addField(new DecNumber(buffer, offset + 148 + (i * 2), 2, @@ -227,8 +231,9 @@ public int read(ByteBuffer buffer, int offset) throws Exception String.format(GAM_NPC_QUICK_SPELL_FMT, i+1), "SPL")); } for (int i = 0; i < 3; i++) { - addField(new IdsBitmap(buffer, offset + 180 + (i * 2), 2, - String.format(GAM_NPC_QUICK_ITEM_SLOT_FMT, i+1), "SLOTS.IDS")); + bitmap = addField(new IdsBitmap(buffer, offset + 180 + (i * 2), 2, + String.format(GAM_NPC_QUICK_ITEM_SLOT_FMT, i+1), "SLOTS.IDS", true, false, true)); + bitmap.addIdsMapEntry(entryNone); } for (int i = 0; i < 3; i++) { addField(new DecNumber(buffer, offset + 186 + (i * 2), 2, @@ -253,8 +258,9 @@ else if (Profile.getEngine() == Profile.Engine.BG2 || Profile.isEnhancedEdition( addField(new DecNumber(buffer, offset + 138, 2, COMMON_UNKNOWN)); } for (int i = 0; i < 4; i++) { - addField(new IdsBitmap(buffer, offset + 140 + (i * 2), 2, - String.format(GAM_NPC_QUICK_WEAPON_SLOT_FMT, i+1), "SLOTS.IDS")); + bitmap = addField(new IdsBitmap(buffer, offset + 140 + (i * 2), 2, + String.format(GAM_NPC_QUICK_WEAPON_SLOT_FMT, i+1), "SLOTS.IDS", true, false, true)); + bitmap.addIdsMapEntry(entryNone); } for (int i = 0; i < 4; i++) { addField(new DecNumber(buffer, offset + 148 + (i * 2), 2, @@ -265,8 +271,9 @@ else if (Profile.getEngine() == Profile.Engine.BG2 || Profile.isEnhancedEdition( String.format(GAM_NPC_QUICK_SPELL_FMT, i+1), "SPL")); } for (int i = 0; i < 3; i++) { - addField(new IdsBitmap(buffer, offset + 180 + (i * 2), 2, - String.format(GAM_NPC_QUICK_ITEM_SLOT_FMT, i+1), "SLOTS.IDS")); + bitmap = addField(new IdsBitmap(buffer, offset + 180 + (i * 2), 2, + String.format(GAM_NPC_QUICK_ITEM_SLOT_FMT, i+1), "SLOTS.IDS", true, false, true)); + bitmap.addIdsMapEntry(entryNone); } for (int i = 0; i < 3; i++) { addField(new DecNumber(buffer, offset + 186 + (i * 2), 2, @@ -312,8 +319,9 @@ else if (Profile.getEngine() == Profile.Engine.IWD) { addField(new DecNumber(buffer, offset + 40, 2, GAM_NPC_MODAL_STATE)); addField(new Unknown(buffer, offset + 42, 98)); for (int i = 0; i < 4; i++) { - addField(new IdsBitmap(buffer, offset + 140 + (i * 2), 2, - String.format(GAM_NPC_QUICK_WEAPON_SLOT_FMT, i+1), "SLOTS.IDS")); + bitmap = addField(new IdsBitmap(buffer, offset + 140 + (i * 2), 2, + String.format(GAM_NPC_QUICK_WEAPON_SLOT_FMT, i+1), "SLOTS.IDS", true, false, true)); + bitmap.addIdsMapEntry(entryNone); } for (int i = 0; i < 4; i++) { addField(new DecNumber(buffer, offset + 148 + (i * 2), 2, @@ -324,8 +332,9 @@ else if (Profile.getEngine() == Profile.Engine.IWD) { String.format(GAM_NPC_QUICK_SPELL_FMT, i+1), "SPL")); } for (int i = 0; i < 3; i++) { - addField(new IdsBitmap(buffer, offset + 180 + (i * 2), 2, - String.format(GAM_NPC_QUICK_ITEM_SLOT_FMT, i+1), "SLOTS.IDS")); + bitmap = addField(new IdsBitmap(buffer, offset + 180 + (i * 2), 2, + String.format(GAM_NPC_QUICK_ITEM_SLOT_FMT, i+1), "SLOTS.IDS", true, false, true)); + bitmap.addIdsMapEntry(entryNone); } for (int i = 0; i < 3; i++) { addField(new DecNumber(buffer, offset + 186 + (i * 2), 2, @@ -342,10 +351,12 @@ else if (Profile.getEngine() == Profile.Engine.IWD2) { addField(new DecNumber(buffer, offset + 40, 2, GAM_NPC_MODAL_STATE)); addField(new Unknown(buffer, offset + 42, 98)); for (int i = 0; i < 4; i++) { - addField(new IdsBitmap(buffer, offset + 140 + (i * 4), 2, - String.format(GAM_NPC_QUICK_WEAPON_SLOT_FMT, i+1), "SLOTS.IDS")); - addField(new IdsBitmap(buffer, offset + 142 + (i * 4), 2, - String.format(GAM_NPC_QUICK_SHIELD_SLOT_FMT, i+1), "SLOTS.IDS")); + bitmap = addField(new IdsBitmap(buffer, offset + 140 + (i * 4), 2, + String.format(GAM_NPC_QUICK_WEAPON_SLOT_FMT, i+1), "SLOTS.IDS", true, false, true)); + bitmap.addIdsMapEntry(entryNone); + bitmap = addField(new IdsBitmap(buffer, offset + 142 + (i * 4), 2, + String.format(GAM_NPC_QUICK_SHIELD_SLOT_FMT, i+1), "SLOTS.IDS", true, false, true)); + bitmap.addIdsMapEntry(entryNone); } for (int i = 0; i < 4; i++) { addField(new DecNumber(buffer, offset + 156 + (i * 4), 2, @@ -363,8 +374,9 @@ else if (Profile.getEngine() == Profile.Engine.IWD2) { } addField(new Unknown(buffer, offset + 253, 1)); for (int i = 0; i < 3; i++) { - addField(new IdsBitmap(buffer, offset + 254 + (i * 2), 2, - String.format(GAM_NPC_QUICK_ITEM_SLOT_FMT, i+1), "SLOTS.IDS")); + bitmap = addField(new IdsBitmap(buffer, offset + 254 + (i * 2), 2, + String.format(GAM_NPC_QUICK_ITEM_SLOT_FMT, i+1), "SLOTS.IDS", true, false, true)); + bitmap.addIdsMapEntry(entryNone); } for (int i = 0; i < 3; i++) { addField(new DecNumber(buffer, offset + 260 + (i * 2), 2, diff --git a/src/org/infinity/resource/graphics/BamDecoder.java b/src/org/infinity/resource/graphics/BamDecoder.java index e7ea8eac8..6a605fff4 100644 --- a/src/org/infinity/resource/graphics/BamDecoder.java +++ b/src/org/infinity/resource/graphics/BamDecoder.java @@ -4,6 +4,8 @@ package org.infinity.resource.graphics; +import java.awt.AlphaComposite; +import java.awt.Composite; import java.awt.Dimension; import java.awt.Image; import java.awt.Point; @@ -26,6 +28,7 @@ public enum Type { INVALID, BAMC, BAMV1, BAMV2, CUSTOM } private final ResourceEntry bamEntry; private Type type; + private Composite composite; /** * Returns whether the specified resource entry points to a valid BAM resource. @@ -43,20 +46,16 @@ public static Type getType(ResourceEntry bamEntry) { Type retVal = Type.INVALID; if (bamEntry != null) { - try { - InputStream is = bamEntry.getResourceDataAsStream(); - if (is != null) { - String signature = StreamUtils.readString(is, 4); - String version = StreamUtils.readString(is, 4); - is.close(); - if ("BAMC".equals(signature)) { - retVal = Type.BAMC; - } else if ("BAM ".equals(signature)) { - if ("V1 ".equals(version)) { - retVal = Type.BAMV1; - } else if ("V2 ".equals(version)) { - retVal = Type.BAMV2; - } + try (InputStream is = bamEntry.getResourceDataAsStream()) { + String signature = StreamUtils.readString(is, 4); + String version = StreamUtils.readString(is, 4); + if ("BAMC".equals(signature)) { + retVal = Type.BAMC; + } else if ("BAM ".equals(signature)) { + if ("V1 ".equals(version)) { + retVal = Type.BAMV1; + } else if ("V2 ".equals(version)) { + retVal = Type.BAMV2; } } } catch (Exception e) { @@ -154,11 +153,30 @@ public Type getType() /** Draws the specified frame onto the canvas. */ public abstract void frameGet(BamControl control, int frameIdx, Image canvas); + /** + * Returns the {@link Composite} instance that is used to draw the BAM frame onto a canvas. + *

    By default the {@link AlphaComposite#SrcOver} composite object is used. + */ + public Composite getComposite() + { + return (composite != null) ? composite : AlphaComposite.SrcOver; + } + + /** + * Sets a {@link Composite} instance that is used to draw the BAM frame onto a canvas. + * Specify {@code null} to use the default {@code Composite}. + */ + public void setComposite(Composite comp) + { + this.composite = comp; + } + protected BamDecoder(ResourceEntry bamEntry) { this.bamEntry = bamEntry; this.type = Type.INVALID; + this.composite = null; // use default } @@ -168,6 +186,39 @@ protected void setType(Type type) this.type = type; } + @Override + public int hashCode() + { + int hash = 7; + hash = 31 * hash + ((type == null) ? 0 : type.hashCode()); + hash = 31 * hash + ((bamEntry == null) ? 0 : bamEntry.hashCode()); + return hash; + } + + @Override + public boolean equals(Object o) + { + if (o == this) { + return true; + } + if (!(o instanceof BamDecoder)) { + return false; + } + BamDecoder other = (BamDecoder)o; + boolean retVal = (this.type == null && other.type == null) || + (this.type != null && this.type.equals(other.type)); + retVal &= (this.bamEntry == null && other.bamEntry == null) || + (this.bamEntry != null && this.bamEntry.equals(other.bamEntry)); + return retVal; + } + + @Override + public String toString() + { + String retVal = "entry=" + ((bamEntry != null) ? bamEntry.toString() : "(null)"); + retVal += ", Type=" + ((type != null) ? type.toString() : "(null)"); + return "[" + retVal + "]"; + } //-------------------------- INNER CLASSES -------------------------- @@ -180,6 +231,9 @@ public interface FrameEntry public int getHeight(); public int getCenterX(); public int getCenterY(); + public void setCenterX(int x); + public void setCenterY(int y); + public void resetCenter(); } @@ -188,14 +242,19 @@ public interface FrameEntry */ public static abstract class BamControl { - /** - * Definitions of how to render BAM frames.
    - * Individual: Each frame is drawn individually. The resulting image dimension is defined - * by the drawn frame. Does not take frame centers into account.
    - * Shared: Each frame is drawn onto a canvas of fixed dimension that is big enough to hold - * every single frame without cropping or resizing. Takes frame centers into account. - */ - public enum Mode { INDIVIDUAL, SHARED } + /** Definitions of how to render BAM frames. */ + public enum Mode { + /** + * Each frame is drawn individually. The resulting image dimension is defined by the + * drawn frame. Does not take frame centers into account. + */ + INDIVIDUAL, + /** + * Each frame is drawn onto a canvas of fixed dimension that is big enough to hold + * every single frame without cropping or resizing. Takes frame centers into account. + */ + SHARED + } private final BamDecoder parent; @@ -315,7 +374,7 @@ public boolean isEmpty() /** Returns the index of the active cycle. */ public abstract int cycleGet(); - /** Sets the active cycle. (Default: first available cycle) */ + /** Sets the active cycle. Returns whether the specified cycle could be set. */ public abstract boolean cycleSet(int cycleIdx); /** Returns whether the active cycle can be advanced by at least one more frame. */ @@ -336,7 +395,7 @@ public boolean isEmpty() /** Returns the index of the currently selected frame in the active cycle. */ public abstract int cycleGetFrameIndex(); - /** Selects the specified frame in the active cycle. */ + /** Selects the specified frame in the active cycle. Returns whether the specified frame index could be set. */ public abstract boolean cycleSetFrameIndex(int frameIdx); /** Translates the active cycle's frame index into an absolute frame index. Returns -1 if cycle doesn't contain frames. */ @@ -353,16 +412,20 @@ protected void updateSharedBamSize() sharedBamSize = calculateSharedBamSize(sharedBamSize, isSharedPerCycle(), false); } - // Calculates a shared canvas size for the current BAM. - // cycleBased: true=for current cycle only, false=for all available frames - // isMirrored: true=mirror along the x axis, false=no mirroring - // To get the top-left corner of the selected frame: - // For unmirrored frames: - // left = -sharedBamSize.x - frameCenterX() - // top = -sharedBamSize.y - frameCenterY() - // For mirrored frames: - // left = -sharedBamSize.x - (frameWidth() - frameCenterX() - 1) - // top = -sharedBamSize.y - frameCenterY() + /** + * Calculates a shared canvas size for the current BAM. + * To get the top-left corner of the selected frame: + *

    For unmirrored frames: + *

    left = -sharedBamSize.x - frameCenterX()
    + *
    top  = -sharedBamSize.y - frameCenterY()
    + *

    For mirrored frames: + *

    left = -sharedBamSize.x - (frameWidth() - frameCenterX() - 1)
    + *
    top  = -sharedBamSize.y - frameCenterY()
    + * @param rect Reuse this {@code Rectangle} instance if available. + * @param cycleBased {@code true} for current cycle only, {@code false} for all available frames + * @param isMirrored {@code true}: mirror along the x axis, {@code false}: no mirroring + * @return the update rectangle + */ protected Rectangle calculateSharedBamSize(Rectangle rect, boolean cycleBased, boolean isMirrored) { if (rect == null) { @@ -376,19 +439,21 @@ protected Rectangle calculateSharedBamSize(Rectangle rect, boolean cycleBased, b if (cycleBased) { for (int i = 0; i < cycleFrameCount(); i++) { int frame = cycleGetFrameIndexAbsolute(i); - int cx = isMirrored ? (parent.getFrameInfo(frame).getWidth() - parent.getFrameInfo(frame).getCenterX() - 1) : parent.getFrameInfo(frame).getCenterX(); + FrameEntry fe = parent.getFrameInfo(frame); + int cx = isMirrored ? (fe.getWidth() - fe.getCenterX() - 1) : fe.getCenterX(); x1 = Math.min(x1, -cx); - y1 = Math.min(y1, -parent.getFrameInfo(frame).getCenterY()); - x2 = Math.max(x2, parent.getFrameInfo(frame).getWidth() - cx); - y2 = Math.max(y2, parent.getFrameInfo(frame).getHeight() - parent.getFrameInfo(frame).getCenterY()); + y1 = Math.min(y1, -fe.getCenterY()); + x2 = Math.max(x2, fe.getWidth() - cx); + y2 = Math.max(y2, fe.getHeight() - fe.getCenterY()); } } else { for (int i = 0; i < parent.frameCount(); i++) { - int cx = isMirrored ? (parent.getFrameInfo(i).getWidth() - parent.getFrameInfo(i).getCenterX() - 1) : parent.getFrameInfo(i).getCenterX(); + FrameEntry fe = parent.getFrameInfo(i); + int cx = isMirrored ? (fe.getWidth() - fe.getCenterX() - 1) : fe.getCenterX(); x1 = Math.min(x1, -cx); - y1 = Math.min(y1, -parent.getFrameInfo(i).getCenterY()); - x2 = Math.max(x2, parent.getFrameInfo(i).getWidth() - cx); - y2 = Math.max(y2, parent.getFrameInfo(i).getHeight() - parent.getFrameInfo(i).getCenterY()); + y1 = Math.min(y1, -fe.getCenterY()); + x2 = Math.max(x2, fe.getWidth() - cx); + y2 = Math.max(y2, fe.getHeight() - fe.getCenterY()); } } if (x1 == Integer.MAX_VALUE) x1 = 0; diff --git a/src/org/infinity/resource/graphics/BamResource.java b/src/org/infinity/resource/graphics/BamResource.java index a971a10f2..7f9778de5 100644 --- a/src/org/infinity/resource/graphics/BamResource.java +++ b/src/org/infinity/resource/graphics/BamResource.java @@ -4,9 +4,6 @@ package org.infinity.resource.graphics; -import tv.porst.jhexview.DataChangedEvent; -import tv.porst.jhexview.IDataChangedListener; - import java.awt.AlphaComposite; import java.awt.BorderLayout; import java.awt.Color; @@ -85,6 +82,9 @@ import org.infinity.util.io.FileManager; import org.infinity.util.io.StreamUtils; +import tv.porst.jhexview.DataChangedEvent; +import tv.porst.jhexview.IDataChangedListener; + /** * This resource describes animated graphics. Such files are used for animations * (both {@link CreResource creature} animations, {@link ItmResource item} and @@ -549,7 +549,7 @@ public JComponent makeViewer(ViewableContainer container) miExportFramesPNG.addActionListener(this); } - List list = new ArrayList(); + List list = new ArrayList<>(); if (miExport != null) { list.add(miExport); } @@ -793,13 +793,13 @@ private void showFrame() if (curCycle >= 0) { ((JLabel)buttonControlPanel.getControlByType(CtrlCycleLabel)) - .setText("Cycle: " + (curCycle + 1) + "/" + bamControl.cycleCount()); + .setText("Cycle: " + curCycle + "/" + (bamControl.cycleCount() - 1)); ((JLabel)buttonControlPanel.getControlByType(CtrlFrameLabel)) - .setText("Frame: " + (curFrame + 1) + "/" + bamControl.cycleFrameCount()); + .setText("Frame: " + curFrame + "/" + (bamControl.cycleFrameCount() - 1)); } else { ((JLabel)buttonControlPanel.getControlByType(CtrlCycleLabel)).setText("All frames"); ((JLabel)buttonControlPanel.getControlByType(CtrlFrameLabel)) - .setText("Frame: " + (curFrame + 1) + "/" + decoder.frameCount()); + .setText("Frame: " + curFrame + "/" + (decoder.frameCount() - 1)); } buttonControlPanel.getControlByType(CtrlPrevCycle).setEnabled(curCycle > -1); @@ -955,7 +955,7 @@ private boolean checkCompatibility(Component parent) int numCycles = bamControl.cycleCount(); boolean hasSemiTrans = false; int maxWidth = 0, maxHeight = 0; - List issues = new ArrayList(10); + List issues = new ArrayList<>(10); // checking for issues BamDecoder.BamControl control = decoder.createControl(); @@ -1079,7 +1079,7 @@ private byte[] convertToBamV1(boolean compressed) throws Exception int[] chainedImageData = ((DataBufferInt)composedImage.getRaster().getDataBuffer()).getData(); int[] palette = ColorConvert.medianCut(chainedImageData, hasTransparency ? 255 : 256, ignoreAlpha); // initializing color cache - IntegerHashMap colorCache = new IntegerHashMap(1536); + IntegerHashMap colorCache = new IntegerHashMap<>(1536); for (int i = 0; i < palette.length; i++) { colorCache.put(palette[i], (byte)i); } @@ -1093,7 +1093,7 @@ private byte[] convertToBamV1(boolean compressed) throws Exception } // 2. encoding frames - List frameList = new ArrayList(frameCount); + List frameList = new ArrayList<>(frameCount); int colorShift = hasTransparency ? 1 : 0; // considers transparent color index for (int i = 0; i < frameCount; i++) { if (decoder.frameGet(control, i) != null) { @@ -1120,7 +1120,8 @@ private byte[] convertToBamV1(boolean compressed) throws Exception if (colIdx != null) { dstData[dstIdx++] = (byte)(colIdx + colorShift); } else { - int color = ColorConvert.nearestColorRGB(srcData[srcIdx], palette, ignoreAlpha); + double weight = ignoreAlpha ? 0.0 : 1.0; + int color = ColorConvert.getNearestColor(srcData[srcIdx], palette, weight, null); dstData[dstIdx++] = (byte)(color); if (color > 0) { colorCache.put(srcData[srcIdx], (byte)(color - colorShift)); @@ -1144,7 +1145,8 @@ private byte[] convertToBamV1(boolean compressed) throws Exception if (colIdx != null) { dstData[idx] = (byte)(colIdx + colorShift); } else { - int color = ColorConvert.nearestColorRGB(srcData[idx], palette, ignoreAlpha); + double weight = ignoreAlpha ? 0.0 : 1.0; + int color = ColorConvert.getNearestColor(srcData[idx], palette, weight, null); dstData[idx] = (byte)(color); if (color > 0) { colorCache.put(srcData[idx], (byte)(color - colorShift)); @@ -1255,7 +1257,7 @@ private void startConversion(boolean compressed) @Override public List doInBackground() { - List list = new Vector(1); + List list = new Vector<>(1); try { byte[] buf = convertToBamV1(exportCompressed); if (buf != null) { @@ -1276,44 +1278,37 @@ public boolean containsPvrzReference(int index) { boolean retVal = false; if (index >= 0 && index <= 99999) { - try { - InputStream is = entry.getResourceDataAsStream(); - if (is != null) { - try { - // parsing resource header - byte[] sig = new byte[8]; - byte[] buf = new byte[24]; - long len; - long curOfs = 0; - if ((len = is.read(sig)) != sig.length) throw new Exception(); - if (!"BAM V2 ".equals(DynamicArray.getString(sig, 0, 8))) throw new Exception(); - curOfs += len; - if ((len = is.read(buf)) != buf.length) throw new Exception(); - curOfs += len; - int numBlocks = DynamicArray.getInt(buf, 8); - int ofsBlocks = DynamicArray.getInt(buf, 20); - curOfs = ofsBlocks - curOfs; - if (curOfs > 0) { - do { - len = is.skip(curOfs); - if (len <= 0) throw new Exception(); - curOfs -= len; - } while (curOfs > 0); - } + try (InputStream is = entry.getResourceDataAsStream()) { + // parsing resource header + byte[] sig = new byte[8]; + byte[] buf = new byte[24]; + long len; + long curOfs = 0; + if ((len = is.read(sig)) != sig.length) throw new Exception(); + if (!"BAM V2 ".equals(DynamicArray.getString(sig, 0, 8))) throw new Exception(); + curOfs += len; + if ((len = is.read(buf)) != buf.length) throw new Exception(); + curOfs += len; + int numBlocks = DynamicArray.getInt(buf, 8); + int ofsBlocks = DynamicArray.getInt(buf, 20); + curOfs = ofsBlocks - curOfs; + if (curOfs > 0) { + do { + len = is.skip(curOfs); + if (len <= 0) throw new Exception(); + curOfs -= len; + } while (curOfs > 0); + } - // parsing blocks - buf = new byte[28]; - for (int i = 0; i < numBlocks && !retVal; i++) { - if (is.read(buf) != buf.length) throw new Exception(); - int curIndex = DynamicArray.getInt(buf, 0); - retVal = (curIndex == index); - } - } finally { - is.close(); - is = null; - } + // parsing blocks + buf = new byte[28]; + for (int i = 0; i < numBlocks && !retVal; i++) { + if (is.read(buf) != buf.length) throw new Exception(); + int curIndex = DynamicArray.getInt(buf, 0); + retVal = (curIndex == index); } } catch (Exception e) { + e.printStackTrace(); } } return retVal; diff --git a/src/org/infinity/resource/graphics/BamV1Decoder.java b/src/org/infinity/resource/graphics/BamV1Decoder.java index 2b6ff9466..45d3c8635 100644 --- a/src/org/infinity/resource/graphics/BamV1Decoder.java +++ b/src/org/infinity/resource/graphics/BamV1Decoder.java @@ -5,7 +5,7 @@ package org.infinity.resource.graphics; import java.awt.Dimension; -import java.awt.Graphics; +import java.awt.Graphics2D; import java.awt.Image; import java.awt.image.BufferedImage; import java.awt.image.DataBufferByte; @@ -25,8 +25,8 @@ */ public class BamV1Decoder extends BamDecoder { - private final List listFrames = new ArrayList(); - private final List listCycles = new ArrayList(); + private final List listFrames = new ArrayList<>(); + private final List listCycles = new ArrayList<>(); private final BamV1FrameEntry defaultFrameInfo = new BamV1FrameEntry(null, 0); private BamV1Control defaultControl; @@ -325,8 +325,11 @@ private void decodeFrame(BamControl control, int frameIdx, Image canvas) // rendering resulting image onto the canvas if needed if (image != canvas) { - Graphics g = canvas.getGraphics(); + Graphics2D g = (Graphics2D)canvas.getGraphics(); try { + if (getComposite() != null) { + g.setComposite(getComposite()); + } g.drawImage(image, 0, 0, null); } finally { g.dispose(); @@ -338,6 +341,39 @@ private void decodeFrame(BamControl control, int frameIdx, Image canvas) } } + @Override + public int hashCode() + { + int hash = super.hashCode(); + hash = 31 * hash + ((listFrames == null) ? 0 : listFrames.hashCode()); + hash = 31 * hash + ((listCycles == null) ? 0 : listCycles.hashCode()); + hash = 31 * hash + ((bamBuffer == null) ? 0 : bamBuffer.hashCode()); + hash = 31 * hash + ((bamPalette == null) ? 0 : bamPalette.hashCode()); + hash = 31 * hash + rleIndex; + return hash; + } + + @Override + public boolean equals(Object o) + { + if (!(o instanceof BamV1Decoder)) { + return false; + } + boolean retVal = super.equals(o); + if (retVal) { + BamV1Decoder other = (BamV1Decoder)o; + retVal &= (this.listFrames == null && other.listFrames == null) || + (this.listFrames != null && this.listFrames.equals(other.listFrames)); + retVal &= (this.listCycles == null && other.listCycles == null) || + (this.listCycles != null && this.listCycles.equals(other.listCycles)); + retVal &= (this.bamBuffer == null && other.bamBuffer == null) || + (this.bamBuffer != null && this.bamBuffer.equals(other.bamBuffer)); + retVal &= (this.bamPalette == null && other.bamPalette == null) || + (this.bamPalette != null && this.bamPalette.equals(other.bamPalette)); + retVal &= (this.rleIndex == other.rleIndex); + } + return retVal; + } //-------------------------- INNER CLASSES -------------------------- @@ -345,6 +381,7 @@ private void decodeFrame(BamControl control, int frameIdx, Image canvas) public class BamV1FrameEntry implements BamDecoder.FrameEntry { private int width, height, centerX, centerY, ofsData; + private int overrideCenterX, overrideCenterY; private boolean compressed; private BamV1FrameEntry(ByteBuffer buffer, int ofs) @@ -352,8 +389,8 @@ private BamV1FrameEntry(ByteBuffer buffer, int ofs) if (buffer != null && ofs + 12 <= buffer.limit()) { width = buffer.getShort(ofs + 0) & 0xffff; height = buffer.getShort(ofs + 2) & 0xffff; - centerX = buffer.getShort(ofs + 4); - centerY = buffer.getShort(ofs + 6); + centerX = overrideCenterX = buffer.getShort(ofs + 4); + centerY = overrideCenterY = buffer.getShort(ofs + 6); ofsData = buffer.getInt(ofs + 8) & 0x7fffffff; compressed = (buffer.getInt(ofs + 8) & 0x80000000) == 0; } else { @@ -367,11 +404,26 @@ private BamV1FrameEntry(ByteBuffer buffer, int ofs) @Override public int getHeight() { return height; } @Override - public int getCenterX() { return centerX; } + public int getCenterX() { return overrideCenterX; } @Override - public int getCenterY() { return centerY; } + public int getCenterY() { return overrideCenterY; } + + @Override + public void setCenterX(int x) { overrideCenterX = Math.max(Short.MIN_VALUE, Math.min(Short.MAX_VALUE, x)); } + @Override + public void setCenterY(int y) { overrideCenterY = Math.max(Short.MIN_VALUE, Math.min(Short.MAX_VALUE, y)); } + @Override + public void resetCenter() { overrideCenterX = centerX; overrideCenterY = centerY; } public boolean isCompressed() { return compressed; } + + @Override + public String toString() + { + return "[width=" + getWidth() + ", height=" + getHeight() + + ", centerX=" + getCenterX() + ", centerY=" + getCenterY() + + ", compressed=" + Boolean.toString(isCompressed()) + "]" ; + } } @@ -515,10 +567,12 @@ public int cycleGet() @Override public boolean cycleSet(int cycleIdx) { - if (cycleIdx >= 0 && cycleIdx < getDecoder().listCycles.size() && currentCycle != cycleIdx) { - currentCycle = cycleIdx; - if (isSharedPerCycle()) { - updateSharedBamSize(); + if (cycleIdx >= 0 && cycleIdx < getDecoder().listCycles.size()) { + if (currentCycle != cycleIdx) { + currentCycle = cycleIdx; + if (isSharedPerCycle()) { + updateSharedBamSize(); + } } return true; } else { diff --git a/src/org/infinity/resource/graphics/BamV2Decoder.java b/src/org/infinity/resource/graphics/BamV2Decoder.java index bf061f011..907e90589 100644 --- a/src/org/infinity/resource/graphics/BamV2Decoder.java +++ b/src/org/infinity/resource/graphics/BamV2Decoder.java @@ -5,7 +5,7 @@ package org.infinity.resource.graphics; import java.awt.Dimension; -import java.awt.Graphics; +import java.awt.Graphics2D; import java.awt.Image; import java.awt.Transparency; import java.awt.image.BufferedImage; @@ -33,8 +33,8 @@ public class BamV2Decoder extends BamDecoder { private final TreeSet pvrIndices = new TreeSet<>(); - private final List listFrames = new ArrayList(); - private final List listCycles = new ArrayList(); + private final List listFrames = new ArrayList<>(); + private final List listCycles = new ArrayList<>(); private final BamV2FrameEntry defaultFrameInfo = new BamV2FrameEntry(null, 0, 0); private BamV2Control defaultControl; @@ -310,8 +310,11 @@ private void renderFrame(BamControl control, int frameIdx, Image canvas) // rendering resulting image onto the canvas if needed if (image != canvas) { - Graphics g = canvas.getGraphics(); + Graphics2D g = (Graphics2D)canvas.getGraphics(); try { + if (getComposite() != null) { + g.setComposite(getComposite()); + } g.drawImage(image, 0, 0, null); } finally { g.dispose(); @@ -323,6 +326,42 @@ private void renderFrame(BamControl control, int frameIdx, Image canvas) } } + @Override + public int hashCode() + { + int hash = super.hashCode(); + hash = 31 * hash + ((pvrIndices == null) ? 0 : pvrIndices.hashCode()); + hash = 31 * hash + ((listFrames == null) ? 0 : listFrames.hashCode()); + hash = 31 * hash + ((listCycles == null) ? 0 : listCycles.hashCode()); + hash = 31 * hash + ((bamBuffer == null) ? 0 : bamBuffer.hashCode()); + hash = 31 * hash + ((bamPath == null) ? 0 : bamPath.hashCode()); + hash = 31 * hash + numDataBlocks; + return hash; + } + + @Override + public boolean equals(Object o) + { + if (!(o instanceof BamV2Decoder)) { + return false; + } + boolean retVal = super.equals(o); + if (retVal) { + BamV2Decoder other = (BamV2Decoder)o; + retVal &= (this.pvrIndices == null && other.pvrIndices == null) || + (this.pvrIndices != null && this.pvrIndices.equals(other.pvrIndices)); + retVal &= (this.listFrames == null && other.listFrames == null) || + (this.listFrames != null && this.listFrames.equals(other.listFrames)); + retVal &= (this.listCycles == null && other.listCycles == null) || + (this.listCycles != null && this.listCycles.equals(other.listCycles)); + retVal &= (this.bamBuffer == null && other.bamBuffer == null) || + (this.bamBuffer != null && this.bamBuffer.equals(other.bamBuffer)); + retVal &= (this.bamPath == null && other.bamPath == null) || + (this.bamPath != null && this.bamPath.equals(other.bamPath)); + retVal &= (this.numDataBlocks == other.numDataBlocks); + } + return retVal; + } //-------------------------- INNER CLASSES -------------------------- @@ -332,6 +371,7 @@ public class BamV2FrameEntry implements BamDecoder.FrameEntry private final int dataBlockSize = 0x1c; // size of a single data block private int width, height, centerX, centerY; + private int overrideCenterX, overrideCenterY; private BufferedImage frame; private BamV2FrameEntry(ByteBuffer buffer, int ofsFrame, int ofsBlocks) @@ -339,8 +379,8 @@ private BamV2FrameEntry(ByteBuffer buffer, int ofsFrame, int ofsBlocks) if (buffer != null && ofsFrame < buffer.limit() && ofsBlocks < buffer.limit()) { width = buffer.getShort(ofsFrame) & 0xffff; height = buffer.getShort(ofsFrame+2) & 0xffff; - centerX = buffer.getShort(ofsFrame+4); - centerY = buffer.getShort(ofsFrame+6); + centerX = overrideCenterX = buffer.getShort(ofsFrame+4); + centerY = overrideCenterY = buffer.getShort(ofsFrame+6); int blockStart = buffer.getShort(ofsFrame+8) & 0xffff; int blockCount = buffer.getShort(ofsFrame+10) & 0xffff; decodeImage(buffer, ofsBlocks, blockStart, blockCount); @@ -355,12 +395,26 @@ private BamV2FrameEntry(ByteBuffer buffer, int ofsFrame, int ofsBlocks) @Override public int getHeight() { return height; } @Override - public int getCenterX() { return centerX; } + public int getCenterX() { return overrideCenterX; } @Override - public int getCenterY() { return centerY; } + public int getCenterY() { return overrideCenterY; } + + @Override + public void setCenterX(int x) { overrideCenterX = Math.max(Short.MIN_VALUE, Math.min(Short.MAX_VALUE, x)); } + @Override + public void setCenterY(int y) { overrideCenterY = Math.max(Short.MIN_VALUE, Math.min(Short.MAX_VALUE, y)); } + @Override + public void resetCenter() { overrideCenterX = centerX; overrideCenterY = centerY; } public Image getImage() { return frame; } + @Override + public String toString() + { + return "[width=" + getWidth() + ", height=" + getHeight() + + ", centerX=" + getCenterX() + ", centerY=" + getCenterY() + "]" ; + } + private void decodeImage(ByteBuffer buffer, int ofsBlocks, int start, int count) { frame = null; @@ -382,7 +436,10 @@ private void decodeImage(ByteBuffer buffer, int ofsBlocks, int start, int count) if (decoder != null) { try { BufferedImage srcImage = decoder.decode(srcX, srcY, w, h); - Graphics g = frame.getGraphics(); + Graphics2D g = (Graphics2D)frame.getGraphics(); + if (getComposite() != null) { + g.setComposite(getComposite()); + } g.drawImage(srcImage, dstX, dstY, null); g.dispose(); g = null; diff --git a/src/org/infinity/resource/graphics/BlendingComposite.java b/src/org/infinity/resource/graphics/BlendingComposite.java new file mode 100644 index 000000000..05a93761b --- /dev/null +++ b/src/org/infinity/resource/graphics/BlendingComposite.java @@ -0,0 +1,355 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.graphics; + +import java.awt.Composite; +import java.awt.CompositeContext; +import java.awt.RenderingHints; +import java.awt.image.ColorModel; +import java.awt.image.DataBuffer; +import java.awt.image.DirectColorModel; +import java.awt.image.Raster; +import java.awt.image.RasterFormatException; +import java.awt.image.WritableRaster; +import java.util.Objects; + +/** + * This composite class implements blending modes that simulates the blending modes + * supported by the IE games. + * Note: Blending is done in software and results in a noticeably performance penalty when used + * in combination with hardware-accelerated image data. + */ +public class BlendingComposite implements Composite +{ + public enum BlendingMode { + /** + * This mode applied the OpenGL blending operation: + * src={@code GL_ONE_MINUS_DST_COLOR}, dst={@code GL_ONE}. + */ + BRIGHTEST { + @Override + void blend(int[] src, int[] dst, int[] result) { + result[0] = Math.min(255, (src[0] * (256 - dst[0]) + (dst[0] << 8)) >>> 8); + result[1] = Math.min(255, (src[1] * (256 - dst[1]) + (dst[1] << 8)) >>> 8); + result[2] = Math.min(255, (src[2] * (256 - dst[2]) + (dst[2] << 8)) >>> 8); + result[3] = Math.min(255, (src[3] * (256 - dst[3]) + (dst[3] << 8)) >>> 8); +// result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); + } + }, + + /** + * This mode applied the OpenGL blending operation: + * src={@code GL_DST_COLOR}, dst={@code GL_ONE}. + */ + MULTIPLY { + @Override + void blend(int[] src, int[] dst, int[] result) { + result[0] = Math.min(255, ((src[0] * dst[0]) + (dst[0] << 8)) >>> 8); + result[1] = Math.min(255, ((src[1] * dst[1]) + (dst[1] << 8)) >>> 8); + result[2] = Math.min(255, ((src[2] * dst[2]) + (dst[2] << 8)) >>> 8); + result[3] = Math.min(255, ((src[3] * dst[3]) + (dst[3] << 8)) >>> 8); +// result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); + } + }, + + /** + * This mode applied the OpenGL blending operation: + * src={@code GL_SRC_COLOR}, dst={@code GL_ONE}. + */ + BRIGHTEST_MULTIPLY { + @Override + void blend(int[] src, int[] dst, int[] result) { + result[0] = Math.min(255, ((src[0] * src[0]) + (dst[0] << 8)) >>> 8); + result[1] = Math.min(255, ((src[1] * src[1]) + (dst[1] << 8)) >>> 8); + result[2] = Math.min(255, ((src[2] * src[2]) + (dst[2] << 8)) >>> 8); + result[3] = Math.min(255, ((src[3] * src[3]) + (dst[3] << 8)) >>> 8); +// result[3] = Math.min(255, src[3] + dst[3] - (src[3] * dst[3]) / 255); + } + }; + + /** + * Blends input colors {@code src} and {@code dst}, and writes the result to {@code result}. + * @param src the source pixel (format: RGBA where R=0, ... A=3; all values in range [0, 255]) + * @param dst the destination pixel + * @param result the blended pixel + */ + abstract void blend(int[] src, int[] dst, int[] result); + } + + /** Implements the following blending operations: src={@code GL_ONE_MINUS_DST_COLOR}, dst={@code GL_ONE}. */ + public static final BlendingComposite Brightest = new BlendingComposite(BlendingMode.BRIGHTEST); + /** Implements the following blending operations: src={@code GL_DST_COLOR}, dst={@code GL_ONE}. */ + public static final BlendingComposite Multiply = new BlendingComposite(BlendingMode.MULTIPLY); + /** Implements the following blending operations: src={@code GL_SRC_COLOR}, dst={@code GL_ONE}. */ + public static final BlendingComposite BrightestMultiply = new BlendingComposite(BlendingMode.BRIGHTEST_MULTIPLY); + + private final float alpha; + private final BlendingMode mode; + + /** + * + * @param modes + */ + public BlendingComposite(BlendingMode mode) + { + this(1.0f, mode); + } + + /** + * + * @param alpha + * @param modes + */ + public BlendingComposite(float alpha, BlendingMode mode) + { + // filtering out null items + this.mode = Objects.requireNonNull(mode, "Blending mode cannot be null"); + + if (alpha < 0.0f || alpha > 1.0f) { + throw new IllegalArgumentException("alpha value must be between 0.0 and 1.0"); + } + + this.alpha = alpha; + } + + /** + * + * @param modes + * @return + */ + public static BlendingComposite getInstance(BlendingMode mode) + { + return new BlendingComposite(1.0f, mode); + } + + /** + * + * @param alpha + * @param modes + * @return + */ + public static BlendingComposite getInstance(float alpha, BlendingMode mode) + { + return new BlendingComposite(alpha, mode); + } + + /** + * + * @param modes + * @return + */ + public BlendingComposite derive(BlendingMode mode) + { + return (mode == this.mode) ? this : new BlendingComposite(this.alpha, mode); + } + + /** + * + * @param alpha + * @return + */ + public BlendingComposite derive(float alpha) + { + return (this.alpha == alpha) ? this : new BlendingComposite(this.alpha, this.mode); + } + + /** + * + * @return + */ + public float getAlpha() + { + return alpha; + } + + /** + * + * @return + */ + public BlendingMode getMode() + { + return this.mode; + } + + @Override + public int hashCode() + { + return Float.floatToIntBits(alpha) * 31 + mode.hashCode(); + } + + @Override + public boolean equals(Object obj) + { + if (obj == this) { + return true; + } + if (!(obj instanceof BlendingComposite)) { + return false; + } + BlendingComposite bc = (BlendingComposite)obj; + return alpha == bc.alpha && this.mode.equals(bc.mode); + } + + private static boolean isColorModelRGB(ColorModel cm) + { + if (cm instanceof DirectColorModel && cm.getTransferType() == DataBuffer.TYPE_INT) { + DirectColorModel dcm = (DirectColorModel)cm; + return dcm.getRedMask() == 0x00ff0000 && + dcm.getGreenMask() == 0x0000ff00 && + dcm.getBlueMask() == 0x000000ff && + (dcm.getNumColorComponents() == 3 || dcm.getAlphaMask() == 0xff000000); + } + return false; + } + + private static boolean isColorModelBGR(ColorModel cm) + { + if (cm instanceof DirectColorModel && cm.getTransferType() == DataBuffer.TYPE_INT) { + DirectColorModel dcm = (DirectColorModel)cm; + return dcm.getRedMask() == 0x000000ff && + dcm.getGreenMask() == 0x0000ff00 && + dcm.getBlueMask() == 0x00ff0000 && + (dcm.getNumColorComponents() == 3 || dcm.getAlphaMask() == 0xff000000); + } + return false; + } + + @Override + public CompositeContext createContext(ColorModel srcColorModel, ColorModel dstColorModel, RenderingHints hints) + { + Objects.requireNonNull(srcColorModel); + Objects.requireNonNull(dstColorModel); + + if (isColorModelRGB(srcColorModel) && isColorModelRGB(dstColorModel)) { + return new BlendingRGBContext(this); + } else if (isColorModelBGR(srcColorModel) && isColorModelBGR(dstColorModel)) { + return new BlendingBGRContext(this); + } + + throw new RasterFormatException("Incompatible color models:\n " + srcColorModel + "\n " + dstColorModel); + } + +//-------------------------- INNER CLASSES -------------------------- + + private static abstract class BlendingContext implements CompositeContext + { + protected final BlendingComposite composite; + + protected BlendingContext(BlendingComposite c) + { + this.composite = Objects.requireNonNull(c); + } + + @Override + public void dispose() { } + } + + private static class BlendingRGBContext extends BlendingContext + { + public BlendingRGBContext(BlendingComposite c) + { + super(c); + } + + @Override + public void compose(Raster src, Raster dstIn, WritableRaster dstOut) + { + int width = Math.min(src.getWidth(), dstIn.getWidth()); + int height = Math.min(src.getHeight(), dstIn.getHeight()); + + int alpha = (int)(composite.getAlpha() * 256.0f); + + int[] result = new int[4]; + int[] srcPixel = new int[4]; + int[] dstPixel = new int[4]; + int[] srcPixels = new int[width]; + int[] dstPixels = new int[width]; + + for (int y = 0; y < height; y++) { + src.getDataElements(0, y, width, 1, srcPixels); + dstIn.getDataElements(0, y, width, 1, dstPixels); + for (int x = 0; x < width; x++) { + // transforming pixels: INT_ARGB -> array [R, G, B, A] + int pixel = srcPixels[x]; + srcPixel[0] = (pixel >> 16) & 0xFF; + srcPixel[1] = (pixel >> 8) & 0xFF; + srcPixel[2] = (pixel ) & 0xFF; + srcPixel[3] = (pixel >> 24) & 0xFF; + + // transforming pixels: INT_ARGB -> array [R, G, B, A] + pixel = dstPixels[x]; + dstPixel[0] = (pixel >> 16) & 0xFF; + dstPixel[1] = (pixel >> 8) & 0xFF; + dstPixel[2] = (pixel ) & 0xFF; + dstPixel[3] = (pixel >> 24) & 0xFF; + + // reusing results from previous blending operation as source + composite.mode.blend(srcPixel, dstPixel, result); + + // mixing results and applying global alpha + dstPixels[x] = ((dstPixel[3] + (result[3] - ((dstPixel[3] * alpha) >> 8))) & 0xFF) << 24 | + ((dstPixel[0] + (result[0] - ((dstPixel[0] * alpha) >> 8))) & 0xFF) << 16 | + ((dstPixel[1] + (result[1] - ((dstPixel[1] * alpha) >> 8))) & 0xFF) << 8 | + (dstPixel[2] + (result[2] - ((dstPixel[2] * alpha) >> 8))) & 0xFF; + } + dstOut.setDataElements(0, y, width, 1, dstPixels); + } + } + } + + private static class BlendingBGRContext extends BlendingContext + { + public BlendingBGRContext(BlendingComposite c) + { + super(c); + } + + @Override + public void compose(Raster src, Raster dstIn, WritableRaster dstOut) + { + int width = Math.min(src.getWidth(), dstIn.getWidth()); + int height = Math.min(src.getHeight(), dstIn.getHeight()); + + int alpha = (int)(composite.getAlpha() * 256.0f); + + int[] result = new int[4]; + int[] srcPixel = new int[4]; + int[] dstPixel = new int[4]; + int[] srcPixels = new int[width]; + int[] dstPixels = new int[width]; + + for (int y = 0; y < height; y++) { + src.getDataElements(0, y, width, 1, srcPixels); + dstIn.getDataElements(0, y, width, 1, dstPixels); + for (int x = 0; x < width; x++) { + // transforming pixels: INT_ABGR -> array [R, G, B, A] + int pixel = srcPixels[x]; + srcPixel[0] = (pixel ) & 0xFF; + srcPixel[1] = (pixel >> 8) & 0xFF; + srcPixel[2] = (pixel >> 16) & 0xFF; + srcPixel[3] = (pixel >> 24) & 0xFF; + + // transforming pixels: INT_ABGR -> array [R, G, B, A] + pixel = dstPixels[x]; + dstPixel[0] = (pixel ) & 0xFF; + dstPixel[1] = (pixel >> 8) & 0xFF; + dstPixel[2] = (pixel >> 16) & 0xFF; + dstPixel[3] = (pixel >> 24) & 0xFF; + + // reusing results from previous blending operation as source + composite.mode.blend(srcPixel, dstPixel, result); + + // mixing results and applying global alpha + dstPixels[x] = ((dstPixel[3] + (result[3] - ((dstPixel[3] * alpha) >> 8))) & 0xFF) << 24 | + ((dstPixel[0] + (result[0] - ((dstPixel[0] * alpha) >> 8))) & 0xFF) << 16 | + ((dstPixel[1] + (result[1] - ((dstPixel[1] * alpha) >> 8))) & 0xFF) << 8 | + (dstPixel[2] + (result[2] - ((dstPixel[2] * alpha) >> 8))) & 0xFF; + } + dstOut.setDataElements(0, y, width, 1, dstPixels); + } + } + } + +} diff --git a/src/org/infinity/resource/graphics/ColorConvert.java b/src/org/infinity/resource/graphics/ColorConvert.java index a5ba46378..0404416d5 100644 --- a/src/org/infinity/resource/graphics/ColorConvert.java +++ b/src/org/infinity/resource/graphics/ColorConvert.java @@ -24,6 +24,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; +import java.util.HashMap; import java.util.Hashtable; import java.util.Iterator; import java.util.List; @@ -33,15 +34,68 @@ import javax.imageio.ImageReader; import javax.imageio.stream.ImageInputStream; +import org.infinity.resource.key.FileResourceEntry; +import org.infinity.resource.key.ResourceEntry; import org.infinity.util.DynamicArray; import org.infinity.util.io.FileEx; import org.infinity.util.io.StreamUtils; +import org.infinity.util.tuples.Triple; /** * Contains a set of color-related static methods (little endian order only). */ public class ColorConvert { + /** + * A fast but somewhat inaccurate algorithm for calculating a distance between two ARGB values. + * It uses predefined weight values for each color component to calculate the distance. + */ + public static final ColorDistanceFunc COLOR_DISTANCE_ARGB = (argb1, argb2, weight) -> { + int a1 = (argb1 >> 24) & 0xff; + int r1 = (argb1 >> 16) & 0xff; + int g1 = (argb1 >> 8) & 0xff; + int b1 = argb1 & 0xff; + if (a1 != 0xff) { + r1 = r1 * a1 / 255; + g1 = g1 * a1 / 255; + b1 = b1 * a1 / 255; + } + + int a2 = (argb2 >> 24) & 0xff; + int r2 = (argb2 >> 16) & 0xff; + int g2 = (argb2 >> 8) & 0xff; + int b2 = argb2 & 0xff; + if (a2 != 0xff) { + r2 = r2 * a2 / 255; + g2 = g2 * a2 / 255; + b2 = b2 * a2 / 255; + } + + weight = Math.max(0.0, Math.min(2.0, weight)); + double da = (double)(a1 - a2) * 48.0 * weight; + double dr = (double)(r1 - r2) * 14.0; + double dg = (double)(g1 - g2) * 28.0; + double db = (double)(b1 - b2) * 6.0; + return Math.sqrt(da*da + dr*dr + dg*dg + db*db); + }; + + /** + * Returns the distance between the two ARGB values using CIELAB colorspace and CIE94 formula. + * This algorithm is slower than the default ARGB distance calculation but more accurate. + */ + public static final ColorDistanceFunc COLOR_DISTANCE_CIE94 = (argb1, argb2, weight) -> { + Triple lab1 = convertRGBtoLab(argb1); + Triple lab2 = convertRGBtoLab(argb2); + weight = Math.max(0.0, Math.min(2.0, weight)); + double alpha1 = (double)((argb1 >> 24) & 0xff) * weight; + double alpha2 = (double)((argb2 >> 24) & 0xff) * weight; + return getColorDistanceLabCIE94(lab1.getValue0().doubleValue(), lab1.getValue1().doubleValue(), lab1.getValue2().doubleValue(), alpha1, + lab2.getValue0().doubleValue(), lab2.getValue1().doubleValue(), lab2.getValue2().doubleValue(), alpha2); + }; + + // Cache for ARGB key -> CIELAB color space values + private static final HashMap> ARGB_LAB_CACHE = new HashMap<>(); + // max. number of colors for color reduction algorithms private static final int MAX_COLORS = 256; @@ -53,7 +107,13 @@ public enum SortType { Red, // Sort by red color component. Green, // Sort by green color component. Blue, // Sort by blue color component. - Alpha // Sort by alpha component. + Alpha, // Sort by alpha component. + Lab, // Sort by CIELAB L component. + } + + public static void clearCache() + { + ARGB_LAB_CACHE.clear(); } /** @@ -228,50 +288,86 @@ public static Dimension getImageDimension(Path fileName) return d; } +// /** +// * Calculates the nearest color available in the given RGBA palette for the specified color. +// * @param rgbColor The source color in ARGB format. +// * @param rgbPalette A palette containing ARGB color entries. +// * @param ignoreAlpha Whether to exclude alpha component from the calculation. +// * @return The palette index pointing to the nearest color, or -1 on error. +// */ +// public static int nearestColorRGB(int rgbColor, int[] rgbPalette, boolean ignoreAlpha) +// { +// // TODO: Improve match quality for grayscaled colors +// int index = -1; +// if (rgbPalette != null && rgbPalette.length > 0) { +// int mask = ignoreAlpha ? 0 : 0xff000000; +// int minDist = Integer.MAX_VALUE; +// int a = ((rgbColor & mask) >> 24) & 0xff; +// int r = (rgbColor >> 16) & 0xff; +// int g = (rgbColor >> 8) & 0xff; +// int b = rgbColor & 0xff; +// +// int da, dr, dg, db; +// for (int i = 0; i < rgbPalette.length; i++) { +// int col = rgbPalette[i]; +// // Extra check for full transparency +// if (a == 0) { +// if ((col & 0xff000000) == 0) { return i; } +// if (col == 0xff00ff00) { return i; } +// } +// // Computing weighted distance to compensate for perceived color differences +// int a2 = ((rgbPalette[i] & mask) >> 24) & 0xff; +// int r2 = (rgbPalette[i] >> 16) & 0xff; +// int g2 = (rgbPalette[i] >> 8) & 0xff; +// int b2 = rgbPalette[i] & 0xff; +// da = (a - a2) * 48; +// dr = (r - r2) * 14; +// dg = (g - g2) * 28; +// db = (b - b2) * 6; +// int dist = da*da + dr*dr + dg*dg + db*db; +// if (dist < minDist) { +// minDist = dist; +// index = i; +// } +// } +// } +// return index; +// } + /** - * Calculates the nearest color available in the given RGBA palette for the specified color. - * @param rgbColor The source color in ARGB format. - * @param rgbPalette A palette containing ARGB color entries. - * @param ignoreAlpha Whether to exclude alpha component from the calculation. - * @return The palette index pointing to the nearest color, or -1 on error. + * Calculates the nearest color available in the given palette using the specified color distance function. + * @param argb the reference ARGB color. + * @param palette palette with ARGB colors to search. + * @param alphaWeight Weight factor of the alpha component. Supported range: [0.0, 2.0]. + * A value < 1.0 makes alpha less important for the distance calculation. + * A value > 1.0 makes alpha more important for the distance calculation. + * Specify 1.0 to use the unmodified alpha compomponent for the calculation. + * Specify 0.0 to ignore the alpha part in the calculation. + * @param calculator the function for distance calculation. Choose one of the predefined functions or specify a custom + * instance. Specify {@code null} to use the fastest (but slightly inaccurate) distance calculation. + * @return Palette index pointing to the nearest color value. Returns -1 if color entry could not be determined. */ - public static int nearestColorRGB(int rgbColor, int[] rgbPalette, boolean ignoreAlpha) + public static int getNearestColor(int argb, int[] palette, double alphaWeight, ColorDistanceFunc calculator) { - // TODO: Improve match quality for grayscaled colors - int index = -1; - if (rgbPalette != null && rgbPalette.length > 0) { - int mask = ignoreAlpha ? 0 : 0xff000000; - int minDist = Integer.MAX_VALUE; - int a = ((rgbColor & mask) >> 24) & 0xff; - int r = (rgbColor >> 16) & 0xff; - int g = (rgbColor >> 8) & 0xff; - int b = rgbColor & 0xff; - - int da, dr, dg, db; - for (int i = 0; i < rgbPalette.length; i++) { - int col = rgbPalette[i]; - // Extra check for full transparency - if (a == 0) { - if ((col & 0xff000000) == 0) { return i; } - if (col == 0xff00ff00) { return i; } - } - // Computing weighted distance to compensate for perceived color differences - int a2 = ((rgbPalette[i] & mask) >> 24) & 0xff; - int r2 = (rgbPalette[i] >> 16) & 0xff; - int g2 = (rgbPalette[i] >> 8) & 0xff; - int b2 = rgbPalette[i] & 0xff; - da = (a - a2) * 48; - dr = (r - r2) * 14; - dg = (g - g2) * 28; - db = (b - b2) * 6; - int dist = da*da + dr*dr + dg*dg + db*db; - if (dist < minDist) { - minDist = dist; - index = i; - } + int retVal = -1; + if (palette == null) { + return retVal; + } + + if (calculator == null) { + calculator = COLOR_DISTANCE_ARGB; + } + alphaWeight = Math.max(0.0, Math.min(2.0, alphaWeight)); + double minDist = Double.MAX_VALUE; + for (int i = 0; i < palette.length; i++) { + double dist = calculator.calculate(argb, palette[i], alphaWeight); + if (dist < minDist) { + minDist = dist; + retVal = i; } } - return index; + + return retVal; } /** @@ -291,25 +387,28 @@ public static void sortPalette(int[] palette, int startIndex, SortType type, boo switch (type) { case Lightness: - Arrays.sort(tmpColors, startIndex, tmpColors.length, new CompareByLightness()); + Arrays.sort(tmpColors, startIndex, tmpColors.length, CompareByLightness); break; case Saturation: - Arrays.sort(tmpColors, startIndex, tmpColors.length, new CompareBySaturation()); + Arrays.sort(tmpColors, startIndex, tmpColors.length, CompareBySaturation); break; case Hue: - Arrays.sort(tmpColors, startIndex, tmpColors.length, new CompareByHue()); + Arrays.sort(tmpColors, startIndex, tmpColors.length, CompareByHue); break; case Red: - Arrays.sort(tmpColors, startIndex, tmpColors.length, new CompareByRed()); + Arrays.sort(tmpColors, startIndex, tmpColors.length, CompareByRed); break; case Green: - Arrays.sort(tmpColors, startIndex, tmpColors.length, new CompareByGreen()); + Arrays.sort(tmpColors, startIndex, tmpColors.length, CompareByGreen); break; case Blue: - Arrays.sort(tmpColors, startIndex, tmpColors.length, new CompareByBlue()); + Arrays.sort(tmpColors, startIndex, tmpColors.length, CompareByBlue); break; case Alpha: - Arrays.sort(tmpColors, startIndex, tmpColors.length, new CompareByAlpha()); + Arrays.sort(tmpColors, startIndex, tmpColors.length, CompareByAlpha); + break; + case Lab: + Arrays.sort(tmpColors, startIndex, tmpColors.length, CompareByLabL); break; default: break; @@ -329,6 +428,214 @@ public static void sortPalette(int[] palette, int startIndex, SortType type, boo } } + /** + * Converts a single RGB value into the CIELAB colorspace. + * @param argb The ARGB value to convert. + * @return the converted color value in CIELAB colorspace. Order: L, a, b, alpha + * where L range is [0.0, 100.0], a and b are open-ended (usually between -150 and 150). + */ + public static Triple convertRGBtoLab(int argb) + { + Integer key = Integer.valueOf(argb & 0xffffff); + Triple retVal = ARGB_LAB_CACHE.get(key); + + if (retVal == null) { + int alpha = (argb >> 24) & 0xff; + int red = (argb >> 16) & 0xff; + int green = (argb >> 8) & 0xff; + int blue = argb & 0xff; + if (alpha != 255) { + red = red * alpha / 255; + green = green * alpha / 255; + blue = blue * alpha / 255; + } + + // 1. Linearize RGB + double r = (double)red / 255.0; + double g = (double)green / 255.0; + double b = (double)blue / 255.0; + + // 2. Convert to CIEXYZ + r = (r > 0.04045) ? Math.pow((r + 0.055) / 1.055, 2.4) : r / 12.92; + g = (g > 0.04045) ? Math.pow((g + 0.055) / 1.055, 2.4) : g / 12.92; + b = (b > 0.04045) ? Math.pow((b + 0.055) / 1.055, 2.4) : b / 12.92; + r *= 100.0; + g *= 100.0; + b *= 100.0; + double x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805); + double y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722); + double z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505); + + // 3. Convert to Lab + x /= 95.047; + y /= 100.0; + z /= 108.883; + x = (x > 0.008856) ? Math.pow(x, 1.0 / 3.0) : (7.787 * x) + (16.0 / 116.0); + y = (y > 0.008856) ? Math.pow(y, 1.0 / 3.0) : (7.787 * y) + (16.0 / 116.0); + z = (z > 0.008856) ? Math.pow(z, 1.0 / 3.0) : (7.787 * z) + (16.0 / 116.0); + Double dstL = Double.valueOf((116.0 * y) - 16.0); + Double dstA = Double.valueOf(500.0 * (x - y)); + Double dstB = Double.valueOf(200.0 * (y - z)); + retVal = Triple.with(dstL, dstA, dstB); + + ARGB_LAB_CACHE.put(key, retVal); + } + + return retVal; + } + + /** + * Converts a color entry in CIELAB colorspace into an RGB value. Alpha part is set to 0. + * @param L the L (lightness) value in range [0.0, 100.0]. + * @param a the a axis (green-red) value. + * @param b the b axis (blue-yellow) value. + * @return RGB value where blue is located in the lowest byte, followed by green and red. Highest byte is set to 0. + */ + public static int convertLabToRGB(double L, double a, double b) + { + // 1. Convert to CIEXYZ + double y = (L + 16.0) / 116.0; + double x = (a / 500.0) + y; + double z = y - (b / 200.0); + + double d = Math.pow(y, 3.0); + y = (d > 0.008856) ? d : (y - (16.0 / 116.0)) / 7.787; + d = Math.pow(x, 3.0); + x = (d > 0.008856) ? d : (x - (16.0 / 116.0)) / 7.787; + d = Math.pow(z, 3.0); + z = (d > 0.008856) ? d : (z - (16.0 / 116.0)) / 7.787; + + x *= 95.047; + y *= 100.0; + z *= 108.883; + + // 2. Convert to linear RGB + x /= 100.0; + y /= 100.0; + z /= 100.0; + double red = (x * 3.2406) + (y * -1.5372) + (z * -0.4986); + double green = (x * -0.9689) + (y * 1.8758) + (z * 0.0415); + double blue = (x * 0.0557) + (y * -0.2040) + (z * 1.0570); + + red = (red > 0.0031308) ? 1.055 * Math.pow(red, 1.0 / 2.4) - 0.055 : 12.92 * red; + green = (green > 0.0031308) ? 1.055 * Math.pow(green, 1.0 / 2.4) - 0.055 : 12.92 * green; + blue = (blue > 0.0031308) ? 1.055 * Math.pow(blue, 1.0 / 2.4) - 0.055 : 12.92 * blue; + + // convert to non-linear RGB + int retVal = 0; + retVal |= Math.max(0, Math.min(255, (int)(red * 255.0))); + retVal <<= 8; + retVal |= Math.max(0, Math.min(255, (int)(green * 255.0))); + retVal <<= 8; + retVal |= Math.max(0, Math.min(255, (int)(blue * 255.0))); + + return retVal; + } + + /** + * Converts a ARGB palette to a palette in CIELAB colorspace. The returned array contains four components per + * color entry: L, a, b and alpha. L range is [0.0, 100.0], a and b are open-ended (usually between -150 and 150), + * alpha range is [0.0, 255.0]. + * @param palette Palette with ARGB color entries + * @return array with CIELAB color entries plus alpha component. + */ + public static double[] convertRGBtoLabPalette(int[] palette) + { + double[] retVal = null; + if (palette == null) { + return retVal; + } + + retVal = new double[palette.length * 4]; + for (int i = 0; i < palette.length; i++) { + int a = (palette[i] >> 24) & 0xff; + Triple entry = convertRGBtoLab(palette[i]); + retVal[i * 4] = entry.getValue0().doubleValue(); + retVal[i * 4 + 1] = entry.getValue1().doubleValue(); + retVal[i * 4 + 2] = entry.getValue2().doubleValue(); + retVal[i * 4 + 3] = (double)a; + } + + return retVal; + } + + /** + * Converts a palette in CIELAB colorspace plus alpha into a ARGB palette. + * @param palette Palette with L, a, b and alpha component per color entry. + * @return array with ARGB color entries packed into single integer per entry. + */ + public static int[] convertLabToRGBPalette(double[] palette) + { + int[] retVal = null; + if (palette == null) { + return retVal; + } + + retVal = new int[palette.length / 4]; + for (int i = 0; i < retVal.length; i++) { + double L = palette[i * 4]; + double a = palette[i * 4 + 1]; + double b = palette[i * 4 + 2]; + + int rgba = convertLabToRGB(L, a, b); + int alpha = Math.max(0, Math.min(255, (int)palette[i * 4 + 3])); + rgba |= (alpha << 24); + retVal[i] = rgba; + } + + return retVal; + } + + /** Calculates the distance between two CIELAB colors based on CIE94 formula. */ + public static double getColorDistanceLabCIE94(double L1, double a1, double b1, double alpha1, + double L2, double a2, double b2, double alpha2) + { + final double kl = 1.0; + final double k1 = 0.045; + final double k2 = 0.015; + + double deltaL = L1 - L2; + double deltaA = a1 - a2; + double deltaB = b1 - b2; + + double c1 = Math.sqrt(a1*a1 + b1*b1); + double c2 = Math.sqrt(a2*a2 + b2*b2); + double deltaC = c1 - c2; + + double deltaH = deltaA*deltaA + deltaB*deltaB - deltaC*deltaC; + deltaH = (deltaH < 0.0) ? 0.0 : Math.sqrt(deltaH); + + double sc = 1.0 + k1*c1; + double sh = 1.0 + k2*c1; + + double i = Math.pow(deltaL / kl, 2.0) + + Math.pow(deltaC / sc, 2.0) + + Math.pow(deltaH / sh, 2.0) + + (alpha1 - alpha2)*(alpha1 - alpha2); + + return (i < 0.0) ? 0.0 : Math.sqrt(i); + } + + /** + * Returns the distance between the two ARGB values using CIELAB colorspace and CIE94 formula. + * @param argb1 the first ARGB color entry. + * @param argb2 the second ARGB color entry. + * @param alphaWeight Weight factor of the alpha component. Supported range: [0.0, 2.0]. + * A value < 1.0 makes alpha less important for the distance calculation. + * A value > 1.0 makes alpha more important for the distance calculation. + * Specify 0.0 to ignore the alpha part in the calculation. + */ + public static double getRGBColorDistanceLabCIE94(int argb1, int argb2, double alphaWeight) + { + Triple lab1 = convertRGBtoLab(argb1); + Triple lab2 = convertRGBtoLab(argb2); + alphaWeight = Math.max(0.0, Math.min(2.0, alphaWeight)); + double alpha1 = (double)((argb1 >> 24) & 0xff) * alphaWeight; + double alpha2 = (double)((argb2 >> 24) & 0xff) * alphaWeight; + return getColorDistanceLabCIE94(lab1.getValue0().doubleValue(), lab1.getValue1().doubleValue(), lab1.getValue2().doubleValue(), alpha1, + lab2.getValue0().doubleValue(), lab2.getValue1().doubleValue(), lab2.getValue2().doubleValue(), alpha2); + } + // Returns each color component as float array {b, g, r, a} in range [0.0, 1.0]. private static double[] getNormalizedColor(int color) { @@ -376,18 +683,16 @@ public static boolean medianCut(int[] pixels, int desiredColors, int[] palette, throw new NullPointerException(); if (desiredColors > 0 && desiredColors <= MAX_COLORS && palette.length >= desiredColors) { - PriorityQueue blockQueue = - new PriorityQueue(desiredColors, PixelBlock.PixelBlockComparator); + final PriorityQueue blockQueue = + new PriorityQueue<>(desiredColors, PixelBlock.PixelBlockComparator); - Pixel[] p = null; - p = new Pixel[pixels.length]; + final Pixel[] p = new Pixel[pixels.length]; int mask = ignoreAlpha ? 0xff000000: 0; - p = new Pixel[pixels.length]; for (int i = 0; i < p.length; i++) { p[i] = new Pixel(pixels[i] | mask); } - PixelBlock initialBlock = new PixelBlock(p); + final PixelBlock initialBlock = new PixelBlock(p); initialBlock.shrink(); blockQueue.add(initialBlock); while (blockQueue.size() < desiredColors) { @@ -398,8 +703,8 @@ public static boolean medianCut(int[] pixels, int desiredColors, int[] palette, Arrays.sort(longestBlock.getPixels(), longestBlock.offset(), longestBlock.offset() + longestBlock.size(), Pixel.PixelComparator.get(longestBlock.getLongestSideIndex())); - PixelBlock block1 = new PixelBlock(longestBlock.getPixels(), ofsBegin, ofsMedian - ofsBegin); - PixelBlock block2 = new PixelBlock(longestBlock.getPixels(), ofsMedian, ofsEnd - ofsMedian); + final PixelBlock block1 = new PixelBlock(longestBlock.getPixels(), ofsBegin, ofsMedian - ofsBegin); + final PixelBlock block2 = new PixelBlock(longestBlock.getPixels(), ofsMedian, ofsEnd - ofsMedian); block1.shrink(); block2.shrink(); blockQueue.add(block1); @@ -408,14 +713,14 @@ public static boolean medianCut(int[] pixels, int desiredColors, int[] palette, int palIndex = 0; while (!blockQueue.isEmpty() && palIndex < desiredColors) { - PixelBlock block = blockQueue.poll(); - int[] sum = {0, 0, 0, 0}; + final PixelBlock block = blockQueue.poll(); + final int[] sum = {0, 0, 0, 0}; for (int i = 0; i < block.size(); i++) { for (int j = 0; j < Pixel.MAX_SIZE; j++) { sum[j] += block.getPixel(i).getElement(j); } } - Pixel avgPixel = new Pixel(); + final Pixel avgPixel = new Pixel(); if (block.size() > 0) { for (int i = 0; i < Pixel.MAX_SIZE; i++) { avgPixel.color[i] = (byte)(sum[i] / block.size()); @@ -438,8 +743,19 @@ public static boolean medianCut(int[] pixels, int desiredColors, int[] palette, */ public static int[] loadPaletteBMP(Path file) throws Exception { - if (file != null && FileEx.create(file).isFile()) { - try (InputStream is = StreamUtils.getInputStream(file)) { + return loadPaletteBMP(new FileResourceEntry(file)); + } + + /** + * Attempts to load a palette from the specified Windows BMP file. + * @param entry The BMP resource entry to extract the palette from. + * @return The palette as ARGB integers. + * @throws Exception on error. + */ + public static int[] loadPaletteBMP(ResourceEntry entry) throws Exception + { + if (entry != null) { + try (InputStream is = entry.getResourceDataAsStream()) { byte[] signature = new byte[8]; is.read(signature); if ("BM".equals(new String(signature, 0, 2))) { @@ -458,19 +774,22 @@ public static int[] loadPaletteBMP(Path file) throws Exception byte[] palette = new byte[colorCount*4]; is.read(palette); int[] retVal = new int[colorCount]; - for (int i =0; i < colorCount; i++) { - retVal[i] = 0xff000000 | (DynamicArray.getInt(palette, i << 2) & 0x00ffffff); + for (int i = 0; i < colorCount; i++) { + retVal[i] = DynamicArray.getInt(palette, i << 2); + if ((retVal[i] & 0xff000000) == 0) { + retVal[i] |= 0xff000000; + } } return retVal; } else { - throw new Exception("Error loading palette from BMP file " + file.getFileName()); + throw new Exception("Error loading palette: " + entry.getResourceName()); } } else { - throw new Exception("Invalid BMP file " + file.getFileName()); + throw new Exception("Invalid BMP resource: " + entry.getResourceName()); } } catch (IOException e) { e.printStackTrace(); - throw new Exception("Unable to read BMP file " + file.getFileName()); + throw new Exception("Unable to read BMP resource: " + entry.getResourceName()); } } else { throw new Exception("File does not exist."); @@ -612,13 +931,18 @@ public static int[] loadPaletteACT(Path file) throws Exception */ public static int[] loadPaletteBAM(Path file, boolean preserveAlpha) throws Exception { - if (file != null && FileEx.create(file).isFile()) { - try (InputStream is = StreamUtils.getInputStream(file)) { + return loadPaletteBAM(new FileResourceEntry(file), preserveAlpha); + } + + public static int[] loadPaletteBAM(ResourceEntry entry, boolean preserveAlpha) throws Exception + { + if (entry != null) { + try (InputStream is = entry.getResourceDataAsStream()) { byte[] signature = new byte[8]; is.read(signature); String s = new String(signature); if ("BAM V1 ".equals(s) || "BAMCV1 ".equals(s)) { - byte[] bamData = new byte[(int)Files.size(file)]; + byte[] bamData = new byte[(int)entry.getResourceSize()]; System.arraycopy(signature, 0, bamData, 0, signature.length); is.read(bamData, signature.length, bamData.length - signature.length); if ("BAMCV1 ".equals(s)) { @@ -636,14 +960,14 @@ public static int[] loadPaletteBAM(Path file, boolean preserveAlpha) throws Exce } return retVal; } else { - throw new Exception("Error loading palette from BAM file " + file.getFileName()); + throw new Exception("Error loading palette: " + entry.getResourceName()); } } else { throw new Exception("Unsupport file type."); } } catch (IOException e) { e.printStackTrace(); - throw new Exception("Unable to read BAM file " + file.getFileName()); + throw new Exception("Unable to read BAM resource: " + entry.getResourceName()); } } else { throw new Exception("File does not exist."); @@ -718,6 +1042,25 @@ private static BufferedImage convertToIndexedImage(BufferedImage image) //-------------------------- INNER CLASSES -------------------------- + /** + * Represents a function to calculate the distance between two ARGB color values. + */ + public interface ColorDistanceFunc + { + /** + * Performs a calculation to determine the distance between the specified ARGB color values. The third argument + * indicates how much influence the alpha component should have. + * @param argb1 the first ARGB color value. + * @param argb2 the second ARGB color value + * @param alphaWeight Weight factor of the alpha component. Supported range: [0.0, 2.0]. + * A value < 1.0 makes alpha less important for the distance calculation. + * A value > 1.0 makes alpha more important for the distance calculation. + * Specify 0.0 to completely ignore the alpha part in the calculation. + * @return the relative distance between the two color values. + */ + double calculate(int argb1, int argb2, double alphaWeight); + } + private static class PixelBlock { private final Pixel minCorner, maxCorner; @@ -737,8 +1080,8 @@ public PixelBlock(Pixel[] pixels, int ofs, int len) this.pixels = pixels; this.ofs = ofs; this.len = len; - minCorner = new Pixel(Byte.MIN_VALUE, Byte.MIN_VALUE, Byte.MIN_VALUE, Byte.MIN_VALUE); - maxCorner = new Pixel(Byte.MAX_VALUE, Byte.MAX_VALUE, Byte.MAX_VALUE, Byte.MAX_VALUE); + minCorner = new Pixel(0); + maxCorner = new Pixel(0xffffffff); } public Pixel[] getPixels() @@ -807,13 +1150,9 @@ public void shrink() } } - public static Comparator PixelBlockComparator = new Comparator() { - @Override - public int compare(PixelBlock pb1, PixelBlock pb2) - { - // inverting natural order by switching sides - return pb2.getLongestSideLength() - pb1.getLongestSideLength(); - } + public static final Comparator PixelBlockComparator = (pb1, pb2) -> { + // inverting natural order by switching sides + return pb2.getLongestSideLength() - pb1.getLongestSideLength(); }; } @@ -835,11 +1174,6 @@ public Pixel(int color) (byte)(color & 0xff)}; } - public Pixel(byte r, byte g, byte b, byte a) - { - this.color = new byte[]{a, r, g, b}; - } - public int toColor() { return ((color[0] & 0xff) << 24) | ((color[1] & 0xff) << 16) | ((color[2] & 0xff) << 8) | (color[3] & 0xff); @@ -854,45 +1188,17 @@ public int getElement(int index) } } - public static List> PixelComparator = new ArrayList>(MAX_SIZE); - static { - PixelComparator.add(new Comparator() { - @Override - public int compare(Pixel p1, Pixel p2) - { - return p1.getElement(0) - p2.getElement(0); - } - }); - PixelComparator.add(new Comparator() { - @Override - public int compare(Pixel p1, Pixel p2) - { - return p1.getElement(1) - p2.getElement(1); - } - }); - PixelComparator.add(new Comparator() { - @Override - public int compare(Pixel p1, Pixel p2) - { - return p1.getElement(2) - p2.getElement(2); - } - }); - PixelComparator.add(new Comparator() { - @Override - public int compare(Pixel p1, Pixel p2) - { - return p1.getElement(3) - p2.getElement(3); - } - }); - } + public static final List> PixelComparator = new ArrayList>(MAX_SIZE) {{ + add((p1, p2) -> p1.getElement(0) - p2.getElement(0)); + add((p1, p2) -> p1.getElement(1) - p2.getElement(1)); + add((p1, p2) -> p1.getElement(2) - p2.getElement(2)); + add((p1, p2) -> p1.getElement(3) - p2.getElement(3)); + }}; } // Compare colors by perceived lightness. - private static class CompareByLightness implements Comparator { - @Override - public int compare(Integer c1, Integer c2) - { + private static final Comparator CompareByLightness = (c1, c2) -> { Integer[] colors = new Integer[] {c1, c2}; double[] dist = new double[colors.length]; for (int i = 0; i < colors.length; i++) { @@ -905,14 +1211,10 @@ public int compare(Integer c1, Integer c2) dist[i] = Math.sqrt(b + g + r + a); } return (dist[0] < dist[1]) ? -1 : ((dist[0] > dist[1]) ? 1 : 0); - } - } + }; // Compare colors by saturation. - private static class CompareBySaturation implements Comparator { - @Override - public int compare(Integer c1, Integer c2) - { + private static final Comparator CompareBySaturation = (c1, c2) -> { Integer[] colors = new Integer[] {c1, c2}; double[] dist = new double[colors.length]; for (int i = 0; i < colors.length; i++) { @@ -932,14 +1234,10 @@ public int compare(Integer c1, Integer c2) dist[i] = s; } return (dist[0] < dist[1]) ? -1 : ((dist[0] > dist[1]) ? 1 : 0); - } - } + }; // Compare colors by hue. - private static class CompareByHue implements Comparator { - @Override - public int compare(Integer c1, Integer c2) - { + private static final Comparator CompareByHue = (c1, c2) -> { Integer[] colors = new Integer[] {c1, c2}; double[] dist = new double[colors.length]; for (int i = 0; i < colors.length; i++) { @@ -970,50 +1268,49 @@ public int compare(Integer c1, Integer c2) dist[i] = h; } return (dist[0] < dist[1]) ? -1 : ((dist[0] > dist[1]) ? 1 : 0); - } - } + }; // Compare colors by red amount. - private static class CompareByRed implements Comparator { - @Override - public int compare(Integer c1, Integer c2) - { + private static final Comparator CompareByRed = (c1, c2) -> { int dist1 = (c1 >>> 16) & 0xff; int dist2 = (c2 >>> 16) & 0xff; return dist1 - dist2; - } - } + }; // Compare colors by green amount. - private static class CompareByGreen implements Comparator { - @Override - public int compare(Integer c1, Integer c2) - { + private static final Comparator CompareByGreen = (c1, c2) -> { int dist1 = (c1 >>> 8) & 0xff; int dist2 = (c2 >>> 8) & 0xff; return dist1 - dist2; - } - } + }; // Compare colors by blue amount. - private static class CompareByBlue implements Comparator { - @Override - public int compare(Integer c1, Integer c2) - { + private static final Comparator CompareByBlue = (c1, c2) -> { int dist1 = c1 & 0xff; int dist2 = c2 & 0xff; return dist1 - dist2; - } - } + }; // Compare colors by alpha. - private static class CompareByAlpha implements Comparator { - @Override - public int compare(Integer c1, Integer c2) - { - int dist1 = (c1 >>> 24) & 0xff; - int dist2 = (c2 >>> 24) & 0xff; - return dist1 - dist2; + private static final Comparator CompareByAlpha = (c1, c2) -> { + int dist1 = (c1 >>> 24) & 0xff; + int dist2 = (c2 >>> 24) & 0xff; + return dist1 - dist2; + }; + + // Compare colors by CIELAB L component. + private static final Comparator CompareByLabL = (c1, c2) -> { + Triple dist1 = convertRGBtoLab(c1); + Triple dist2 = convertRGBtoLab(c2); + if (dist1.getValue0() < dist2.getValue0()) { + return -1; + } else if (dist1.getValue0() > dist2.getValue0()) { + return 1; + } else { + return 0; } - } +// int dist1 = (c1 >>> 24) & 0xff; +// int dist2 = (c2 >>> 24) & 0xff; +// return dist1 - dist2; + }; } diff --git a/src/org/infinity/resource/graphics/DxtEncoder.java b/src/org/infinity/resource/graphics/DxtEncoder.java index 56adf1189..b5ad4d68d 100644 --- a/src/org/infinity/resource/graphics/DxtEncoder.java +++ b/src/org/infinity/resource/graphics/DxtEncoder.java @@ -48,7 +48,8 @@ public static enum DxtType { DXT1, DXT3, DXT5 } * @return A data block containing the DXT-encoded image. * @throws Exception */ - static public byte[] encodeImage(int[] pixels, int width, int height, DxtType dxtType) throws Exception + static public byte[] encodeImage(final int[] pixels, final int width, final int height, + final DxtType dxtType) throws Exception { // consistency check if (dxtType == null) @@ -60,7 +61,7 @@ static public byte[] encodeImage(int[] pixels, int width, int height, DxtType dx if (pixels == null || pixels.length < width*height) throw new Exception("Insufficient source data."); - int size = calcImageSize(width, height, dxtType); + final int size = calcImageSize(width, height, dxtType); byte[] output = new byte[size]; try { encodeImage(pixels, width, height, output, dxtType); @@ -81,8 +82,8 @@ static public byte[] encodeImage(int[] pixels, int width, int height, DxtType dx * @param dxtType The compression type to use. * @throws Exception */ - static public void encodeImage(int[] pixels, int width, int height, byte[] output, - DxtType dxtType) throws Exception + static public void encodeImage(final int[] pixels, final int width, final int height, final byte[] output, + final DxtType dxtType) throws Exception { // consistency check if (dxtType == null) @@ -98,10 +99,10 @@ static public void encodeImage(int[] pixels, int width, int height, byte[] outpu calcImageSize(width, height, dxtType), (output == null) ? 0 : output.length)); int outputOfs = 0; // points to the end of encoded data - int bw = width / 4; - int bh = height / 4; - int[] inBlock = new int[16]; - byte[] outBlock = new byte[calcBlockSize(dxtType)]; + final int bw = width / 4; + final int bh = height / 4; + final int[] inBlock = new int[16]; + final byte[] outBlock = new byte[calcBlockSize(dxtType)]; for (int y = 0; y < bh; y++) { for (int x = 0; x < bw; x++) { // create 4x4 block of pixels for DXTn compression @@ -124,7 +125,7 @@ static public void encodeImage(int[] pixels, int width, int height, byte[] outpu * @param block Data block to store the compressed DXTn data in. * @param dxtType The DXT type to use. */ - public static void encodeBlock(int[] pixels, byte[] block, DxtType dxtType) throws Exception + public static void encodeBlock(final int[] pixels, final byte[] block, final DxtType dxtType) throws Exception { if (pixels == null || pixels.length < 16) throw new Exception("Insufficient source data."); @@ -132,14 +133,14 @@ public static void encodeBlock(int[] pixels, byte[] block, DxtType dxtType) thro throw new Exception(String.format("Insufficient output space. Needed: %d bytes, available: %d bytes", calcBlockSize(dxtType), (block == null) ? 0 : block.length)); - byte[] colorBlock = new byte[8]; + final byte[] colorBlock = new byte[8]; byte[] alphaBlock = null; if (dxtType == DxtType.DXT3 || dxtType == DxtType.DXT5) { alphaBlock = new byte[8]; } // create the minimal point set - ColorSet colors = new ColorSet(pixels, dxtType); + final ColorSet colors = new ColorSet(pixels, dxtType); // check the compression type and compress color ColorFit fit = null; @@ -179,7 +180,7 @@ public static void encodeBlock(int[] pixels, byte[] block, DxtType dxtType) thro * @param dxtType The desired compression. * @return The number of bytes to hold the compressed data of a 4x4 block of pixels. */ - public static int calcBlockSize(DxtType dxtType) + public static int calcBlockSize(final DxtType dxtType) { if (dxtType != null) { switch (dxtType) { @@ -200,7 +201,7 @@ public static int calcBlockSize(DxtType dxtType) * @param dxtType The desired compression type. * @return The number of bytes required to hold the compressed image data. */ - public static int calcImageSize(int width, int height, DxtType dxtType) + public static int calcImageSize(int width, int height, final DxtType dxtType) { if (width <= 0 || height <= 0 || dxtType == null) return 0; @@ -223,12 +224,12 @@ private static class ColorSet private boolean transparent; // Extracts the color component at the specified pos (0..3 = blue,green,red,alpha) - public static int argb(int color, int pos) + public static int argb(final int color, final int pos) { return (color >>> ((pos & 3) << 3)) & 0xff; } - public ColorSet(int[] pixels, DxtType dxtType) + public ColorSet(final int[] pixels, final DxtType dxtType) { points = new Vec3[16]; weights = new float[16]; @@ -236,7 +237,7 @@ public ColorSet(int[] pixels, DxtType dxtType) count = 0; transparent = false; - boolean isDXT1 = (dxtType == DxtType.DXT1); + final boolean isDXT1 = (dxtType == DxtType.DXT1); // create minimal set for (int i = 0; i < 16; i++) { @@ -252,12 +253,12 @@ public ColorSet(int[] pixels, DxtType dxtType) // allocate new points if (j == i) { // normalize coordinates to [0, 1] - float x = (float)argb(pixels[i], 2) / 255.0f; - float y = (float)argb(pixels[i], 1) / 255.0f; - float z = (float)argb(pixels[i], 0) / 255.0f; + final float x = (float)argb(pixels[i], 2) / 255.0f; + final float y = (float)argb(pixels[i], 1) / 255.0f; + final float z = (float)argb(pixels[i], 0) / 255.0f; // ensure there is always non-zero weight even for zero alpha - float w = (float)(argb(pixels[i], 3) + 1) / 256.0f; + final float w = (float)(argb(pixels[i], 3) + 1) / 256.0f; // add the points points[count] = new Vec3(x, y, z); @@ -269,15 +270,15 @@ public ColorSet(int[] pixels, DxtType dxtType) } // check for a match - boolean match = (argb(pixels[i], 0) == argb(pixels[j], 0) && - argb(pixels[i], 1) == argb(pixels[j], 1) && - argb(pixels[i], 2) == argb(pixels[j], 2) && - (argb(pixels[j], 3) >= 128 || !isDXT1)); + final boolean match = (argb(pixels[i], 0) == argb(pixels[j], 0) && + argb(pixels[i], 1) == argb(pixels[j], 1) && + argb(pixels[i], 2) == argb(pixels[j], 2) && + (argb(pixels[j], 3) >= 128 || !isDXT1)); if (match) { // get the index of the match - int index = remap[j]; + final int index = remap[j]; // ensure there is always non-zero weight even for zero alpha - float w = (float)(argb(pixels[i], 3) + 1) / 256.0f; + final float w = (float)(argb(pixels[i], 3) + 1) / 256.0f; // map to this point and increase the weight weights[index] += w; remap[i] = index; @@ -300,10 +301,10 @@ public ColorSet(int[] pixels, DxtType dxtType) public boolean isTransparent() { return transparent; } - public void remapIndices(int[] source, int[] target) + public void remapIndices(final int[] source, final int[] target) { for (int i = 0; i < 16; i++) { - int j = remap[i]; + final int j = remap[i]; target[i] = (j == -1) ? 3 : source[j]; } } @@ -312,8 +313,8 @@ public void remapIndices(int[] source, int[] target) private static abstract class ColorFit { - protected ColorSet colors; - protected DxtType dxtType; + protected final ColorSet colors; + protected final DxtType dxtType; public ColorFit(ColorSet colors, DxtType dxtType) { @@ -321,9 +322,9 @@ public ColorFit(ColorSet colors, DxtType dxtType) this.dxtType = dxtType; } - public void compress(byte[] block) + public void compress(final byte[] block) { - boolean isDXT1 = (dxtType == DxtType.DXT1); + final boolean isDXT1 = (dxtType == DxtType.DXT1); if (isDXT1) { compress3(block); if (!colors.isTransparent()) { @@ -334,8 +335,8 @@ public void compress(byte[] block) } } - protected abstract void compress3(byte[] block); - protected abstract void compress4(byte[] block); + protected abstract void compress3(final byte[] block); + protected abstract void compress4(final byte[] block); } @@ -348,7 +349,7 @@ private static class SingleColorFit extends ColorFit private int error; private int bestError; - public SingleColorFit(ColorSet colors, DxtType dxtType) + public SingleColorFit(final ColorSet colors, final DxtType dxtType) { super(colors, dxtType); color = new int[3]; @@ -356,7 +357,7 @@ public SingleColorFit(ColorSet colors, DxtType dxtType) end = new Vec3(); // grab the single color - Vec3 value = this.colors.getPoints()[0]; + final Vec3 value = this.colors.getPoints()[0]; color[0] = Misc.floatToInt(255.0f*value.x(), 255); color[1] = Misc.floatToInt(255.0f*value.y(), 255); color[2] = Misc.floatToInt(255.0f*value.z(), 255); @@ -366,10 +367,10 @@ public SingleColorFit(ColorSet colors, DxtType dxtType) } @Override - protected void compress3(byte[] block) + protected void compress3(final byte[] block) { // build the table of lookups - SingleColorLookup[][] lookups = new SingleColorLookup[][]{ + final SingleColorLookup[][] lookups = new SingleColorLookup[][]{ Lookups.lookup53, Lookups.lookup63, Lookups.lookup53 }; @@ -379,7 +380,7 @@ protected void compress3(byte[] block) // build the block if we win if (error < bestError) { // remap indices - int[] indices = new int[16]; + final int[] indices = new int[16]; colors.remapIndices(new int[]{index}, indices); // save the block @@ -391,10 +392,10 @@ protected void compress3(byte[] block) } @Override - protected void compress4(byte[] block) + protected void compress4(final byte[] block) { // build the table of lookups - SingleColorLookup[][] lookups = new SingleColorLookup[][]{ + final SingleColorLookup[][] lookups = new SingleColorLookup[][]{ Lookups.lookup54, Lookups.lookup64, Lookups.lookup54 }; @@ -404,7 +405,7 @@ protected void compress4(byte[] block) // build the block if we win if (error < bestError) { // remap indices - int[] indices = new int[16]; + final int[] indices = new int[16]; colors.remapIndices(new int[]{index}, indices); // save the block @@ -415,23 +416,23 @@ protected void compress4(byte[] block) } } - protected void computeEndPoints(SingleColorLookup[][] lookups) + protected void computeEndPoints(final SingleColorLookup[][] lookups) { // check each index combination (endpoint or intermediate) this.error = Integer.MAX_VALUE; for (int index = 0; index < 2; index++) { - SourceBlock[] sources = new SourceBlock[3]; + final SourceBlock[] sources = new SourceBlock[3]; int error = 0; for (int channel = 0; channel < 3; channel++) { // grab the lookup table and index for this channel - SingleColorLookup[] lookup = lookups[channel]; - int target = color[channel]; + final SingleColorLookup[] lookup = lookups[channel]; + final int target = color[channel]; // store a pointer to the source for this channel sources[channel] = lookup[target].sources[index]; // accumulate the error - int diff = sources[channel].error; + final int diff = sources[channel].error; error += diff * diff; } @@ -455,7 +456,7 @@ private static class RangeFit extends ColorFit private Vec3 metric, start, end; private float bestError; - public RangeFit(ColorSet colors, DxtType dxtType) + public RangeFit(final ColorSet colors, final DxtType dxtType) { super(colors, dxtType); @@ -466,15 +467,15 @@ public RangeFit(ColorSet colors, DxtType dxtType) bestError = Float.MAX_VALUE; // cache some values - int count = this.colors.getCount(); - Vec3[] values = this.colors.getPoints(); - float[] weights = this.colors.getWeights(); + final int count = this.colors.getCount(); + final Vec3[] values = this.colors.getPoints(); + final float[] weights = this.colors.getWeights(); // get the covariance matrix - Sym3x3 covariance = Sym3x3.computeWeightedCovariance(count, values, weights); + final Sym3x3 covariance = Sym3x3.computeWeightedCovariance(count, values, weights); // compute the principle component - Vec3 principle = Sym3x3.computePrincipleComponent(covariance); + final Vec3 principle = Sym3x3.computePrincipleComponent(covariance); // get the min and max range as the codebook endpoints Vec3 start = new Vec3(); @@ -486,7 +487,7 @@ public RangeFit(ColorSet colors, DxtType dxtType) start = end = values[0]; min = max = Vec3.dot(values[0], principle); for (int i = 1; i < count; i++) { - float val = Vec3.dot(values[i], principle); + final float val = Vec3.dot(values[i], principle); if (val < min) { start = values[i]; min = val; @@ -512,20 +513,20 @@ public RangeFit(ColorSet colors, DxtType dxtType) } @Override - protected void compress3(byte[] block) + protected void compress3(final byte[] block) { // cache some values - int count = colors.getCount(); - Vec3[] values = colors.getPoints(); + final int count = colors.getCount(); + final Vec3[] values = colors.getPoints(); // create a codebook - Vec3[] codes = new Vec3[3]; + final Vec3[] codes = new Vec3[3]; codes[0] = start; codes[1] = end; codes[2] = Vec3.mul(start, 0.5f).add(Vec3.mul(end, 0.5f)); // match each point to the closest code - int[] closest = new int[16]; + final int[] closest = new int[16]; float error = 0.0f; for (int i = 0; i < count; i++) { // find the closest code @@ -549,7 +550,7 @@ protected void compress3(byte[] block) // save this scheme if it wins if (error < bestError) { // remap the indices - int[] indices = new int[16]; + final int[] indices = new int[16]; colors.remapIndices(closest, indices); // save the block @@ -561,21 +562,21 @@ protected void compress3(byte[] block) } @Override - protected void compress4(byte[] block) + protected void compress4(final byte[] block) { // cache some values - int count = colors.getCount(); - Vec3[] values = colors.getPoints(); + final int count = colors.getCount(); + final Vec3[] values = colors.getPoints(); // create a codebook - Vec3[] codes = new Vec3[4]; + final Vec3[] codes = new Vec3[4]; codes[0] = start; codes[1] = end; codes[2] = Vec3.mul(start, 2.0f/3.0f).add(Vec3.mul(end, 1.0f/3.0f)); codes[3] = Vec3.mul(start, 1.0f/3.0f).add(Vec3.mul(end, 2.0f/3.0f)); // match each point to the closest code - int[] closest = new int[16]; + final int[] closest = new int[16]; float error = 0.0f; for (int i = 0; i < count; i++) { // find the closest code @@ -618,12 +619,13 @@ private static class ClusterFit extends ColorFit private final int[] order; private final Vec4[] pointsWeights; - private Vec3 principle; + private final Vec3 principle; + private final Vec4 metric; + private Vec4 xsum_wsum; - private Vec4 metric; private Vec4 bestError; - public ClusterFit(ColorSet colors, DxtType dxtType) + public ClusterFit(final ColorSet colors, final DxtType dxtType) { super(colors, dxtType); order = new int[16*IterationCount]; @@ -636,16 +638,16 @@ public ClusterFit(ColorSet colors, DxtType dxtType) metric = new Vec4(0.2126f, 0.7152f, 0.0722f, 0.0f); // get the covariance matrix - Sym3x3 covariance = Sym3x3.computeWeightedCovariance(this.colors.getCount(), - this.colors.getPoints(), - this.colors.getWeights()); + final Sym3x3 covariance = Sym3x3.computeWeightedCovariance(this.colors.getCount(), + this.colors.getPoints(), + this.colors.getWeights()); // compute the principle component principle = Sym3x3.computePrincipleComponent(covariance); } @Override - protected void compress3(byte[] block) + protected void compress3(final byte[] block) { // declare variables final int count = colors.getCount(); @@ -664,33 +666,33 @@ protected void compress3(byte[] block) Vec4 bestStart = new Vec4(); Vec4 bestEnd = new Vec4(); Vec4 bestError = this.bestError; - int[] bestIndices = new int[16]; + final int[] bestIndices = new int[16]; int bestIteration = 0; int bestI = 0, bestJ = 0; // loop over iterations (we avoid the case that all points in first or last cluster) for (int iterIndex = 0; ; ) { // first cluster [0, i) is at the start - Vec4 part0 = new Vec4(); + final Vec4 part0 = new Vec4(); for (int i = 0; i < count; i++) { // second cluster [i, j) is half along - Vec4 part1 = (i == 0) ? (Vec4)pointsWeights[0].clone() : new Vec4(); - int jmin = (i == 0) ? 1 : i; + final Vec4 part1 = (i == 0) ? (Vec4)pointsWeights[0].clone() : new Vec4(); + final int jmin = (i == 0) ? 1 : i; for (int j = jmin; ; ) { // last cluster [j, count) is at the end - Vec4 part2 = Vec4.sub(xsum_wsum, part1).sub(part0); + final Vec4 part2 = Vec4.sub(xsum_wsum, part1).sub(part0); // compute least squares terms directly - Vec4 alphaXSum = Vec4.multiplyAdd(part1, halfHalf2, part0); - Vec4 alpha2Sum = alphaXSum.splatW(); + final Vec4 alphaXSum = Vec4.multiplyAdd(part1, halfHalf2, part0); + final Vec4 alpha2Sum = alphaXSum.splatW(); - Vec4 betaXSum = Vec4.multiplyAdd(part1, halfHalf2, part2); - Vec4 beta2Sum = betaXSum.splatW(); + final Vec4 betaXSum = Vec4.multiplyAdd(part1, halfHalf2, part2); + final Vec4 beta2Sum = betaXSum.splatW(); - Vec4 alphaBetaSum = Vec4.mul(part1, halfHalf2).splatW(); + final Vec4 alphaBetaSum = Vec4.mul(part1, halfHalf2).splatW(); // compute the least squares optimal points - Vec4 factor = Vec4.reciprocal(Vec4.negMulSub(alphaBetaSum, alphaBetaSum, Vec4.mul(alpha2Sum, beta2Sum))); + final Vec4 factor = Vec4.reciprocal(Vec4.negMulSub(alphaBetaSum, alphaBetaSum, Vec4.mul(alpha2Sum, beta2Sum))); Vec4 a = Vec4.negMulSub(betaXSum, alphaBetaSum, Vec4.mul(alphaXSum, beta2Sum)).mul(factor); Vec4 b = Vec4.negMulSub(alphaXSum, alphaBetaSum, Vec4.mul(betaXSum, alpha2Sum)).mul(factor); @@ -701,14 +703,14 @@ protected void compress3(byte[] block) b = Vec4.truncate(Vec4.multiplyAdd(grid, b, half)).mul(gridrcp); // compute the error (we skip the constant xxsum) - Vec4 e1 = Vec4.multiplyAdd(Vec4.mul(a, a), alpha2Sum, Vec4.mul(b, b).mul(beta2Sum)); - Vec4 e2 = Vec4.negMulSub(a, alphaXSum, Vec4.mul(a, b).mul(alphaBetaSum)); - Vec4 e3 = Vec4.negMulSub(b, betaXSum, e2); - Vec4 e4 = Vec4.multiplyAdd(two, e3, e1); + final Vec4 e1 = Vec4.multiplyAdd(Vec4.mul(a, a), alpha2Sum, Vec4.mul(b, b).mul(beta2Sum)); + final Vec4 e2 = Vec4.negMulSub(a, alphaXSum, Vec4.mul(a, b).mul(alphaBetaSum)); + final Vec4 e3 = Vec4.negMulSub(b, betaXSum, e2); + final Vec4 e4 = Vec4.multiplyAdd(two, e3, e1); // apply the metric to the error terms - Vec4 e5 = Vec4.mul(e4, metric); - Vec4 error = Vec4.add(e5.splatX(), e5.splatY()).add(e5.splatZ()); + final Vec4 e5 = Vec4.mul(e4, metric); + final Vec4 error = Vec4.add(e5.splatX(), e5.splatY()).add(e5.splatZ()); // keep the solution if it wins if (Vec4.compareAnyLessThan(error, bestError)) { @@ -739,7 +741,7 @@ protected void compress3(byte[] block) break; // stop if a new iteration is an ordering that has already been tried - Vec3 axis = Vec4.sub(bestEnd, bestStart).getVec3(); + final Vec3 axis = Vec4.sub(bestEnd, bestStart).getVec3(); if (!constructOrdering(axis, iterIndex)) break; } @@ -747,8 +749,8 @@ protected void compress3(byte[] block) // save the block if necessary if (Vec4.compareAnyLessThan(bestError, this.bestError)) { // remap the indices - int orderIdx = 16*bestIteration; - int[] unordered = new int[16]; + final int orderIdx = 16*bestIteration; + final int[] unordered = new int[16]; for (int m = 0; m < bestI; m++) unordered[order[orderIdx+m]] = 0; for (int m = bestI; m < bestJ; m++) @@ -767,7 +769,7 @@ protected void compress3(byte[] block) } @Override - protected void compress4(byte[] block) + protected void compress4(final byte[] block) { // declare variables final int count = colors.getCount(); @@ -788,39 +790,39 @@ protected void compress4(byte[] block) Vec4 bestStart = new Vec4(); Vec4 bestEnd = new Vec4(); Vec4 bestError = this.bestError; - int[] bestIndices = new int[16]; + final int[] bestIndices = new int[16]; int bestIteration = 0; int bestI = 0, bestJ = 0, bestK = 0; // loop over iterations (we avoid the case all points in first or last cluster) for (int iterIndex = 0; ; ) { // first cluster [0, i) is at the start - Vec4 part0 = new Vec4(); + final Vec4 part0 = new Vec4(); for (int i = 0; i < count; i++) { // second cluster [i, j) is one third along - Vec4 part1 = new Vec4(); + final Vec4 part1 = new Vec4(); for (int j = i; ; ) { // third cluster [j, k) is two thirds along - Vec4 part2 = (j == 0) ? (Vec4)pointsWeights[0].clone() : new Vec4(); - int kmin = (j == 0) ? 1 : j; + final Vec4 part2 = (j == 0) ? (Vec4)pointsWeights[0].clone() : new Vec4(); + final int kmin = (j == 0) ? 1 : j; for (int k = kmin; ; ) { // last cluster [k, count) is at the end - Vec4 part3 = Vec4.sub(xsum_wsum, part2).sub(part1).sub(part0); + final Vec4 part3 = Vec4.sub(xsum_wsum, part2).sub(part1).sub(part0); // compute least squares terms directly - Vec4 alphaXSum = Vec4.multiplyAdd(part2, oneThirdOneThird2, - Vec4.multiplyAdd(part1, twoThirdsTwoThirds2, part0)); - Vec4 alpha2Sum = alphaXSum.splatW(); + final Vec4 alphaXSum = Vec4.multiplyAdd(part2, oneThirdOneThird2, + Vec4.multiplyAdd(part1, twoThirdsTwoThirds2, part0)); + final Vec4 alpha2Sum = alphaXSum.splatW(); - Vec4 betaXSum = Vec4.multiplyAdd(part1, oneThirdOneThird2, - Vec4.multiplyAdd(part2, twoThirdsTwoThirds2, part3)); - Vec4 beta2Sum = betaXSum.splatW(); + final Vec4 betaXSum = Vec4.multiplyAdd(part1, oneThirdOneThird2, + Vec4.multiplyAdd(part2, twoThirdsTwoThirds2, part3)); + final Vec4 beta2Sum = betaXSum.splatW(); - Vec4 alphaBetaSum = Vec4.mul(twoNineth, Vec4.add(part1, part2).splatW()); + final Vec4 alphaBetaSum = Vec4.mul(twoNineth, Vec4.add(part1, part2).splatW()); // compute the least-squares optimal points - Vec4 factor = Vec4.reciprocal(Vec4.negMulSub(alphaBetaSum, alphaBetaSum, - Vec4.mul(alpha2Sum, beta2Sum))); + final Vec4 factor = Vec4.reciprocal(Vec4.negMulSub(alphaBetaSum, alphaBetaSum, + Vec4.mul(alpha2Sum, beta2Sum))); Vec4 a = Vec4.negMulSub(betaXSum, alphaBetaSum, Vec4.mul(alphaXSum, beta2Sum)).mul(factor); Vec4 b = Vec4.negMulSub(alphaXSum, alphaBetaSum, Vec4.mul(betaXSum, alpha2Sum)).mul(factor); @@ -831,14 +833,14 @@ protected void compress4(byte[] block) b = Vec4.truncate(Vec4.multiplyAdd(grid, b, half)).mul(gridrcp); // compute the error (we skip the constant xxsum) - Vec4 e1 = Vec4.multiplyAdd(Vec4.mul(a, a), alpha2Sum, Vec4.mul(b, b).mul(beta2Sum)); - Vec4 e2 = Vec4.negMulSub(a, alphaXSum, Vec4.mul(a, b).mul(alphaBetaSum)); - Vec4 e3 = Vec4.negMulSub(b, betaXSum, e2); - Vec4 e4 = Vec4.multiplyAdd(two, e3, e1); + final Vec4 e1 = Vec4.multiplyAdd(Vec4.mul(a, a), alpha2Sum, Vec4.mul(b, b).mul(beta2Sum)); + final Vec4 e2 = Vec4.negMulSub(a, alphaXSum, Vec4.mul(a, b).mul(alphaBetaSum)); + final Vec4 e3 = Vec4.negMulSub(b, betaXSum, e2); + final Vec4 e4 = Vec4.multiplyAdd(two, e3, e1); // apply the metric to the error term - Vec4 e5 = Vec4.mul(e4, metric); - Vec4 error = Vec4.add(e5.splatX(), e5.splatY()).add(e5.splatZ()); + final Vec4 e5 = Vec4.mul(e4, metric); + final Vec4 error = Vec4.add(e5.splatX(), e5.splatY()).add(e5.splatZ()); // keep the solution if it wins if (Vec4.compareAnyLessThan(error, bestError)) { @@ -875,7 +877,7 @@ protected void compress4(byte[] block) break; // stop if a new iteration is an ordering that has already been tried - Vec3 axis = Vec4.sub(bestEnd, bestStart).getVec3(); + final Vec3 axis = Vec4.sub(bestEnd, bestStart).getVec3(); if (!constructOrdering(axis, iterIndex)) break; } @@ -883,8 +885,8 @@ protected void compress4(byte[] block) // save the block if necessary if (Vec4.compareAnyLessThan(bestError, this.bestError)) { // remap the indices - int orderIdx = 16*bestIteration; - int[] unordered = new int[16]; + final int orderIdx = 16*bestIteration; + final int[] unordered = new int[16]; for (int m = 0; m < bestI; m++) unordered[order[orderIdx+m]] = 0; for (int m = bestI; m < bestJ; m++) @@ -904,15 +906,15 @@ protected void compress4(byte[] block) } } - private boolean constructOrdering(Vec3 axis, int iteration) + private boolean constructOrdering(final Vec3 axis, final int iteration) { // cache some values final int count = colors.getCount(); final Vec3[] values = colors.getPoints(); // build list of dot products - float[] dps = new float[16]; - int orderIdx = 16*iteration; + final float[] dps = new float[16]; + final int orderIdx = 16*iteration; for (int i = 0; i < count; i++) { dps[i] = Vec3.dot(values[i], axis); order[orderIdx+i] = i; @@ -921,14 +923,14 @@ private boolean constructOrdering(Vec3 axis, int iteration) // stable sort using them for (int i = 0; i < count; i++) { for (int j = i; j > 0 && dps[j] < dps[j-1]; j--) { - float tmp1 = dps[j]; dps[j] = dps[j-1]; dps[j-1] = tmp1; - int tmp2 = order[orderIdx+j]; order[orderIdx+j] = order[orderIdx+j-1]; order[orderIdx+j-1] = tmp2; + final float tmp1 = dps[j]; dps[j] = dps[j-1]; dps[j-1] = tmp1; + final int tmp2 = order[orderIdx+j]; order[orderIdx+j] = order[orderIdx+j-1]; order[orderIdx+j-1] = tmp2; } } // check this ordering is unique for (int it = 0; it < iteration; it++) { - int prevIdx = 16*it; + final int prevIdx = 16*it; boolean same = true; for (int i = 0; i < count; i++) { if (order[orderIdx+i] != order[prevIdx+i]) { @@ -946,9 +948,9 @@ private boolean constructOrdering(Vec3 axis, int iteration) xsum_wsum = new Vec4(); for (int i = 0; i < count; i++) { int j = order[orderIdx+i]; - Vec4 p = new Vec4(unweighted[j].x(), unweighted[j].y(), unweighted[j].z(), 1.0f); - Vec4 w = new Vec4(weights[j]); - Vec4 x = Vec4.mul(p, w); + final Vec4 p = new Vec4(unweighted[j].x(), unweighted[j].y(), unweighted[j].z(), 1.0f); + final Vec4 w = new Vec4(weights[j]); + final Vec4 x = Vec4.mul(p, w); pointsWeights[i] = x; xsum_wsum.add(x); } @@ -959,20 +961,20 @@ private boolean constructOrdering(Vec3 axis, int iteration) private static final class ColorBlock { - public static void writeColorBlock3(Vec3 start, Vec3 end, int[] indices, byte[] block) + public static void writeColorBlock3(final Vec3 start, final Vec3 end, final int[] indices, final byte[] block) { // get the packed values int a = floatTo565(start); int b = floatTo565(end); // remap the indices - int[] remapped = new int[16]; + final int[] remapped = new int[16]; if (a <= b) { // use the indices directly System.arraycopy(indices, 0, remapped, 0, 16); } else { // swap a and b - int tmp = a; a = b; b = tmp; + final int tmp = a; a = b; b = tmp; for (int i = 0; i < 16; i++) { if (indices[i] == 0) { remapped[i] = 1; @@ -988,17 +990,17 @@ public static void writeColorBlock3(Vec3 start, Vec3 end, int[] indices, byte[] writeColorBlock(a, b, remapped, block); } - public static void writeColorBlock4(Vec3 start, Vec3 end, int[] indices, byte[] block) + public static void writeColorBlock4(final Vec3 start, final Vec3 end, final int[] indices, final byte[] block) { // get the packed values int a = floatTo565(start); int b = floatTo565(end); // remap the indices - int[] remapped = new int[16]; + final int[] remapped = new int[16]; if (a < b) { // swap a and b - int tmp = a; a = b; b = tmp; + final int tmp = a; a = b; b = tmp; for (int i = 0; i < 16; i++) { remapped[i] = (indices[i] ^ 1) & 3; } @@ -1016,18 +1018,18 @@ public static void writeColorBlock4(Vec3 start, Vec3 end, int[] indices, byte[] writeColorBlock(a, b, remapped, block); } - private static int floatTo565(Vec3 color) + private static int floatTo565(final Vec3 color) { // get the components in the correct range - int r = Misc.floatToInt(31.0f*color.x(), 31); - int g = Misc.floatToInt(63.0f*color.y(), 63); - int b = Misc.floatToInt(31.0f*color.z(), 31); + final int r = Misc.floatToInt(31.0f*color.x(), 31); + final int g = Misc.floatToInt(63.0f*color.y(), 63); + final int b = Misc.floatToInt(31.0f*color.z(), 31); // pack the color into a single value return ((r << 11) | (g << 5) | b) & 0xffff; } - private static void writeColorBlock(int a, int b, int[] indices, byte[] block) + private static void writeColorBlock(final int a, final int b, final int[] indices, final byte[] block) { // write the endpoints block[0] = (byte)(a & 0xff); @@ -1037,7 +1039,7 @@ private static void writeColorBlock(int a, int b, int[] indices, byte[] block) // write the indices for (int i = 0; i < 4; i++) { - int idx = 4*i; + final int idx = 4*i; block[i+4] = (byte)((indices[idx+0]) | (indices[idx+1] << 2) | (indices[idx+2] << 4) | (indices[idx+3] << 6)); } @@ -1046,22 +1048,22 @@ private static void writeColorBlock(int a, int b, int[] indices, byte[] block) private static final class Alpha { - public static void compressAlphaDxt3(int[] pixels, byte[] block) + public static void compressAlphaDxt3(final int[] pixels, final byte[] block) { // quantize and pack the alpha values pairwise for (int i = 0; i < 8; i++) { // quantize down to 4 bits - float alpha1 = (float)ColorSet.argb(pixels[2*i], 3) * (15.0f / 255.0f); - float alpha2 = (float)ColorSet.argb(pixels[2*i+1], 3) * (15.0f / 255.0f); - int quant1 = Misc.floatToInt(alpha1, 15); - int quant2 = Misc.floatToInt(alpha2, 15); + final float alpha1 = (float)ColorSet.argb(pixels[2*i], 3) * (15.0f / 255.0f); + final float alpha2 = (float)ColorSet.argb(pixels[2*i+1], 3) * (15.0f / 255.0f); + final int quant1 = Misc.floatToInt(alpha1, 15); + final int quant2 = Misc.floatToInt(alpha2, 15); // pack into the byte block[i] = (byte)(quant1 | (quant2 << 4)); } } - public static void compressAlphaDxt5(int[] pixels, byte[] block) + public static void compressAlphaDxt5(final int[] pixels, final byte[] block) { // get the range for 5-alpha and 7-alpha interpolation int min5 = 255; @@ -1088,7 +1090,7 @@ public static void compressAlphaDxt5(int[] pixels, byte[] block) min7 = max7; // fix the range to be the minimum in each case - int[] minmax = new int[2]; + final int[] minmax = new int[2]; minmax[0] = min5; minmax[1] = max5; fixRange(minmax, 5); min5 = minmax[0]; max5 = minmax[1]; @@ -1097,7 +1099,7 @@ public static void compressAlphaDxt5(int[] pixels, byte[] block) min7 = minmax[0]; max7 = minmax[1]; // set up the 5-alpha code book - int[] codes5 = new int[8]; + final int[] codes5 = new int[8]; codes5[0] = min5; codes5[1] = max5; for (int i = 1; i < 5; i++) { @@ -1107,7 +1109,7 @@ public static void compressAlphaDxt5(int[] pixels, byte[] block) codes5[7] = 255; // set up the 7-alpha code book - int[] codes7 = new int[8]; + final int[] codes7 = new int[8]; codes7[0] = min7; codes7[1] = max7; for (int i = 1; i < 7; i++) { @@ -1115,10 +1117,10 @@ public static void compressAlphaDxt5(int[] pixels, byte[] block) } // fit the data to both code books - int[] indices5 = new int[16]; - int[] indices7 = new int[16]; - int err5 = fitCodes(pixels, codes5, indices5); - int err7 = fitCodes(pixels, codes7, indices7); + final int[] indices5 = new int[16]; + final int[] indices7 = new int[16]; + final int err5 = fitCodes(pixels, codes5, indices5); + final int err7 = fitCodes(pixels, codes7, indices7); // save the block with the least error if (err5 <= err7) { @@ -1128,7 +1130,7 @@ public static void compressAlphaDxt5(int[] pixels, byte[] block) } } - private static void fixRange(int[] minMax, int steps) + private static void fixRange(final int[] minMax, final int steps) { if (minMax[1] - minMax[0] < steps) minMax[1] = Math.min(minMax[0] + steps, 255); @@ -1142,7 +1144,7 @@ private static int fitCodes(int[] pixels, int[] codes, int[] indices) int err = 0; for (int i = 0; i < 16; i++) { // find the least error and corresponding index - int value = ColorSet.argb(pixels[i], 3); + final int value = ColorSet.argb(pixels[i], 3); int least = Integer.MAX_VALUE; int index = 0; for (int j = 0; j < 8; j++) { @@ -1166,7 +1168,7 @@ private static int fitCodes(int[] pixels, int[] codes, int[] indices) return err; } - private static void writeAlphaBlock(int alpha0, int alpha1, int[] indices, byte[] block) + private static void writeAlphaBlock(final int alpha0, final int alpha1, final int[] indices, final byte[] block) { // write the first two bytes block[0] = (byte)(alpha0 & 0xff); @@ -1183,20 +1185,20 @@ private static void writeAlphaBlock(int alpha0, int alpha1, int[] indices, byte[ // store in 3 bytes for (int j = 0; j < 3; j++) { - byte b = (byte)((value >>> (8*j)) & 0xff); + final byte b = (byte)((value >>> (8*j)) & 0xff); block[dstIdx++] = b; } } } - private static void writeAlphaBlock5(int alpha0, int alpha1, int[] indices, byte[] block) + private static void writeAlphaBlock5(final int alpha0, final int alpha1, final int[] indices, final byte[] block) { // check the relative values of the endpoints if (alpha0 > alpha1) { // swap the indices - int[] swapped = new int[16]; + final int[] swapped = new int[16]; for (int i = 0; i < 16; i++) { - int index = indices[i]; + final int index = indices[i]; if (index == 0) { swapped[i] = 1; } else if (index == 1) { @@ -1217,13 +1219,13 @@ private static void writeAlphaBlock5(int alpha0, int alpha1, int[] indices, byte } } - private static void writeAlphaBlock7(int alpha0, int alpha1, int[] indices, byte[] block) + private static void writeAlphaBlock7(final int alpha0, final int alpha1, final int[] indices, final byte[] block) { if (alpha0 < alpha1) { // swap the indices - int[] swapped = new int[16]; + final int[] swapped = new int[16]; for (int i = 0; i < 16; i++) { - int index = indices[i]; + final int index = indices[i]; if (index == 0) { swapped[i] = 1; } else if (index == 1) { @@ -1247,7 +1249,7 @@ private static final class SourceBlock { public final int start, end, error; - public SourceBlock(int start, int end, int error) + public SourceBlock(final int start, final int end, final int error) { this.start = start; this.end = end; @@ -1259,7 +1261,7 @@ private static final class SingleColorLookup { public final SourceBlock[] sources; - public SingleColorLookup(SourceBlock sb1, SourceBlock sb2) + public SingleColorLookup(final SourceBlock sb1, final SourceBlock sb2) { sources = new SourceBlock[]{sb1, sb2}; } @@ -2308,67 +2310,67 @@ public static class Vec3 { float vx, vy, vz; - public static Vec3 neg(Vec3 v) + public static Vec3 neg(final Vec3 v) { return new Vec3(-v.vx, -v.vy, -v.vz); } - public static Vec3 add(Vec3 v1, Vec3 v2) + public static Vec3 add(final Vec3 v1, final Vec3 v2) { return new Vec3(v1.vx + v2.vx, v1.vy + v2.vy, v1.vz + v2.vz); } - public static Vec3 sub(Vec3 v1, Vec3 v2) + public static Vec3 sub(final Vec3 v1, final Vec3 v2) { return new Vec3(v1.vx - v2.vx, v1.vy - v2.vy, v1.vz - v2.vz); } - public static Vec3 mul(Vec3 v1, Vec3 v2) + public static Vec3 mul(final Vec3 v1, final Vec3 v2) { return new Vec3(v1.vx * v2.vx, v1.vy * v2.vy, v1.vz * v2.vz); } - public static Vec3 mul(Vec3 v, float s) + public static Vec3 mul(final Vec3 v, final float s) { return new Vec3(v.vx * s, v.vy * s, v.vz * s); } - public static Vec3 div(Vec3 v1, Vec3 v2) + public static Vec3 div(final Vec3 v1, final Vec3 v2) { return new Vec3(v1.vx / v2.vx, v1.vy / v2.vy, v1.vz / v2.vz); } - public static Vec3 div(Vec3 v, float s) + public static Vec3 div(final Vec3 v, final float s) { - float t = 1.0f / s; + final float t = 1.0f / s; return new Vec3(v.vx * t, v.vy * t, v.vz * t); } - public static float dot(Vec3 v1, Vec3 v2) + public static float dot(final Vec3 v1, final Vec3 v2) { return v1.vx*v2.vx + v1.vy*v2.vy + v1.vz*v2.vz; } - public static Vec3 min(Vec3 v1, Vec3 v2) + public static Vec3 min(final Vec3 v1, final Vec3 v2) { v1.fixNaN(); v2.fixNaN(); return new Vec3(Math.min(v1.vx, v2.vx), Math.min(v1.vy, v2.vy), Math.min(v1.vz, v2.vz)); } - public static Vec3 max(Vec3 v1, Vec3 v2) + public static Vec3 max(final Vec3 v1, final Vec3 v2) { v1.fixNaN(); v2.fixNaN(); return new Vec3(Math.max(v1.vx, v2.vx), Math.max(v1.vy, v2.vy), Math.max(v1.vz, v2.vz)); } - public static Vec3 truncate(Vec3 v) + public static Vec3 truncate(final Vec3 v) { return new Vec3((v.vx > 0.0f) ? (float)Math.floor(v.vx) : (float)Math.ceil(v.vx), (v.vy > 0.0f) ? (float)Math.floor(v.vy) : (float)Math.ceil(v.vy), (v.vz > 0.0f) ? (float)Math.floor(v.vz) : (float)Math.ceil(v.vz)); } - public static float lengthSquared(Vec3 v) + public static float lengthSquared(final Vec3 v) { return v.vx*v.vx + v.vy*v.vy + v.vz*v.vz; } @@ -2378,16 +2380,26 @@ public Vec3() vx = 0.0f; vy = 0.0f; vz = 0.0f; } - public Vec3(float s) + public Vec3(final float s) { vx = s; vy = s; vz = s; } - public Vec3(float x, float y, float z) + public Vec3(final float x, final float y, final float z) { vx = x; vy = y; vz = z; } + @Override + public int hashCode() + { + int hash = 7; + hash = 31 * hash + Float.hashCode(vx); + hash = 31 * hash + Float.hashCode(vy); + hash = 31 * hash + Float.hashCode(vz); + return hash; + } + @Override public boolean equals(Object o) { @@ -2416,7 +2428,7 @@ public Object clone() public float y() { return vy; } public float z() { return vz; } - public Vec3 add(Vec3 v) + public Vec3 add(final Vec3 v) { vx += v.vx; vy += v.vy; @@ -2424,7 +2436,7 @@ public Vec3 add(Vec3 v) return this; } - public Vec3 sub(Vec3 v) + public Vec3 sub(final Vec3 v) { vx -= v.vx; vy -= v.vy; @@ -2432,7 +2444,7 @@ public Vec3 sub(Vec3 v) return this; } - public Vec3 mul(Vec3 v) + public Vec3 mul(final Vec3 v) { vx *= v.vx; vy *= v.vy; @@ -2440,7 +2452,7 @@ public Vec3 mul(Vec3 v) return this; } - public Vec3 mul(float s) + public Vec3 mul(final float s) { vx *= s; vy *= s; @@ -2448,7 +2460,7 @@ public Vec3 mul(float s) return this; } - public Vec3 div(Vec3 v) + public Vec3 div(final Vec3 v) { vx /= v.vx; vy /= v.vy; @@ -2456,16 +2468,16 @@ public Vec3 div(Vec3 v) return this; } - public Vec3 div(float s) + public Vec3 div(final float s) { - float t = 1.0f / s; + final float t = 1.0f / s; vx *= t; vy *= t; vz *= t; return this; } - public float dot(Vec3 v) + public float dot(final Vec3 v) { return vx*v.vx + vy*v.vy + vz*v.vz; } @@ -2490,37 +2502,37 @@ public static class Vec4 // return new Vec4(-v.vx, -v.vy, -v.vz, -v.vw); // } - public static Vec4 add(Vec4 v1, Vec4 v2) + public static Vec4 add(final Vec4 v1, final Vec4 v2) { return new Vec4(v1.vx + v2.vx, v1.vy + v2.vy, v1.vz + v2.vz, v1.vw + v2.vw); } - public static Vec4 sub(Vec4 v1, Vec4 v2) + public static Vec4 sub(final Vec4 v1, final Vec4 v2) { return new Vec4(v1.vx - v2.vx, v1.vy - v2.vy, v1.vz - v2.vz, v1.vw - v2.vw); } - public static Vec4 mul(Vec4 v1, Vec4 v2) + public static Vec4 mul(final Vec4 v1, final Vec4 v2) { return new Vec4(v1.vx * v2.vx, v1.vy * v2.vy, v1.vz * v2.vz, v1.vw * v2.vw); } - public static Vec4 multiplyAdd(Vec4 v1, Vec4 v2, Vec4 v3) + public static Vec4 multiplyAdd(final Vec4 v1, final Vec4 v2, final Vec4 v3) { return new Vec4(v1.vx*v2.vx + v3.vx, v1.vy*v2.vy + v3.vy, v1.vz*v2.vz + v3.vz, v1.vw*v2.vw + v3.vw); } - public static Vec4 negMulSub(Vec4 v1, Vec4 v2, Vec4 v3) + public static Vec4 negMulSub(final Vec4 v1, final Vec4 v2, final Vec4 v3) { return new Vec4(v3.vx - v1.vx*v2.vx, v3.vy - v1.vy*v2.vy, v3.vz - v1.vz*v2.vz, v3.vw - v1.vw*v2.vw); } - public static Vec4 reciprocal(Vec4 v) + public static Vec4 reciprocal(final Vec4 v) { return new Vec4(1.0f / v.vx, 1.0f / v.vy, 1.0f / v.vz, 1.0f / v.vw); } - public static Vec4 min(Vec4 v1, Vec4 v2) + public static Vec4 min(final Vec4 v1, final Vec4 v2) { v1.fixNaN(); v2.fixNaN(); return new Vec4(Math.min(v1.vx, v2.vx), @@ -2530,7 +2542,7 @@ public static Vec4 min(Vec4 v1, Vec4 v2) } - public static Vec4 max(Vec4 v1, Vec4 v2) + public static Vec4 max(final Vec4 v1, final Vec4 v2) { v1.fixNaN(); v2.fixNaN(); return new Vec4(Math.max(v1.vx, v2.vx), @@ -2539,7 +2551,7 @@ public static Vec4 max(Vec4 v1, Vec4 v2) Math.max(v1.vw, v2.vw)); } - public static Vec4 truncate(Vec4 v) + public static Vec4 truncate(final Vec4 v) { return new Vec4((v.vx > 0.0f) ? (float)Math.floor(v.vx) : (float)Math.ceil(v.vx), (v.vy > 0.0f) ? (float)Math.floor(v.vy) : (float)Math.ceil(v.vy), @@ -2547,7 +2559,7 @@ public static Vec4 truncate(Vec4 v) (v.vw > 0.0f) ? (float)Math.floor(v.vw) : (float)Math.ceil(v.vw)); } - public static boolean compareAnyLessThan(Vec4 v1, Vec4 v2) + public static boolean compareAnyLessThan(final Vec4 v1, final Vec4 v2) { return (v1.vx < v2.vx || v1.vy < v2.vy || v1.vz < v2.vz || v1.vw < v2.vw); } @@ -2557,16 +2569,27 @@ public Vec4() vx = 0.0f; vy = 0.0f; vz = 0.0f; vw = 0.0f; } - public Vec4(float s) + public Vec4(final float s) { vx = s; vy = s; vz = s; vw = s; } - public Vec4(float x, float y, float z, float w) + public Vec4(final float x, final float y, final float z, final float w) { vx = x; vy = y; vz = z; vw = w; } + @Override + public int hashCode() + { + int hash = 7; + hash = 31 * hash + Float.hashCode(vx); + hash = 31 * hash + Float.hashCode(vy); + hash = 31 * hash + Float.hashCode(vz); + hash = 31 * hash + Float.hashCode(vw); + return hash; + } + @Override public boolean equals(Object o) { @@ -2603,7 +2626,7 @@ public Object clone() public float z() { return vz; } public float w() { return vw; } - public Vec4 add(Vec4 v) + public Vec4 add(final Vec4 v) { vx += v.vx; vy += v.vy; @@ -2612,7 +2635,7 @@ public Vec4 add(Vec4 v) return this; } - public Vec4 sub(Vec4 v) + public Vec4 sub(final Vec4 v) { vx -= v.vx; vy -= v.vy; @@ -2621,7 +2644,7 @@ public Vec4 sub(Vec4 v) return this; } - public Vec4 mul(Vec4 v) + public Vec4 mul(final Vec4 v) { vx *= v.vx; vy *= v.vy; @@ -2648,11 +2671,11 @@ public static class Sym3x3 private final float[] m; - public static Sym3x3 computeWeightedCovariance(int count, Vec3[] points, float[] weights) + public static Sym3x3 computeWeightedCovariance(final int count, final Vec3[] points, final float[] weights) { // computing the centroid float total = 0.0f; - Vec3 centroid = new Vec3(); + final Vec3 centroid = new Vec3(); for (int i = 0; i < count; i++) { total += weights[i]; centroid.add(Vec3.mul(points[i], weights[i])); @@ -2660,10 +2683,10 @@ public static Sym3x3 computeWeightedCovariance(int count, Vec3[] points, float[] centroid.div(total); // accumulating the covariance matrix - Sym3x3 covariance = new Sym3x3(0.0f); + final Sym3x3 covariance = new Sym3x3(0.0f); for (int i = 0; i < count; i++) { - Vec3 a = Vec3.sub(points[i], centroid); - Vec3 b = Vec3.mul(a, weights[i]); + final Vec3 a = Vec3.sub(points[i], centroid); + final Vec3 b = Vec3.mul(a, weights[i]); covariance.m[0] += a.x()*b.x(); covariance.m[1] += a.x()*b.y(); @@ -2676,28 +2699,28 @@ public static Sym3x3 computeWeightedCovariance(int count, Vec3[] points, float[] return covariance; } - public static Vec3 computePrincipleComponent(Sym3x3 matrix) + public static Vec3 computePrincipleComponent(final Sym3x3 matrix) { // computing the cubic coefficients - float c0 = matrix.m[0] * matrix.m[3] * matrix.m[5] + - 2.0f * matrix.m[1] * matrix.m[2] * matrix.m[4] - - matrix.m[0] * matrix.m[4] * matrix.m[4] - - matrix.m[3] * matrix.m[2] * matrix.m[2] - - matrix.m[5] * matrix.m[1] * matrix.m[1]; - float c1 = matrix.m[0] * matrix.m[3] + - matrix.m[0] * matrix.m[5] + - matrix.m[3] * matrix.m[5] - - matrix.m[1] * matrix.m[1] - - matrix.m[2] * matrix.m[2] - - matrix.m[4] * matrix.m[4]; - float c2 = matrix.m[0] + matrix.m[3] + matrix.m[5]; + final float c0 = matrix.m[0] * matrix.m[3] * matrix.m[5] + + 2.0f * matrix.m[1] * matrix.m[2] * matrix.m[4] - + matrix.m[0] * matrix.m[4] * matrix.m[4] - + matrix.m[3] * matrix.m[2] * matrix.m[2] - + matrix.m[5] * matrix.m[1] * matrix.m[1]; + final float c1 = matrix.m[0] * matrix.m[3] + + matrix.m[0] * matrix.m[5] + + matrix.m[3] * matrix.m[5] - + matrix.m[1] * matrix.m[1] - + matrix.m[2] * matrix.m[2] - + matrix.m[4] * matrix.m[4]; + final float c2 = matrix.m[0] + matrix.m[3] + matrix.m[5]; // computing the quadratic coefficients - float a = c1 - (1.0f/3.0f)*c2*c2; - float b = (-2.0f/27.0f)*c2*c2*c2 + (1.0f/3.0f)*c1*c2 - c0; + final float a = c1 - (1.0f/3.0f)*c2*c2; + final float b = (-2.0f/27.0f)*c2*c2*c2 + (1.0f/3.0f)*c1*c2 - c0; // computing the root count check - float Q = 0.25f*b*b + (1.0f/27.0f)*a*a*a; + final float Q = 0.25f*b*b + (1.0f/27.0f)*a*a*a; // testing the multiplicity if (FLT_EPSILON < Q) { @@ -2705,12 +2728,12 @@ public static Vec3 computePrincipleComponent(Sym3x3 matrix) return new Vec3(1.0f); } else if (Q < -FLT_EPSILON) { // three distinct roots - double theta = Math.atan2(Math.sqrt(-Q), -0.5*b); - double rho = Math.sqrt(0.25*b*b - Q); + final double theta = Math.atan2(Math.sqrt(-Q), -0.5*b); + final double rho = Math.sqrt(0.25*b*b - Q); - float rt = (float)Math.pow(rho, 1.0/3.0); - float ct = (float)Math.cos(theta/3.0); - float st = (float)Math.sin(theta/3.0); + final float rt = (float)Math.pow(rho, 1.0/3.0); + final float ct = (float)Math.cos(theta/3.0); + final float st = (float)Math.sin(theta/3.0); float l1 = (1.0f / 3.0f)*c2 + 2.0f*rt*ct; float l2 = (1.0f / 3.0f)*c2 - rt*(ct + (float)Math.sqrt(3.0)*st); @@ -2726,10 +2749,10 @@ public static Vec3 computePrincipleComponent(Sym3x3 matrix) return getMultiplicity1Evector(matrix, l1); } else { // if (-FLT_EPSILON <= Q && Q <= FLT_EPSILON) // two roots - float rt = (float)((b < 0.0f) ? -Math.pow(-0.5*b, 1.0/3.0) : Math.pow(0.5*b, 1.0/3.0)); + final float rt = (float)((b < 0.0f) ? -Math.pow(-0.5*b, 1.0/3.0) : Math.pow(0.5*b, 1.0/3.0)); - float l1 = (1.0f/3.0f)*c2 + rt; // repeated - float l2 = (1.0f/3.0f)*c2 - 2.0f*rt; + final float l1 = (1.0f/3.0f)*c2 + rt; // repeated + final float l2 = (1.0f/3.0f)*c2 - 2.0f*rt; // getting the eigenvector if (Math.abs(l1) > Math.abs(l2)) { @@ -2740,13 +2763,13 @@ public static Vec3 computePrincipleComponent(Sym3x3 matrix) } } - private static Vec3 getMultiplicity1Evector(Sym3x3 matrix, float evalue) + private static Vec3 getMultiplicity1Evector(final Sym3x3 matrix, final float evalue) { if (matrix == null) throw new NullPointerException(); // computing M - Sym3x3 m = new Sym3x3(); + final Sym3x3 m = new Sym3x3(); m.m[0] = matrix.m[0] - evalue; m.m[1] = matrix.m[1]; m.m[2] = matrix.m[2]; @@ -2755,7 +2778,7 @@ private static Vec3 getMultiplicity1Evector(Sym3x3 matrix, float evalue) m.m[5] = matrix.m[5] - evalue; // computing U - Sym3x3 u = new Sym3x3(); + final Sym3x3 u = new Sym3x3(); u.m[0] = m.m[3]*m.m[5] - m.m[4]*m.m[4]; u.m[1] = m.m[2]*m.m[4] - m.m[1]*m.m[5]; u.m[2] = m.m[1]*m.m[4] - m.m[2]*m.m[3]; @@ -2786,13 +2809,13 @@ private static Vec3 getMultiplicity1Evector(Sym3x3 matrix, float evalue) } } - private static Vec3 getMultiplicity2Evector(Sym3x3 matrix, float evalue) + private static Vec3 getMultiplicity2Evector(final Sym3x3 matrix, final float evalue) { if (matrix == null) throw new NullPointerException(); // computing M - Sym3x3 m = new Sym3x3(); + final Sym3x3 m = new Sym3x3(); m.m[0] = matrix.m[0] - evalue; m.m[1] = matrix.m[1]; m.m[2] = matrix.m[2]; @@ -2804,7 +2827,7 @@ private static Vec3 getMultiplicity2Evector(Sym3x3 matrix, float evalue) float mc = Math.abs(m.m[0]); int mi = 0; for (int i = 1; i < 6; i++) { - float c = Math.abs(m.m[i]); + final float c = Math.abs(m.m[i]); if (c > mc) { mc = c; mi = i; @@ -2834,7 +2857,7 @@ public Sym3x3() } } - public Sym3x3(float s) + public Sym3x3(final float s) { m = new float[6]; for (int i = 0; i < m.length; i++) { @@ -2851,17 +2874,17 @@ public String toString() @Override public Object clone() { - Sym3x3 s = new Sym3x3(); + final Sym3x3 s = new Sym3x3(); s.m[0] = m[0]; s.m[1] = m[1]; s.m[2] = m[2]; s.m[3] = m[3]; s.m[4] = m[4]; s.m[5] = m[5]; return s; } - public float get(int idx) + public float get(final int idx) { return m[idx]; } - public Sym3x3 set(int idx, float s) + public Sym3x3 set(final int idx, final float s) { m[idx] = s; return this; @@ -2870,7 +2893,7 @@ public Sym3x3 set(int idx, float s) private static final class Misc { - public static int floatToInt(float a, int limit) + public static int floatToInt(final float a, final int limit) { // use ANSI round-to-zero behavior to get round-to-nearest int i = (int)(a + 0.5f); diff --git a/src/org/infinity/resource/graphics/GifSequenceReader.java b/src/org/infinity/resource/graphics/GifSequenceReader.java index 4d5809096..bb3dca100 100644 --- a/src/org/infinity/resource/graphics/GifSequenceReader.java +++ b/src/org/infinity/resource/graphics/GifSequenceReader.java @@ -200,7 +200,7 @@ private Frame initFrame(int index, IIOImage ioimg) throws Exception } // Updates the specified Frame object with metadata - private Frame updateMetadata(Frame frame, IIOMetadata meta) throws IOException + private Frame updateMetadata(Frame frame, IIOMetadata meta) { if (frame != null && meta != null) { Node tree = meta.getAsTree(metaFormatName); diff --git a/src/org/infinity/resource/graphics/GifSequenceWriter.java b/src/org/infinity/resource/graphics/GifSequenceWriter.java index 70955e93b..92dd244df 100644 --- a/src/org/infinity/resource/graphics/GifSequenceWriter.java +++ b/src/org/infinity/resource/graphics/GifSequenceWriter.java @@ -99,6 +99,7 @@ public void writeToSequence(RenderedImage img) throws IOException * Close this GifSequenceWriter object. This does not close the underlying * stream, just finishes off the GIF. */ + @Override public void close() throws IOException { gifWriter.endWriteSequence(); diff --git a/src/org/infinity/resource/graphics/GraphicsResource.java b/src/org/infinity/resource/graphics/GraphicsResource.java index ca5c46522..1195eb1d3 100644 --- a/src/org/infinity/resource/graphics/GraphicsResource.java +++ b/src/org/infinity/resource/graphics/GraphicsResource.java @@ -9,6 +9,7 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.image.BufferedImage; +import java.io.InputStream; import java.nio.ByteBuffer; import javax.imageio.ImageIO; @@ -160,8 +161,8 @@ private void init() throws Exception } } if (image == null) { - try { - image = ImageIO.read(entry.getResourceDataAsStream()); + try (InputStream is = entry.getResourceDataAsStream()) { + image = ImageIO.read(is); } catch (Exception e) { image = null; throw new Exception("Unsupported graphics format"); diff --git a/src/org/infinity/resource/graphics/MosDecoder.java b/src/org/infinity/resource/graphics/MosDecoder.java index 930681d06..7c09907ca 100644 --- a/src/org/infinity/resource/graphics/MosDecoder.java +++ b/src/org/infinity/resource/graphics/MosDecoder.java @@ -39,20 +39,17 @@ public static Type getType(ResourceEntry mosEntry) { Type retVal = Type.INVALID; if (mosEntry != null) { - try { - InputStream is = mosEntry.getResourceDataAsStream(); - if (is != null) { - String signature = StreamUtils.readString(is, 4); - String version = StreamUtils.readString(is, 4); - is.close(); - if ("MOSC".equals(signature)) { - retVal = Type.MOSC; - } else if ("MOS ".equals(signature)) { - if ("V1 ".equals(version)) { - retVal = Type.MOSV1; - } else if ("V2 ".equals(version)) { - retVal = Type.MOSV2; - } + try (InputStream is = mosEntry.getResourceDataAsStream()) { + String signature = StreamUtils.readString(is, 4); + String version = StreamUtils.readString(is, 4); + is.close(); + if ("MOSC".equals(signature)) { + retVal = Type.MOSC; + } else if ("MOS ".equals(signature)) { + if ("V1 ".equals(version)) { + retVal = Type.MOSV1; + } else if ("V2 ".equals(version)) { + retVal = Type.MOSV2; } } } catch (Exception e) { diff --git a/src/org/infinity/resource/graphics/MosResource.java b/src/org/infinity/resource/graphics/MosResource.java index e49d9477b..a36cd5c24 100644 --- a/src/org/infinity/resource/graphics/MosResource.java +++ b/src/org/infinity/resource/graphics/MosResource.java @@ -281,7 +281,7 @@ public JComponent makeViewer(ViewableContainer container) } } } - List list = new ArrayList(); + List list = new ArrayList<>(); if (miExport != null) list.add(miExport); if (miExportMOSV1 != null) @@ -478,7 +478,7 @@ private byte[] convertToMosV1(boolean compressed) throws Exception progress.setMillisToPopup(2000); // creating list of tiles as int[] arrays - List tileList = new ArrayList(cols*rows); + List tileList = new ArrayList<>(cols*rows); for (int y = 0; y < rows; y++) { for (int x = 0; x < cols; x++) { int tileX = x * 64; @@ -497,7 +497,7 @@ private byte[] convertToMosV1(boolean compressed) throws Exception byte[] tilePalette = new byte[1024]; byte[] tileData = new byte[64*64]; int curPalOfs = palOfs, curTableOfs = tableOfs, curDataOfs = dataOfs; - IntegerHashMap colorCache = new IntegerHashMap(1536); // caching RGBColor -> index + IntegerHashMap colorCache = new IntegerHashMap<>(1536); // caching RGBColor -> index for (int tileIdx = 0; tileIdx < tileList.size(); tileIdx++) { colorCache.clear(); if (progress.isCanceled()) { @@ -531,7 +531,7 @@ private byte[] convertToMosV1(boolean compressed) throws Exception if (palIndex != null) { tileData[i] = (byte)(palIndex + 1); } else { - byte color = (byte)ColorConvert.nearestColorRGB(pixels[i], palette, true); + byte color = (byte)ColorConvert.getNearestColor(pixels[i], palette, 0.0, null); tileData[i] = (byte)(color + 1); colorCache.put(pixels[i], color); } @@ -571,7 +571,7 @@ private void startConversion(boolean compressed) @Override public List doInBackground() { - List list = new Vector(1); + List list = new Vector<>(1); try { byte[] buf = convertToMosV1(exportCompressed); if (buf != null) { @@ -592,44 +592,37 @@ public boolean containsPvrzReference(int index) { boolean retVal = false; if (index >= 0 && index <= 99999) { - try { - InputStream is = entry.getResourceDataAsStream(); - if (is != null) { - try { - // parsing resource header - byte[] sig = new byte[8]; - byte[] buf = new byte[16]; - long len; - long curOfs = 0; - if ((len = is.read(sig)) != sig.length) throw new Exception(); - if (!"MOS V2 ".equals(DynamicArray.getString(sig, 0, 8))) throw new Exception(); - curOfs += len; - if ((len = is.read(buf)) != buf.length) throw new Exception(); - curOfs += len; - int numBlocks = DynamicArray.getInt(buf, 8); - int ofsBlocks = DynamicArray.getInt(buf, 12); - curOfs = ofsBlocks - curOfs; - if (curOfs > 0) { - do { - len = is.skip(curOfs); - if (len <= 0) throw new Exception(); - curOfs -= len; - } while (curOfs > 0); - } + try (InputStream is = entry.getResourceDataAsStream()) { + // parsing resource header + byte[] sig = new byte[8]; + byte[] buf = new byte[16]; + long len; + long curOfs = 0; + if ((len = is.read(sig)) != sig.length) throw new Exception(); + if (!"MOS V2 ".equals(DynamicArray.getString(sig, 0, 8))) throw new Exception(); + curOfs += len; + if ((len = is.read(buf)) != buf.length) throw new Exception(); + curOfs += len; + int numBlocks = DynamicArray.getInt(buf, 8); + int ofsBlocks = DynamicArray.getInt(buf, 12); + curOfs = ofsBlocks - curOfs; + if (curOfs > 0) { + do { + len = is.skip(curOfs); + if (len <= 0) throw new Exception(); + curOfs -= len; + } while (curOfs > 0); + } - // parsing blocks - buf = new byte[28]; - for (int i = 0; i < numBlocks && !retVal; i++) { - if (is.read(buf) != buf.length) throw new Exception(); - int curIndex = DynamicArray.getInt(buf, 0); - retVal = (curIndex == index); - } - } finally { - is.close(); - is = null; - } + // parsing blocks + buf = new byte[28]; + for (int i = 0; i < numBlocks && !retVal; i++) { + if (is.read(buf) != buf.length) throw new Exception(); + int curIndex = DynamicArray.getInt(buf, 0); + retVal = (curIndex == index); } } catch (Exception e) { + e.printStackTrace(); } } return retVal; diff --git a/src/org/infinity/resource/graphics/PltResource.java b/src/org/infinity/resource/graphics/PltResource.java index 57ed6813d..0e249d1e4 100644 --- a/src/org/infinity/resource/graphics/PltResource.java +++ b/src/org/infinity/resource/graphics/PltResource.java @@ -19,6 +19,7 @@ import java.awt.image.DataBufferInt; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.InputStream; import java.io.OutputStream; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -650,8 +651,8 @@ private void init() { ResourceEntry entry = ResourceFactory.getResourceEntry(PAL_RESOURCE); if (entry != null) { - try { - BufferedImage image = ImageIO.read(entry.getResourceDataAsStream()); + try (InputStream is = entry.getResourceDataAsStream()) { + BufferedImage image = ImageIO.read(is); // preparing fixed color ranges int[] buffer = null; diff --git a/src/org/infinity/resource/graphics/PseudoBamDecoder.java b/src/org/infinity/resource/graphics/PseudoBamDecoder.java index 23cd43e6f..fc8919377 100644 --- a/src/org/infinity/resource/graphics/PseudoBamDecoder.java +++ b/src/org/infinity/resource/graphics/PseudoBamDecoder.java @@ -7,7 +7,6 @@ import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Dimension; -import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Point; @@ -32,9 +31,9 @@ import org.infinity.resource.Profile; import org.infinity.util.BinPack2D; import org.infinity.util.DynamicArray; -import org.infinity.util.Pair; import org.infinity.util.io.FileManager; import org.infinity.util.io.StreamUtils; +import org.infinity.util.tuples.Couple; /** * A decoder that takes individual images as input and simulates a BAM structure. @@ -58,9 +57,9 @@ public class PseudoBamDecoder extends BamDecoder private static final Color TransparentColor = new Color(0, true); private final PseudoBamFrameEntry defaultFrameInfo = new PseudoBamFrameEntry(null, 0, 0); - private final HashMap mapOptions = new HashMap(); + private final HashMap mapOptions = new HashMap<>(); - private List listCycles = new ArrayList(); + private List listCycles = new ArrayList<>(); private List listFrames; private PseudoBamControl defaultControl; @@ -164,7 +163,7 @@ public void setFramesList(List framesList) if (framesList != null) { listFrames = framesList; } else { - listFrames = new ArrayList(); + listFrames = new ArrayList<>(); } } @@ -186,7 +185,7 @@ public void setCyclesList(List cyclesList) if (cyclesList != null) { listCycles = cyclesList; } else { - listCycles = new ArrayList(); + listCycles = new ArrayList<>(); } } @@ -194,49 +193,54 @@ public void setCyclesList(List cyclesList) /** * Adds a new frame to the end of the frame list. Center position defaults to (0, 0). * @param image The image to add. + * @return The index of the added frame or -1 if frame could not be added. */ - public void frameAdd(BufferedImage image) + public int frameAdd(BufferedImage image) { - frameInsert(listFrames.size(), new BufferedImage[]{image}, new Point[0]); + return frameInsert(listFrames.size(), new BufferedImage[]{image}, new Point[0]); } /** * Adds a new frame to the end of the frame list. * @param image The image to add. * @param center The center position of the image. + * @return The index of the added frame or -1 if frame could not be added. */ - public void frameAdd(BufferedImage image, Point center) + public int frameAdd(BufferedImage image, Point center) { - frameInsert(listFrames.size(), new BufferedImage[]{image}, new Point[]{center}); + return frameInsert(listFrames.size(), new BufferedImage[]{image}, new Point[]{center}); } /** * Adds the list of frames to the end of the frame list. Center positions default to (0, 0). * @param images An array containing the images to add. + * @return The index of the first added frame or -1 if frames could not be added. */ - public void frameAdd(BufferedImage[] images) + public int frameAdd(BufferedImage[] images) { - frameInsert(listFrames.size(), images, new Point[0]); + return frameInsert(listFrames.size(), images, new Point[0]); } /** * Adds the list of frames to the end of the frame list. * @param images An array containing the images to add. * @param centers An array of center positions corresponding with the images. + * @return The index of the first added frame or -1 if frames could not be added. */ - public void frameAdd(BufferedImage[] images, Point[] centers) + public int frameAdd(BufferedImage[] images, Point[] centers) { - frameInsert(listFrames.size(), images, centers); + return frameInsert(listFrames.size(), images, centers); } /** * Inserts a frame at the specified position. Center position defaults to (0, 0). * @param frameIdx The position for the frame to insert. * @param image The image to insert. + * @return The index of the inserted frame or -1 if frame could not be inserted. */ - public void frameInsert(int frameIdx, BufferedImage image) + public int frameInsert(int frameIdx, BufferedImage image) { - frameInsert(frameIdx, new BufferedImage[]{image}, new Point[0]); + return frameInsert(frameIdx, new BufferedImage[]{image}, new Point[0]); } /** @@ -244,20 +248,22 @@ public void frameInsert(int frameIdx, BufferedImage image) * @param frameIdx The position for the frame to insert. * @param image The image to insert. * @param center The center position of the image. + * @return The index of the inserted frame or -1 if frame could not be inserted. */ - public void frameInsert(int frameIdx, BufferedImage image, Point center) + public int frameInsert(int frameIdx, BufferedImage image, Point center) { - frameInsert(frameIdx, new BufferedImage[]{image}, new Point[]{center}); + return frameInsert(frameIdx, new BufferedImage[]{image}, new Point[]{center}); } /** * Inserts an array of frames at the specified position. Center positions default to (0, 0). * @param frameIdx The position for the frames to insert. * @param images An array containing the images to insert. + * @return The index of the first inserted frame or -1 if frames could not be inserted. */ - public void frameInsert(int frameIdx, BufferedImage[] images) + public int frameInsert(int frameIdx, BufferedImage[] images) { - frameInsert(frameIdx, images, new Point[0]); + return frameInsert(frameIdx, images, new Point[0]); } /** @@ -265,8 +271,9 @@ public void frameInsert(int frameIdx, BufferedImage[] images) * @param frameIdx The position for the frames to insert. * @param images An array containing the images to insert. * @param centers An array of center positions corresponding with the images. + * @return The index of the first inserted frame or -1 if frames could not be inserted. */ - public void frameInsert(int frameIdx, BufferedImage[] images, Point[] centers) + public int frameInsert(int frameIdx, BufferedImage[] images, Point[] centers) { if (frameIdx >= 0 && frameIdx <= listFrames.size() && images != null) { for (int i = 0; i < images.length; i++) { @@ -277,7 +284,9 @@ public void frameInsert(int frameIdx, BufferedImage[] images, Point[] centers) } listFrames.add(frameIdx+i, new PseudoBamFrameEntry(images[i], x, y)); } + return frameIdx; } + return -1; } /** @@ -524,13 +533,13 @@ private void renderFrame(BamControl control, int frameIdx, Image canvas) int dstWidth = dstImage.getWidth(); int dstHeight = dstImage.getHeight(); - int srcWidth = listFrames.get(frameIdx).width; - int srcHeight = listFrames.get(frameIdx).height; + int srcWidth = listFrames.get(frameIdx).getWidth(); + int srcHeight = listFrames.get(frameIdx).getHeight(); if (control.getMode() == BamControl.Mode.SHARED) { // drawing on shared canvas Rectangle shared = control.getSharedRectangle(); - int srcCenterX = listFrames.get(frameIdx).centerX; - int srcCenterY = listFrames.get(frameIdx).centerY; + int srcCenterX = listFrames.get(frameIdx).getCenterX(); + int srcCenterY = listFrames.get(frameIdx).getCenterY(); int left = -shared.x - srcCenterX; int top = -shared.y - srcCenterY; int maxWidth = (dstWidth < srcWidth + left) ? dstWidth : srcWidth; @@ -579,8 +588,11 @@ private void renderFrame(BamControl control, int frameIdx, Image canvas) // rendering resulting image onto the canvas if needed if (dstImage != canvas) { - Graphics g = canvas.getGraphics(); + Graphics2D g = (Graphics2D)canvas.getGraphics(); try { + if (getComposite() != null) { + g.setComposite(getComposite()); + } g.drawImage(dstImage, 0, 0, null); } finally { g.dispose(); @@ -617,7 +629,7 @@ public static boolean isRleColor(int color, int rleColor, int threshold) */ public static boolean isTransparentColor(int color, int threshold) { - if (threshold < 0) return false; + if (threshold < 0) return (color & 0xff000000) == 0; final int Green = 0x0000ff00; if (threshold < 0) threshold = 0; else if (threshold > 255) threshold = 255; boolean isAlpha = (((color >>> 24) & 0xff) < (255 - threshold)); @@ -670,9 +682,9 @@ public boolean exportBamV1(Path fileName, ProgressMonitor progress, int curProgr } // checking frame properties - if (entry.width <= 0 || entry.width > 65535 || entry.height <= 0 || entry.height > 65535 || - entry.centerX < Short.MIN_VALUE || entry.centerX > Short.MAX_VALUE || - entry.centerY < Short.MIN_VALUE || entry.centerY > Short.MAX_VALUE) { + if (entry.getWidth() <= 0 || entry.getWidth() > 65535 || entry.getHeight() <= 0 || entry.getHeight() > 65535 || + entry.getCenterX() < Short.MIN_VALUE || entry.getCenterX() > Short.MAX_VALUE || + entry.getCenterY() < Short.MIN_VALUE || entry.getCenterY() > Short.MAX_VALUE) { throw new Exception("Dimensions are out of range for frame index " + i); } @@ -714,7 +726,7 @@ public boolean exportBamV1(Path fileName, ProgressMonitor progress, int curProgr control.setSharedPerCycle(false); Dimension dimFrame = control.calculateSharedCanvas(false).getSize(); int maxImageSize = (dimFrame.width*dimFrame.height*3) / 2; // about 1.5x of max. size - List listFrameData = new ArrayList(listFrames.size()); + List listFrameData = new ArrayList<>(listFrames.size()); // encoding frames Object o = getOption(OPTION_INT_RLEINDEX); @@ -739,7 +751,7 @@ public boolean exportBamV1(Path fileName, ProgressMonitor progress, int curProgr cnt++; srcIdx++; } - dstData[dstIdx++] = (byte)rleIndex; + dstData[dstIdx++] = rleIndex; dstData[dstIdx++] = (byte)cnt; } else { // uncompressed pixels @@ -767,7 +779,7 @@ public boolean exportBamV1(Path fileName, ProgressMonitor progress, int curProgr } // creating cycles table and frame lookup table - List listFrameLookup = new ArrayList(); + List listFrameLookup = new ArrayList<>(); int lookupSize = 0; for (int i = 0; i < listCycles.size(); i++) { listFrameLookup.add(Integer.valueOf(lookupSize)); @@ -795,7 +807,7 @@ public boolean exportBamV1(Path fileName, ProgressMonitor progress, int curProgr System.arraycopy("BAM V1 ".getBytes(), 0, bamData, 0, 8); DynamicArray.putShort(bamData, 0x08, (short)listFrames.size()); DynamicArray.putByte(bamData, 0x0a, (byte)listCycles.size()); - DynamicArray.putByte(bamData, 0x0b, (byte)rleIndex); + DynamicArray.putByte(bamData, 0x0b, rleIndex); DynamicArray.putInt(bamData, 0x0c, ofsFrameEntries); DynamicArray.putInt(bamData, 0x10, ofsPalette); DynamicArray.putInt(bamData, 0x14, ofsLookup); @@ -803,10 +815,10 @@ public boolean exportBamV1(Path fileName, ProgressMonitor progress, int curProgr // adding frame entries int curOfs = ofsFrameEntries; for (int i = 0; i < listFrames.size(); i++) { - DynamicArray.putShort(bamData, curOfs, (short)listFrames.get(i).width); - DynamicArray.putShort(bamData, curOfs + 2, (short)listFrames.get(i).height); - DynamicArray.putShort(bamData, curOfs + 4, (short)listFrames.get(i).centerX); - DynamicArray.putShort(bamData, curOfs + 6, (short)listFrames.get(i).centerY); + DynamicArray.putShort(bamData, curOfs, (short)listFrames.get(i).getWidth()); + DynamicArray.putShort(bamData, curOfs + 2, (short)listFrames.get(i).getHeight()); + DynamicArray.putShort(bamData, curOfs + 4, (short)listFrames.get(i).getCenterX()); + DynamicArray.putShort(bamData, curOfs + 6, (short)listFrames.get(i).getCenterY()); DynamicArray.putInt(bamData, curOfs + 8, frameDataOffsets[i]); curOfs += FrameEntrySize; } @@ -896,8 +908,8 @@ public boolean exportBamV2(Path fileName, DxtEncoder.DxtType dxtType, int pvrzIn // preparing output path for PVRZ files Path pvrzFilePath = fileName.toAbsolutePath().getParent(); - List listFrameData = new ArrayList(listFrames.size()); - List listGrid = new ArrayList(); + List listFrameData = new ArrayList<>(listFrames.size()); + List listGrid = new ArrayList<>(); // initializing progress monitor if (progress != null) { @@ -914,17 +926,17 @@ public boolean exportBamV2(Path fileName, DxtEncoder.DxtType dxtType, int pvrzIn } // generating remaining info blocks - List listFrameDataBlocks = new ArrayList(); - List listFrameEntries = new ArrayList(); - List> listCycleData = new ArrayList>(listCycles.size()); + List listFrameDataBlocks = new ArrayList<>(); + List listFrameEntries = new ArrayList<>(); + List> listCycleData = new ArrayList<>(listCycles.size()); int frameStartIndex = 0; // keeps track of current start index of frame entries int blockStartIndex = 0; // keeps track of current start index of frame data blocks for (int i = 0; i < listCycles.size(); i++) { List cycleFrames = listCycles.get(i).frames; // generating cycle entries - Pair cycle = new Pair(Short.valueOf((short)cycleFrames.size()), - Short.valueOf((short)frameStartIndex)); + Couple cycle = Couple.with(Short.valueOf((short)cycleFrames.size()), + Short.valueOf((short)frameStartIndex)); listCycleData.add(cycle); for (int j = 0; j < cycleFrames.size(); j++) { @@ -933,7 +945,7 @@ public boolean exportBamV2(Path fileName, DxtEncoder.DxtType dxtType, int pvrzIn FrameDataV2 frame = listFrameData.get(idx); PseudoBamFrameEntry bfe = listFrames.get(idx); - PseudoBamFrameEntry entry = new PseudoBamFrameEntry(bfe.frame, bfe.centerX, bfe.centerY); + PseudoBamFrameEntry entry = new PseudoBamFrameEntry(bfe.frame, bfe.getCenterX(), bfe.getCenterY()); entry.setOption(OPTION_INT_BLOCKINDEX, Integer.valueOf(blockStartIndex)); entry.setOption(OPTION_INT_BLOCKCOUNT, Integer.valueOf(1)); listFrameEntries.add(entry); @@ -969,10 +981,10 @@ public boolean exportBamV2(Path fileName, DxtEncoder.DxtType dxtType, int pvrzIn short v; for (int i = 0; i < listFrameEntries.size(); i++) { PseudoBamFrameEntry fe = listFrameEntries.get(i); - DynamicArray.putShort(bamData, ofs, (short)fe.width); - DynamicArray.putShort(bamData, ofs + 2, (short)fe.height); - DynamicArray.putShort(bamData, ofs + 4, (short)fe.centerX); - DynamicArray.putShort(bamData, ofs + 6, (short)fe.centerY); + DynamicArray.putShort(bamData, ofs, (short)fe.getWidth()); + DynamicArray.putShort(bamData, ofs + 2, (short)fe.getHeight()); + DynamicArray.putShort(bamData, ofs + 4, (short)fe.getCenterX()); + DynamicArray.putShort(bamData, ofs + 6, (short)fe.getCenterY()); o = fe.getOption(OPTION_INT_BLOCKINDEX); v = (o != null) ? ((Integer)o).shortValue() : 0; DynamicArray.putShort(bamData, ofs + 8, v); @@ -984,9 +996,9 @@ public boolean exportBamV2(Path fileName, DxtEncoder.DxtType dxtType, int pvrzIn // writing cycle entries for (int i = 0; i < listCycleData.size(); i++) { - Pair entry = listCycleData.get(i); - DynamicArray.putShort(bamData, ofs, entry.getFirst().shortValue()); - DynamicArray.putShort(bamData, ofs + 2, entry.getSecond().shortValue()); + Couple entry = listCycleData.get(i); + DynamicArray.putShort(bamData, ofs, entry.getValue0().shortValue()); + DynamicArray.putShort(bamData, ofs + 2, entry.getValue1().shortValue()); ofs += CycleEntrySize; } @@ -1034,7 +1046,7 @@ public int[] createGlobalPalette(HashMap colorMap) // adding pixels of all available frames to the hashset HashMap newMap; if (colorMap == null) { - newMap = new HashMap(); + newMap = new HashMap<>(); for (int i = 0; i < listFrames.size(); i++) { registerColors(newMap, listFrames.get(i).frame); } @@ -1068,7 +1080,7 @@ public int[] createGlobalPalette(HashMap colorMap) } // removing duplicate entries from the palette - HashSet colorSet = new HashSet(); + HashSet colorSet = new HashSet<>(); for (int i = 0; i < retVal.length; i++) { colorSet.add(Integer.valueOf(retVal[i])); } @@ -1354,15 +1366,44 @@ private boolean createPvrzPages(Path path, DxtEncoder.DxtType dxtType, List mapOptions = new HashMap(); + private final HashMap mapOptions = new HashMap<>(); private int width, height, centerX, centerY; + private int overrideCenterX, overrideCenterY; private BufferedImage frame; public PseudoBamFrameEntry(BufferedImage image, int centerX, int centerY) @@ -1370,6 +1411,8 @@ public PseudoBamFrameEntry(BufferedImage image, int centerX, int centerY) setFrame(image); setCenterX(centerX); setCenterY(centerY); + this.centerX = getCenterX(); + this.centerY = getCenterY(); } @Override @@ -1377,23 +1420,16 @@ public PseudoBamFrameEntry(BufferedImage image, int centerX, int centerY) @Override public int getHeight() { return height; } @Override - public int getCenterX() { return centerX; } + public int getCenterX() { return overrideCenterX; } @Override - public int getCenterY() { return centerY; } + public int getCenterY() { return overrideCenterY; } - public void setCenterX(int value) - { - if (value < Short.MIN_VALUE) value = Short.MIN_VALUE; - else if (value > Short.MAX_VALUE) value = Short.MAX_VALUE; - centerX = value; - } - - public void setCenterY(int value) - { - if (value < Short.MIN_VALUE) value = Short.MIN_VALUE; - else if (value > Short.MAX_VALUE) value = Short.MAX_VALUE; - centerY = value; - } + @Override + public void setCenterX(int x) { overrideCenterX = Math.max(Short.MIN_VALUE, Math.min(Short.MAX_VALUE, x)); } + @Override + public void setCenterY(int y) { overrideCenterY = Math.max(Short.MIN_VALUE, Math.min(Short.MAX_VALUE, y)); } + @Override + public void resetCenter() { overrideCenterX = centerX; overrideCenterY = centerY; } /** Returns the image object of this frame entry. */ public BufferedImage getFrame() @@ -1417,7 +1453,10 @@ public void setFrame(BufferedImage image) @Override public Object clone() { - return new PseudoBamFrameEntry(frame, centerX, centerY); + PseudoBamFrameEntry retVal = new PseudoBamFrameEntry(frame, centerX, centerY); + retVal.overrideCenterX = overrideCenterX; + retVal.overrideCenterY = overrideCenterY; + return retVal; } @Override @@ -1521,32 +1560,37 @@ public void cycleSetOption(int cycleIdx, String name, Object value) } - /** Adds a new empty cycle. */ - public void cycleAdd() + /** Adds a new empty cycle. Returns the index of the added cycle. */ + public int cycleAdd() { - cycleInsert(getDecoder().listCycles.size(), null); + return cycleInsert(getDecoder().listCycles.size(), null); } - /** Adds a new cycle and initializes it with an array of frame indices. */ - public void cycleAdd(int[] indices) + /** Adds a new cycle and initializes it with an array of frame indices. Returns the index of the added cycle. */ + public int cycleAdd(int[] indices) { - cycleInsert(getDecoder().listCycles.size(), indices); + return cycleInsert(getDecoder().listCycles.size(), indices); } /** Inserts a new empty cycle at the specified position. */ - public void cycleInsert(int cycleIdx) + public int cycleInsert(int cycleIdx) { - cycleInsert(cycleIdx, null); + return cycleInsert(cycleIdx, null); } - /** Inserts a new cycle at the specified position and initializes it with an array of frame indices. */ - public void cycleInsert(int cycleIdx, int[] indices) + /** + * Inserts a new cycle at the specified position and initializes it with an array of frame indices. + * Returns the index of the inserted cycle. Returns -1 if the cycle could not be inserted. + */ + public int cycleInsert(int cycleIdx, int[] indices) { if (cycleIdx >= 0 && cycleIdx <= getDecoder().listCycles.size()) { PseudoBamCycleEntry ce = new PseudoBamCycleEntry(indices); getDecoder().listCycles.add(cycleIdx, ce); update(); + return cycleIdx; } + return -1; } /** Removes the cycle at the specified position. */ @@ -1607,20 +1651,24 @@ public int cycleMove(int cycleIdx, int offset) /** Adds frame indices to the specified cycle. */ - public void cycleAddFrames(int cycleIdx, int[] indices) + public int cycleAddFrames(int cycleIdx, int[] indices) { if (cycleIdx >= 0 && cycleIdx < getDecoder().listCycles.size()) { - cycleInsertFrames(cycleIdx, getDecoder().listCycles.get(cycleIdx).size(), indices); + return cycleInsertFrames(cycleIdx, getDecoder().listCycles.get(cycleIdx).size(), indices); } + return -1; } /** Inserts frame indices to the cycle at the specified position. */ - public void cycleInsertFrames(int cycleIdx, int pos, int[] indices) + public int cycleInsertFrames(int cycleIdx, int pos, int[] indices) { if (cycleIdx >= 0 && cycleIdx < getDecoder().listCycles.size()) { - getDecoder().listCycles.get(cycleIdx).insert(pos, indices); - update(); + if (getDecoder().listCycles.get(cycleIdx).insert(pos, indices)) { + update(); + return pos; + } } + return -1; } /** Removes one frame index from the cycle at the specified position. */ @@ -1924,7 +1972,6 @@ private void init() { currentCycle = currentFrame = -1; update(); - updateSharedBamSize(); } // Updates current cycle and frame pointers @@ -1958,11 +2005,11 @@ private void update() public static class PseudoBamCycleEntry { private final List frames; // stores abs. frame indices that define this cycle - private final HashMap mapOptions = new HashMap(); + private final HashMap mapOptions = new HashMap<>(); protected PseudoBamCycleEntry(int[] indices) { - frames = new ArrayList(); + frames = new ArrayList<>(); add(indices); } @@ -2091,6 +2138,20 @@ public FrameDataV2(int page, int sx, int sy, int width, int height, int dx, int this.dy = dy; } + @Override + public int hashCode() + { + int hash = 7; + hash = 31 * hash + page; + hash = 31 * hash + sx; + hash = 31 * hash + sy; + hash = 31 * hash + width; + hash = 31 * hash + height; + hash = 31 * hash + dx; + hash = 31 * hash + dy; + return hash; + } + @Override public boolean equals(Object o) { diff --git a/src/org/infinity/resource/graphics/PvrDecoder.java b/src/org/infinity/resource/graphics/PvrDecoder.java index 03fc1288c..31ccb5515 100644 --- a/src/org/infinity/resource/graphics/PvrDecoder.java +++ b/src/org/infinity/resource/graphics/PvrDecoder.java @@ -63,7 +63,7 @@ public enum ChannelType { } // The global cache list for PVR objects. The "key" has to be a unique String (e.g. filename or integer as string) - private static final Map pvrCache = new LinkedHashMap(); + private static final Map pvrCache = new LinkedHashMap<>(); // The max. number of cache entries to hold private static int MaxCacheEntries = 32; @@ -86,18 +86,16 @@ public static PvrDecoder loadPvr(ResourceEntry entry) if (entry == null) { throw new NullPointerException(); } - try { + try (InputStream is = entry.getResourceDataAsStream()) { String key = null; if (entry instanceof FileResourceEntry) { key = ((FileResourceEntry)entry).getActualPath().toString(); } else { key = entry.getResourceName(); } - PvrDecoder decoder = getCachedPvrDecoder(key); + PvrDecoder decoder = createPvrDecoder(key, is); if (decoder != null) { return decoder; - } else { - return createPvrDecoder(key, entry.getResourceDataAsStream()); } } catch (Exception e) { e.printStackTrace(); @@ -115,13 +113,11 @@ public static PvrDecoder loadPvr(String fileName) if (fileName == null) { throw new NullPointerException(); } - try { + try (InputStream is = StreamUtils.getInputStream(FileManager.resolve(fileName))) { String key = fileName; - PvrDecoder decoder = getCachedPvrDecoder(key); + PvrDecoder decoder = createPvrDecoder(key, is); if (decoder != null) { return decoder; - } else { - return createPvrDecoder(key, StreamUtils.getInputStream(FileManager.resolve(fileName))); } } catch (Exception e) { e.printStackTrace(); @@ -136,13 +132,11 @@ public static PvrDecoder loadPvr(String fileName) */ public static PvrDecoder loadPvr(Path file) { - try { + try (InputStream is = StreamUtils.getInputStream(file)) { String key = file.getFileName().toString(); - PvrDecoder decoder = getCachedPvrDecoder(key); + PvrDecoder decoder = createPvrDecoder(key, is); if (decoder != null) { return decoder; - } else { - return createPvrDecoder(key, StreamUtils.getInputStream(file)); } } catch (Exception e) { e.printStackTrace(); @@ -162,11 +156,9 @@ public static PvrDecoder loadPvr(InputStream input) } try { String key = Integer.valueOf(input.hashCode()).toString(); - PvrDecoder decoder = getCachedPvrDecoder(key); + PvrDecoder decoder = createPvrDecoder(key, input); if (decoder != null) { return decoder; - } else { - return createPvrDecoder(key, input); } } catch (Exception e) { e.printStackTrace(); @@ -209,8 +201,24 @@ public static int getCacheLoad() } } - // Returns a PvrDecoder object only if it already exists in the cache. - private static synchronized PvrDecoder getCachedPvrDecoder(String key) +// // Returns a PvrDecoder object only if it already exists in the cache. +// private static synchronized PvrDecoder getCachedPvrDecoder(String key) +// { +// PvrDecoder retVal = null; +// if (key != null && !key.isEmpty()) { +// key = key.toUpperCase(Locale.ENGLISH); +// if (pvrCache.containsKey(key)) { +// retVal = pvrCache.get(key); +// // re-inserting entry to prevent premature removal from cache +// pvrCache.remove(key); +// pvrCache.put(key, retVal); +// } +// } +// return retVal; +// } + + // Returns a PvrDecoder object of the specified key if available, or creates and returns a new one otherwise. + private static synchronized PvrDecoder createPvrDecoder(String key, InputStream input) { PvrDecoder retVal = null; if (key != null && !key.isEmpty()) { @@ -220,30 +228,38 @@ private static synchronized PvrDecoder getCachedPvrDecoder(String key) // re-inserting entry to prevent premature removal from cache pvrCache.remove(key); pvrCache.put(key, retVal); - } - } - return retVal; - } - - // Returns a PvrDecoder object of the specified key if available, or creates and returns a new one otherwise. - private static synchronized PvrDecoder createPvrDecoder(String key, InputStream input) - { - PvrDecoder retVal = getCachedPvrDecoder(key); - if (retVal == null && input != null) { - try { - retVal = new PvrDecoder(input); - if (retVal != null) { - pvrCache.put(key, retVal); - // removing excess cache entries - while (pvrCache.size() > MaxCacheEntries) { - pvrCache.remove(pvrCache.keySet().iterator().next()); + } else { + try { + retVal = new PvrDecoder(input); + if (retVal != null) { + pvrCache.put(key, retVal); + // removing excess cache entries + while (pvrCache.size() > MaxCacheEntries) { + pvrCache.remove(pvrCache.keySet().iterator().next()); + } } + } catch (Exception e) { + e.printStackTrace(); } - } catch (Exception e) { - e.printStackTrace(); } } return retVal; +// PvrDecoder retVal = getCachedPvrDecoder(key); +// if (retVal == null && input != null) { +// try { +// retVal = new PvrDecoder(input); +// if (retVal != null) { +// pvrCache.put(key, retVal); +// // removing excess cache entries +// while (pvrCache.size() > MaxCacheEntries) { +// pvrCache.remove(pvrCache.keySet().iterator().next()); +// } +// } +// } catch (Exception e) { +// e.printStackTrace(); +// } +// } +// return retVal; } // Returns a rectangle that is aligned to the values specified as arguments 2 and 3 diff --git a/src/org/infinity/resource/graphics/PvrzResource.java b/src/org/infinity/resource/graphics/PvrzResource.java index 35e4ea472..b8a7533f8 100644 --- a/src/org/infinity/resource/graphics/PvrzResource.java +++ b/src/org/infinity/resource/graphics/PvrzResource.java @@ -100,8 +100,7 @@ public void actionPerformed(ActionEvent event) JOptionPane.ERROR_MESSAGE); } } else if (event.getSource() == miPNG) { - try { - ByteArrayOutputStream os = new ByteArrayOutputStream(); + try (ByteArrayOutputStream os = new ByteArrayOutputStream()) { final String fileName = StreamUtils.replaceFileExtension(entry.getResourceName(), "PNG"); BufferedImage image = getImage(); if (ImageIO.write(image, "png", os)) { @@ -112,9 +111,6 @@ public void actionPerformed(ActionEvent event) "Error while exporting " + entry, "Error", JOptionPane.ERROR_MESSAGE); } - os.close(); - os = null; - image = null; } catch (Exception e) { e.printStackTrace(); } diff --git a/src/org/infinity/resource/graphics/TisResource.java b/src/org/infinity/resource/graphics/TisResource.java index 0830352da..7a3a3b464 100644 --- a/src/org/infinity/resource/graphics/TisResource.java +++ b/src/org/infinity/resource/graphics/TisResource.java @@ -509,7 +509,7 @@ public JComponent makeViewer(ViewableContainer container) miExportPNG = new JMenuItem("as PNG"); miExportPNG.addActionListener(this); - List list = new ArrayList(); + List list = new ArrayList<>(); if (miExport != null) list.add(miExport); if (miExportPaletteTis != null) { @@ -618,7 +618,7 @@ private void initTileset() if (decoder != null) { int tileCount = decoder.getTileCount(); defaultWidth = calcTileWidth(entry, tileCount); - tileImages = new ArrayList(tileCount); + tileImages = new ArrayList<>(tileCount); for (int tileIdx = 0; tileIdx < tileCount; tileIdx++) { BufferedImage image = ColorConvert.createCompatibleImage(64, 64, Transparency.BITMASK); decoder.getTile(tileIdx, image); @@ -632,7 +632,7 @@ private void initTileset() e.printStackTrace(); WindowBlocker.blockWindow(false); if (tileImages == null) - tileImages = new ArrayList(); + tileImages = new ArrayList<>(); if (tileImages.isEmpty()) tileImages.add(ColorConvert.createCompatibleImage(1, 1, Transparency.BITMASK)); JOptionPane.showMessageDialog(NearInfinity.getInstance(), @@ -676,7 +676,7 @@ public Status convertToPaletteTis(Path output, boolean showProgress) BufferedImage image = ColorConvert.createCompatibleImage(decoder.getTileWidth(), decoder.getTileHeight(), Transparency.BITMASK); - IntegerHashMap colorCache = new IntegerHashMap(1800); // caching RGBColor -> index + IntegerHashMap colorCache = new IntegerHashMap<>(1800); // caching RGBColor -> index for (int tileIdx = 0; tileIdx < decoder.getTileCount(); tileIdx++) { colorCache.clear(); if (progress != null && progress.isCanceled()) { @@ -721,7 +721,7 @@ public Status convertToPaletteTis(Path output, boolean showProgress) if (palIndex != null) { tileData[i] = (byte)(palIndex + 1); } else { - byte color = (byte)ColorConvert.nearestColorRGB(pixels[i], palette, true); + byte color = (byte)ColorConvert.getNearestColor(pixels[i], palette, 0.0, null); tileData[i] = (byte)(color + 1); colorCache.put(pixels[i], color); } @@ -816,7 +816,7 @@ public Status convertToPvrzTis(Path output, boolean showProgress) } boolean[] markedTiles = new boolean[numTiles]; Arrays.fill(markedTiles, false); - List listRegions = new ArrayList(256); + List listRegions = new ArrayList<>(256); // divide primary tiles into regions int pw = (tisWidth + pageDim - 1) / pageDim; @@ -924,8 +924,8 @@ public Status convertToPvrzTis(Path output, boolean showProgress) } // packing tileset regions - List entryList = new ArrayList(numTiles); - List pageList = new ArrayList(); + List entryList = new ArrayList<>(numTiles); + List pageList = new ArrayList<>(); for (TileRect rect: listRegions) { Dimension space = new Dimension(rect.bounds); int pageIndex = -1; diff --git a/src/org/infinity/resource/itm/Ability.java b/src/org/infinity/resource/itm/Ability.java index 314137d2f..ebb884215 100644 --- a/src/org/infinity/resource/itm/Ability.java +++ b/src/org/infinity/resource/itm/Ability.java @@ -51,7 +51,7 @@ public final class Ability extends AbstractAbility implements AddRemovable, HasC 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, "EE: Break Sanctuary;Ignored for Target: Caster", + "EE: THAC0 strength bonus", null, null, null, null, null, "EE: Break Sanctuary/Invisibility;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"}; @@ -65,7 +65,6 @@ public final class Ability extends AbstractAbility implements AddRemovable, HasC super(superStruct, ITM_ABIL + " " + number, buffer, offset); } - // @Override public AddRemovable[] getPrototypes() throws Exception { @@ -77,17 +76,13 @@ public AddRemovable confirmAddEntry(AddRemovable entry) throws Exception { return entry; } - // - // @Override public boolean canRemove() { return true; } - // - // @Override public int getViewerTabCount() { @@ -111,9 +106,7 @@ public boolean viewerTabAddedBefore(int index) { return true; } - // - // @Override public int read(ByteBuffer buffer, int offset) throws Exception { @@ -174,6 +167,5 @@ public int read(ByteBuffer buffer, int offset) throws Exception return offset + 56; } - // } diff --git a/src/org/infinity/resource/itm/ItmResource.java b/src/org/infinity/resource/itm/ItmResource.java index 176e74596..bb836a60f 100644 --- a/src/org/infinity/resource/itm/ItmResource.java +++ b/src/org/infinity/resource/itm/ItmResource.java @@ -16,9 +16,11 @@ import javax.swing.JScrollPane; import org.infinity.datatype.Bitmap; +import org.infinity.datatype.ColorValue; import org.infinity.datatype.DecNumber; import org.infinity.datatype.Flag; import org.infinity.datatype.IdsBitmap; +import org.infinity.datatype.IsNumeric; import org.infinity.datatype.ResourceRef; import org.infinity.datatype.SectionCount; import org.infinity.datatype.SectionOffset; @@ -42,8 +44,13 @@ import org.infinity.resource.key.ResourceEntry; import org.infinity.resource.spl.SplResource; import org.infinity.search.SearchOptions; +import org.infinity.util.IdsMap; import org.infinity.util.IdsMapCache; +import org.infinity.util.IdsMapEntry; +import org.infinity.util.Misc; import org.infinity.util.StringTable; +import org.infinity.util.Table2da; +import org.infinity.util.Table2daCache; import org.infinity.util.io.StreamUtils; /** @@ -189,7 +196,7 @@ public final class ItmResource extends AbstractStruct implements Resource, HasCh "Lawful;Includes Lawful Good, Lawful Neutral and Lawful Evil", "Neutral ...;Includes Neutral Good, True Neutral and Neutral Evil", null, null, null, null, null, "Elf", - "Dwarf", "Half-elf", "Halfling", "Human", "Gnome" + "Dwarf", "Half-elf", "Halfling", "Human", "Gnome", "Half-Orc" }; public static final String[] s_kituse1 = {"None", "Cleric of Talos", "Cleric of Helm", "Cleric of Lathander", @@ -203,6 +210,17 @@ public final class ItmResource extends AbstractStruct implements Resource, HasCh public static final String[] s_kituse4 = {"None", "Berserker", "Wizard slayer", "Kensai", "Cavalier", "Inquisitor", "Undead hunter", "Abjurer", "Conjurer"}; + public static final String[] s_kituse1_v2 = {"None", "", "", "", "", "", "", "", ""}; + public static final String[] s_kituse2_v2 = + {"None", "Cleric of Lathander", "Cleric of Selune", "Cleric of Helm", "Cleric of Oghma", + "Cleric of Tempus", "Cleric of Bane", "Cleric of Mask", "Cleric of Talos"}; + public static final String[] s_kituse3_v2 = + {"None", "Diviner", "Enchanter", "Illusionist", "Invoker", "Necromancer", "Transmuter", + "Generalist", "Cleric of Ilmater"}; + public static final String[] s_kituse4_v2 = + {"None", "Paladin of Ilmater", "Paladin of Helm", "Paladin of Mystra", + "Monk of the Old Order", "Monk of the Broken Ones", "Monk of the Dark Moon", + "Abjurer", "Conjurer"}; private StructHexViewer hexViewer; @@ -223,7 +241,6 @@ public ItmResource(ResourceEntry entry) throws Exception super(entry); } - // @Override public AddRemovable[] getPrototypes() throws Exception { @@ -235,9 +252,7 @@ public AddRemovable confirmAddEntry(AddRemovable entry) throws Exception { return entry; } - // - // @Override public int getViewerTabCount() { @@ -282,9 +297,7 @@ public boolean viewerTabAddedBefore(int index) { return (index == 0); } - // - // @Override public void write(OutputStream os) throws IOException { @@ -296,9 +309,7 @@ public void write(OutputStream os) throws IOException } } } - // - // @Override protected void viewerInitialized(StructViewer viewer) { @@ -315,7 +326,7 @@ protected void datatypeAdded(AddRemovable datatype) } } else if (datatype instanceof Ability) { - int effect_count = ((SectionCount)getAttribute(ITM_NUM_GLOBAL_EFFECTS)).getValue(); + int effect_count = ((IsNumeric)getAttribute(ITM_NUM_GLOBAL_EFFECTS)).getValue(); for (final StructEntry o : getFields()) { if (o instanceof Ability) { Ability ability = (Ability)o; @@ -346,7 +357,7 @@ protected void datatypeRemoved(AddRemovable datatype) } } else if (datatype instanceof Ability) { - int effect_count = ((SectionCount)getAttribute(ITM_NUM_GLOBAL_EFFECTS)).getValue(); + int effect_count = ((IsNumeric)getAttribute(ITM_NUM_GLOBAL_EFFECTS)).getValue(); for (final StructEntry o : getFields()) { if (o instanceof Ability) { Ability ability = (Ability)o; @@ -366,19 +377,19 @@ protected void datatypeRemovedInChild(AbstractStruct child, AddRemovable datatyp super.datatypeRemovedInChild(child, datatype); incAbilityEffects(child, datatype, -1); } - // - // @Override public int read(ByteBuffer buffer, int offset) throws Exception { addField(new TextString(buffer, 0, 4, COMMON_SIGNATURE)); TextString version = new TextString(buffer, 4, 4, COMMON_VERSION); + boolean isV10 = version.getText().equalsIgnoreCase("V1 "); + boolean isV11 = version.getText().equalsIgnoreCase("V1.1"); + boolean isV20 = version.getText().equalsIgnoreCase("V2.0"); addField(version); addField(new StringRef(buffer, 8, ITM_NAME_GENERAL)); addField(new StringRef(buffer, 12, ITM_NAME_IDENTIFIED)); - if (version.getText().equalsIgnoreCase("V1.1") || - (version.getText().equalsIgnoreCase("V1 ") && Profile.getGame() == Profile.Game.PSTEE)) { + if (isV11 || (isV10 && Profile.getGame() == Profile.Game.PSTEE)) { addField(new ResourceRef(buffer, 16, ITM_DROP_SOUND, "WAV")); if (Profile.getGame() == Profile.Game.PSTEE) { addField(new Flag(buffer, 24, 4, ITM_FLAGS, s_flags_pstee)); @@ -394,7 +405,7 @@ public int read(ByteBuffer buffer, int offset) throws Exception addField(new ResourceRef(buffer, 16, ITM_USED_UP_ITEM, "ITM")); addField(new Flag(buffer, 24, 4, ITM_FLAGS, IdsMapCache.getUpdatedIdsFlags(s_flags, "ITEMFLAG.IDS", 4, false, false))); addField(new Bitmap(buffer, 28, 2, ITM_CATEGORY, s_categories)); - if (version.toString().equalsIgnoreCase("V2.0")) { + if (isV20) { addField(new Flag(buffer, 30, 4, ITM_UNUSABLE_BY, s_usability20)); } else { addField(new Flag(buffer, 30, 4, ITM_UNUSABLE_BY, s_usability)); @@ -405,13 +416,16 @@ public int read(ByteBuffer buffer, int offset) throws Exception addField(new DecNumber(buffer, 38, 2, ITM_MIN_STRENGTH)); if (ResourceFactory.resourceExists("KIT.IDS")) { addField(new DecNumber(buffer, 40, 1, ITM_MIN_STRENGTH_BONUS)); - addField(new Flag(buffer, 41, 1, ITM_UNUSABLE_BY_1, s_kituse1)); + if (isV20) { + updateKitUsability(s_kituse1_v2, 24, 8, true); + } + addField(new Flag(buffer, 41, 1, ITM_UNUSABLE_BY_1, isV20 ? s_kituse1_v2 : s_kituse1)); addField(new DecNumber(buffer, 42, 1, ITM_MIN_INTELLIGENCE)); - addField(new Flag(buffer, 43, 1, ITM_UNUSABLE_BY_2, s_kituse2)); + addField(new Flag(buffer, 43, 1, ITM_UNUSABLE_BY_2, isV20 ? s_kituse2_v2 : s_kituse2)); addField(new DecNumber(buffer, 44, 1, ITM_MIN_DEXTERITY)); - addField(new Flag(buffer, 45, 1, ITM_UNUSABLE_BY_3, s_kituse3)); + addField(new Flag(buffer, 45, 1, ITM_UNUSABLE_BY_3, isV20 ? s_kituse3_v2 : s_kituse3)); addField(new DecNumber(buffer, 46, 1, ITM_MIN_WISDOM)); - addField(new Flag(buffer, 47, 1, ITM_UNUSABLE_BY_4, s_kituse4)); + addField(new Flag(buffer, 47, 1, ITM_UNUSABLE_BY_4, isV20 ? s_kituse4_v2 : s_kituse4)); addField(new DecNumber(buffer, 48, 1, ITM_MIN_CONSTITUTION)); if (ResourceFactory.resourceExists("PROFTYPE.IDS")) { addField(new IdsBitmap(buffer, 49, 1, ITM_WEAPON_PROFICIENCY, "PROFTYPE.IDS")); @@ -435,7 +449,7 @@ public int read(ByteBuffer buffer, int offset) throws Exception addField(new DecNumber(buffer, 76, 4, ITM_WEIGHT)); addField(new StringRef(buffer, 80, ITM_DESCRIPTION_GENERAL)); addField(new StringRef(buffer, 84, ITM_DESCRIPTION_IDENTIFIED)); - if (version.toString().equalsIgnoreCase("V1.1")) { + if (isV11) { addField(new ResourceRef(buffer, 88, ITM_PICK_UP_SOUND, "WAV")); } else { addField(new ResourceRef(buffer, 88, ITM_DESCRIPTION_IMAGE, "BAM")); @@ -455,13 +469,13 @@ public int read(ByteBuffer buffer, int offset) throws Exception Effect.class); addField(global_count); - if (version.toString().equalsIgnoreCase("V1.1")) { + if (isV11) { addField(new ResourceRef(buffer, 114, ITM_DIALOG, "DLG")); addField(new StringRef(buffer, 122, ITM_SPEAKER_NAME)); - addField(new IdsBitmap(buffer, 126, 2, ITM_WEAPON_COLOR, "CLOWNCLR.IDS")); + addField(new ColorValue(buffer, 126, 2, ITM_WEAPON_COLOR, true, "PAL32.BMP")); addField(new Unknown(buffer, 128, 26)); } - else if (version.toString().equalsIgnoreCase("V2.0")) { + else if (isV20) { addField(new Unknown(buffer, 114, 16)); } @@ -485,7 +499,6 @@ else if (version.toString().equalsIgnoreCase("V2.0")) { return Math.max(offset, offset2); } - // private void incAbilityEffects(StructEntry child, AddRemovable datatype, int value) { @@ -504,6 +517,57 @@ private void incAbilityEffects(StructEntry child, AddRemovable datatype, int val } } + /** + * Updates the specified string array with kit names from KIT.IDS. + * @param kits The string array for kit names. (First slot is reserved for empty selection string.) + * @param offset bit position to start. + * @param count number of bits to update. + * @param fillMissing whether only empty slots in the string array should be updated. + */ + private void updateKitUsability(String[] kits, int offset, int count, boolean fillMissing) + { + if (kits != null && offset >= 0 && offset < 32 && count > 0) { + IdsMap map = IdsMapCache.get("KIT.IDS"); + Table2da table = Table2daCache.get("KITLIST.2DA"); + if (map != null) { + for (int i = 0; i < count; i++) { + long value = 1L << (offset + i); + IdsMapEntry entry = map.get(value); + if (entry != null) { + if (i + 1 < kits.length && (!fillMissing || kits[i + 1] == null || kits[i + 1].isEmpty())) { + int strref = -1; + if (table != null) { + // try getting proper kit name from kitlist.2da + for (int row = 3, rowCount = table.getRowCount(); row < rowCount; row++) { + if (entry.getSymbol().equalsIgnoreCase(table.get(row, 0))) { + strref = Misc.toNumber(table.get(row, 2), -1); // mixed + if (strref < 0) { + strref = Misc.toNumber(table.get(row, 1), -1); // lowercase + } + } + if (strref > 0) { + break; + } + } + } + String desc = null; + if (strref > 0) { + try { + desc = StringTable.getStringRef(strref); + } catch (IndexOutOfBoundsException e) { + } + } + if (desc == null || desc.isEmpty()) { + desc = entry.getSymbol(); + } + kits[i + 1] = desc; + } + } + } + } + } + } + /** * Called by "Extended Search" * Checks whether the specified resource entry matches all available search options. @@ -521,8 +585,8 @@ public static boolean matchSearchOptions(ResourceEntry entry, SearchOptions sear Object o; // preparing substructures - DecNumber ofs = (DecNumber)itm.getAttribute(ITM_OFFSET_EFFECTS, false); - DecNumber cnt = (DecNumber)itm.getAttribute(ITM_NUM_GLOBAL_EFFECTS, false); + IsNumeric ofs = (IsNumeric)itm.getAttribute(ITM_OFFSET_EFFECTS, false); + IsNumeric cnt = (IsNumeric)itm.getAttribute(ITM_NUM_GLOBAL_EFFECTS, false); if (ofs != null && ofs.getValue() > 0 && cnt != null && cnt.getValue() > 0) { effects = new Effect[cnt.getValue()]; for (int idx = 0; idx < cnt.getValue(); idx++) { @@ -533,8 +597,8 @@ public static boolean matchSearchOptions(ResourceEntry entry, SearchOptions sear effects = new Effect[0]; } - ofs = (DecNumber)itm.getAttribute(ITM_OFFSET_ABILITIES, false); - cnt = (DecNumber)itm.getAttribute(ITM_NUM_ABILITIES, false); + ofs = (IsNumeric)itm.getAttribute(ITM_OFFSET_ABILITIES, false); + cnt = (IsNumeric)itm.getAttribute(ITM_NUM_ABILITIES, false); if (ofs != null && ofs.getValue() > 0 && cnt != null && cnt.getValue() > 0) { abilities = new Ability[cnt.getValue()]; for (int idx = 0; idx < cnt.getValue(); idx++) { @@ -548,7 +612,7 @@ public static boolean matchSearchOptions(ResourceEntry entry, SearchOptions sear abilityEffects = new Effect[abilities.length][]; for (int idx = 0; idx < abilities.length; idx++) { if (abilities[idx] != null) { - cnt = (DecNumber)abilities[idx].getAttribute(AbstractAbility.ABILITY_NUM_EFFECTS, false); + cnt = (IsNumeric)abilities[idx].getAttribute(AbstractAbility.ABILITY_NUM_EFFECTS, false); if (cnt != null && cnt.getValue() > 0) { abilityEffects[idx] = new Effect[cnt.getValue()]; for (int idx2 = 0; idx2 < cnt.getValue(); idx2++) { diff --git a/src/org/infinity/resource/itm/Viewer.java b/src/org/infinity/resource/itm/Viewer.java index 29ae538ba..56815b39d 100644 --- a/src/org/infinity/resource/itm/Viewer.java +++ b/src/org/infinity/resource/itm/Viewer.java @@ -16,15 +16,18 @@ import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JPanel; +import javax.swing.JTabbedPane; import org.infinity.datatype.EffectType; import org.infinity.datatype.Flag; +import org.infinity.datatype.IsNumeric; import org.infinity.datatype.ResourceRef; import org.infinity.gui.ViewerUtil; import org.infinity.resource.AbstractAbility; import org.infinity.resource.Effect; import org.infinity.resource.Profile; import org.infinity.resource.StructEntry; +import org.infinity.util.StringTable; import org.infinity.util.Table2da; import org.infinity.util.Table2daCache; @@ -38,10 +41,17 @@ final class Viewer extends JPanel JPanel abilitiesPanel = ViewerUtil.makeListPanel("Abilities", itm, Ability.class, AbstractAbility.ABILITY_TYPE); JPanel fieldPanel = makeFieldPanel(itm); JPanel boxPanel = ViewerUtil.makeCheckPanel((Flag)itm.getAttribute(ItmResource.ITM_FLAGS), 1); - StructEntry desc = itm.getAttribute(ItmResource.ITM_DESCRIPTION_IDENTIFIED); - if (desc.toString().equalsIgnoreCase("No such index")) - desc = itm.getAttribute(ItmResource.ITM_DESCRIPTION_GENERAL); - JPanel descPanel = ViewerUtil.makeTextAreaPanel(desc); + + StructEntry descGeneral = itm.getAttribute(ItmResource.ITM_DESCRIPTION_GENERAL); + StructEntry descIdentified = itm.getAttribute(ItmResource.ITM_DESCRIPTION_IDENTIFIED); + JTabbedPane tabbedDescPanel = new JTabbedPane(JTabbedPane.TOP); + tabbedDescPanel.addTab(descGeneral.getName(), ViewerUtil.makeTextAreaPanel(descGeneral, false)); + tabbedDescPanel.setEnabledAt(0, StringTable.isValidStringRef(((IsNumeric)descGeneral).getValue())); + tabbedDescPanel.addTab(descIdentified.getName(), ViewerUtil.makeTextAreaPanel(descIdentified, false)); + tabbedDescPanel.setEnabledAt(1, StringTable.isValidStringRef(((IsNumeric)descIdentified).getValue())); + if (tabbedDescPanel.isEnabledAt(1)) { + tabbedDescPanel.setSelectedIndex(1); + } JPanel iconPanel = new JPanel(new GridLayout(2, 1, 0, 6)); iconPanel.add(iconPanel1); @@ -56,7 +66,7 @@ final class Viewer extends JPanel panel2.add(globaleffectsPanel); JPanel panel3 = new JPanel(new GridLayout(2, 1, 6, 6)); - panel3.add(descPanel); + panel3.add(tabbedDescPanel); panel3.add(panel2); JPanel panel4 = new JPanel(new BorderLayout()); diff --git a/src/org/infinity/resource/key/BIFFEntry.java b/src/org/infinity/resource/key/BIFFEntry.java index 8188e7498..60ceceba9 100644 --- a/src/org/infinity/resource/key/BIFFEntry.java +++ b/src/org/infinity/resource/key/BIFFEntry.java @@ -86,22 +86,45 @@ public void write(OutputStream os) throws IOException //--------------------- End Interface Writeable --------------------- + @Override + public int hashCode() + { + int hash = 7; + hash = 31 * hash + location; + hash = 31 * hash + ((keyFile == null) ? 0 : keyFile.hashCode()); + hash = 31 * hash + ((biffFile == null) ? 0 : biffFile.hashCode()); + hash = 31 * hash + ((fileName == null) ? 0 : fileName.hashCode()); + hash = 31 * hash + index; + hash = 31 * hash + fileSize; + hash = 31 * hash + stringOffset; + hash = 31 * hash + (int)separatorChar; + return hash; + } + @Override public boolean equals(Object o) { if (this == o) { return true; - } else if (o instanceof BIFFEntry) { - BIFFEntry other = (BIFFEntry)o; - boolean bRet = (keyFile == null && other.keyFile == null) || - (keyFile != null && keyFile.equals(other.keyFile)); - bRet &= (biffFile == null && other.biffFile == null) || - (biffFile != null && biffFile.equals(other.biffFile)); - bRet &= (fileSize == other.fileSize) && (stringOffset == other.stringOffset); - return bRet; - } else { + } + + if (!(o instanceof BIFFEntry)) { return false; } + + BIFFEntry other = (BIFFEntry)o; + boolean retVal = (location == other.location); + retVal &= (keyFile == null && other.keyFile == null) || + (keyFile != null && keyFile.equals(other.keyFile)); + retVal &= (biffFile == null && other.biffFile == null) || + (biffFile != null && biffFile.equals(other.biffFile)); + retVal &= (fileName == null && other.fileName == null) || + (fileName != null && fileName.equals(other.fileName)); + retVal &= (index == other.index); + retVal &= (fileSize == other.fileSize); + retVal &= (stringOffset == other.stringOffset); + retVal &= (separatorChar == other.separatorChar); + return retVal; } @Override diff --git a/src/org/infinity/resource/key/BIFFReader.java b/src/org/infinity/resource/key/BIFFReader.java index cd95ae80d..20d3038ac 100644 --- a/src/org/infinity/resource/key/BIFFReader.java +++ b/src/org/infinity/resource/key/BIFFReader.java @@ -103,13 +103,13 @@ public ByteBuffer getResourceBuffer(int locator) throws IOException buffer = StreamUtils.getByteBuffer(remaining); StreamUtils.copyBytes(header, buffer, header.limit()); remaining -= header.limit(); - while (channel.read(buffer) > 0); + while (channel.read(buffer) > 0) {} } finally { blocker.setBlocked(false); } } else { buffer = StreamUtils.getByteBuffer(entry.size); - while (channel.read(buffer) > 0); + while (channel.read(buffer) > 0) {} } buffer.position(0); @@ -139,7 +139,7 @@ public InputStream getResourceAsStream(int locator) throws IOException } } - private void init(ByteBuffer buffer, int numFiles, int numTilesets) throws IOException + private void init(ByteBuffer buffer, int numFiles, int numTilesets) { // reading file entries for (int i = 0; i < numFiles; i++) { diff --git a/src/org/infinity/resource/key/BIFFResourceEntry.java b/src/org/infinity/resource/key/BIFFResourceEntry.java index c923481fc..f2f4bc721 100644 --- a/src/org/infinity/resource/key/BIFFResourceEntry.java +++ b/src/org/infinity/resource/key/BIFFResourceEntry.java @@ -85,18 +85,38 @@ public void write(OutputStream os) throws IOException // --------------------- End Interface Writeable --------------------- + @Override + public int hashCode() + { + int hash = super.hashCode(); + hash = 31 * hash + ((resourceName == null) ? 0 : resourceName.hashCode()); + hash = 31 * hash + type; + hash = 31 * hash + ((extension == null) ? 0 : extension.hashCode()); + hash = 31 * hash + Boolean.hashCode(hasOverride); + hash = 31 * hash + locator; + return hash; + } + @Override public boolean equals(Object o) { if (o == this) { return true; - } else if (o instanceof BIFFResourceEntry) { - BIFFResourceEntry other = (BIFFResourceEntry)o; - return (locator == other.locator) && - (type == other.type) && - resourceName.equalsIgnoreCase(other.resourceName); } - return false; + + if (!(o instanceof BIFFResourceEntry)) { + return false; + } + + BIFFResourceEntry other = (BIFFResourceEntry)o; + boolean retVal = (resourceName == null && other.resourceName == null) || + (resourceName != null && resourceName.equalsIgnoreCase(other.resourceName)); + retVal &= (type == other.type); + retVal &= (extension == null && other.extension == null) || + (extension != null && extension.equalsIgnoreCase(other.extension)); + retVal &= (hasOverride == other.hasOverride); + retVal &= (locator == other.locator); + return retVal; } @Override diff --git a/src/org/infinity/resource/key/BIFReader.java b/src/org/infinity/resource/key/BIFReader.java index acc216eeb..fdd2193a5 100644 --- a/src/org/infinity/resource/key/BIFReader.java +++ b/src/org/infinity/resource/key/BIFReader.java @@ -175,7 +175,7 @@ private void init() throws Exception } // Returns an inflater input stream - private InflaterInputStream getInflaterInputStream() throws IOException + private InflaterInputStream getInflaterInputStream() { return new InflaterInputStream(new ByteBufferInputStream(mappedBuffer.duplicate())); } diff --git a/src/org/infinity/resource/key/BufferedResourceEntry.java b/src/org/infinity/resource/key/BufferedResourceEntry.java new file mode 100644 index 000000000..3473196ad --- /dev/null +++ b/src/org/infinity/resource/key/BufferedResourceEntry.java @@ -0,0 +1,136 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.resource.key; + +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Locale; +import java.util.Objects; + +import org.infinity.gui.BrowserMenuBar; +import org.infinity.gui.BrowserMenuBar.OverrideMode; +import org.infinity.resource.Profile; +import org.infinity.resource.ResourceFactory; +import org.infinity.util.io.ByteBufferInputStream; +import org.infinity.util.io.StreamUtils; + +/** + * A ResourceEntry based on {@link ByteBuffer} data. + */ +public class BufferedResourceEntry extends ResourceEntry +{ + private final ByteBuffer buffer; + private final String fileName; + + public BufferedResourceEntry(ByteBuffer buffer, String fileName) + { + this.buffer = Objects.requireNonNull(buffer); + this.fileName = Objects.requireNonNull(fileName); + } + + @Override + protected Path getActualPath(boolean ignoreOverride) + { + return Paths.get(Profile.getProperty(Profile.Key.GET_GAME_ROOT_FOLDER), + Profile.getProperty(Profile.Key.GET_GLOBAL_OVERRIDE_NAME), + getResourceName()); + } + + @Override + public long getResourceSize(boolean ignoreOverride) + { + return buffer.limit(); + } + + @Override + public String getExtension() + { + return getResourceName().substring(getResourceName().lastIndexOf('.') + 1).toUpperCase(Locale.ENGLISH); + } + + @Override + public ByteBuffer getResourceBuffer(boolean ignoreOverride) throws Exception + { + return buffer; + } + + @Override + public InputStream getResourceDataAsStream(boolean ignoreOverride) throws Exception + { + return new ByteBufferInputStream(getResourceBuffer()); + } + + @Override + public int[] getResourceInfo(boolean ignoreOverride) throws Exception + { + ByteBuffer buffer = getResourceBuffer(); + String sig = "", ver = ""; + if (getResourceSize() >= 8) { + sig = StreamUtils.readString(buffer, 0, 4); + ver = StreamUtils.readString(buffer, 4, 4); + } + if ("TIS ".equals(sig) && "V1 ".equals(ver)) { + if (getResourceSize() > 16) { + int v1 = buffer.getInt(8); + int v2 = buffer.getInt(12); + return new int[] { v1, v2 }; + } else { + throw new Exception("Unexpected end of file"); + } + } else { + return new int[] { (int)getResourceSize() }; + } + } + + @Override + public String getResourceName() + { + return fileName; + } + + @Override + public String getResourceRef() + { + String fileName = getResourceName(); + int pos = fileName.lastIndexOf('.'); + if (pos >= 0) + fileName = fileName.substring(0, pos); + return fileName; + } + + @Override + public String getTreeFolderName() + { + if (BrowserMenuBar.getInstance() != null) { + final OverrideMode mode = BrowserMenuBar.getInstance().getOverrideMode(); + final Keyfile keyfile = ResourceFactory.getKeyfile(); + + if (keyfile.getExtensionType(getExtension()) != -1) { + if (mode == OverrideMode.InTree) { + return getExtension(); + } else if (mode == OverrideMode.Split && + keyfile.getResourceEntry(getResourceName()) != null) { + return getExtension(); + } + } + } + return Profile.getOverrideFolderName(); + } + + @Override + public ResourceTreeFolder getTreeFolder() + { + return ResourceFactory.getResourceTreeModel().getFolder(getTreeFolderName()); + } + + @Override + public boolean hasOverride() + { + return false; + } + +} diff --git a/src/org/infinity/resource/key/FileResourceEntry.java b/src/org/infinity/resource/key/FileResourceEntry.java index 6e0ac03d5..508cb5557 100644 --- a/src/org/infinity/resource/key/FileResourceEntry.java +++ b/src/org/infinity/resource/key/FileResourceEntry.java @@ -103,6 +103,7 @@ public String getResourceName() return file.getFileName().toString(); } + @Override public String getResourceRef() { String fileName = file.getFileName().toString(); diff --git a/src/org/infinity/resource/key/Keyfile.java b/src/org/infinity/resource/key/Keyfile.java index aaef18511..fc5783b32 100644 --- a/src/org/infinity/resource/key/Keyfile.java +++ b/src/org/infinity/resource/key/Keyfile.java @@ -222,6 +222,19 @@ public Keyfile(Path keyFile) throws FileNotFoundException resourceIcons.put("LOG", ICON_TEXT);// WeiDU log files - from Special category in the resource tree } + @Override + public int hashCode() + { + int hash = 7; + hash = 31 * hash + ((keyFile == null) ? 0 : keyFile.hashCode()); + hash = 31 * hash + ((keyList == null) ? 0 : keyList.hashCode()); + hash = 31 * hash + ((extMap == null) ? 0 : extMap.hashCode()); + hash = 31 * hash + ((resourceIcons == null) ? 0 : resourceIcons.hashCode()); + hash = 31 * hash + ((biffEntries == null) ? 0 : biffEntries.hashCode()); + hash = 31 * hash + ((resourceEntries == null) ? 0 : resourceEntries.hashCode()); + return hash; + } + @Override public boolean equals(Object o) { @@ -257,7 +270,7 @@ public List getDlcKeyfiles() * Overrides current key file mapping with data from the specified key file. * @param keyFile The key file containing new entries. */ - public void addKeyfile(Path keyFile) throws IOException + public void addKeyfile(Path keyFile) { if (keyFile == null) { throw new NullPointerException("No DLC keyfile specified"); diff --git a/src/org/infinity/resource/key/ResourceEntry.java b/src/org/infinity/resource/key/ResourceEntry.java index 49cb603a6..d2e62f0b0 100644 --- a/src/org/infinity/resource/key/ResourceEntry.java +++ b/src/org/infinity/resource/key/ResourceEntry.java @@ -37,7 +37,7 @@ public abstract class ResourceEntry implements Comparable { // list of file extensions not shown in the resource tree - private static final HashSet skippedExtensions = new HashSet(); + private static final HashSet skippedExtensions = new HashSet<>(); static { skippedExtensions.add("BAK"); @@ -95,6 +95,14 @@ static int[] getLocalFileInfo(Path file) return null; } + @Override + public int hashCode() + { + int hash = 7; + hash = 31 * hash + ((searchString == null) ? 0 : searchString.hashCode()); + return hash; + } + @Override public boolean equals(Object o) { diff --git a/src/org/infinity/resource/key/ResourceTreeFolder.java b/src/org/infinity/resource/key/ResourceTreeFolder.java index 53d5f5383..ef297463e 100644 --- a/src/org/infinity/resource/key/ResourceTreeFolder.java +++ b/src/org/infinity/resource/key/ResourceTreeFolder.java @@ -16,7 +16,7 @@ public final class ResourceTreeFolder implements Comparable { private final SortedListSet resourceEntries = new SortedListSet<>(); - private final List folders = new ArrayList(); + private final List folders = new ArrayList<>(); private final ResourceTreeFolder parentFolder; private final String folderName; @@ -54,7 +54,7 @@ public List getResourceEntries() public List getResourceEntries(String type) { - List list = new ArrayList(); + List list = new ArrayList<>(); resourceEntries.forEach((entry) -> { if (entry.getExtension().equalsIgnoreCase(type)) { list.add(entry); @@ -287,7 +287,7 @@ public T first() @Override public synchronized SortedSet headSet(T toElement) { - int toIdx = Collections.binarySearch(this, (T)toElement); + int toIdx = Collections.binarySearch(this, toElement); if (toIdx < 0) { toIdx = ~toIdx; } else { @@ -308,11 +308,11 @@ public T last() @Override public synchronized SortedSet subSet(T fromElement, T toElement) { - int fromIdx = Collections.binarySearch(this, (T)fromElement); + int fromIdx = Collections.binarySearch(this, fromElement); if (fromIdx < 0) { fromIdx = ~fromIdx; } - int toIdx = Collections.binarySearch(this, (T)toElement); + int toIdx = Collections.binarySearch(this, toElement); if (toIdx < 0) { toIdx = ~toIdx; } else { @@ -324,7 +324,7 @@ public synchronized SortedSet subSet(T fromElement, T toElement) @Override public synchronized SortedSet tailSet(T fromElement) { - int fromIdx = Collections.binarySearch(this, (T)fromElement); + int fromIdx = Collections.binarySearch(this, fromElement); if (fromIdx < 0) { fromIdx = ~fromIdx; } @@ -333,7 +333,7 @@ public synchronized SortedSet tailSet(T fromElement) private SortedSet getSortedSet(int fromIdx, int toIdx) { - SortedListSet retVal = new SortedListSet(); + SortedListSet retVal = new SortedListSet<>(); for (int idx = fromIdx; idx <= toIdx; idx++) { retVal.add(get(idx)); } diff --git a/src/org/infinity/resource/key/ResourceTreeModel.java b/src/org/infinity/resource/key/ResourceTreeModel.java index f0116c17a..8cbdbeac1 100644 --- a/src/org/infinity/resource/key/ResourceTreeModel.java +++ b/src/org/infinity/resource/key/ResourceTreeModel.java @@ -147,7 +147,7 @@ public List getBIFFResourceEntries() public List getBIFFResourceEntries(Path keyFile) { - List list = new ArrayList(); + List list = new ArrayList<>(); for (int i = 0; i < root.getFolders().size(); i++) { List entries = root.getFolders().get(i).getResourceEntries(); for (int j = 0; j < entries.size(); j++) { diff --git a/src/org/infinity/resource/mus/MusResource.java b/src/org/infinity/resource/mus/MusResource.java index 87e974d8c..e3e1c2176 100644 --- a/src/org/infinity/resource/mus/MusResource.java +++ b/src/org/infinity/resource/mus/MusResource.java @@ -185,7 +185,6 @@ public MusResource(ResourceEntry entry) throws Exception resourceChanged = false; } - // @Override public void actionPerformed(ActionEvent event) { @@ -199,9 +198,7 @@ else if (buttonPanel.getControlByType(ButtonPanel.Control.EXPORT_BUTTON) == even ResourceFactory.exportResource(entry, panel.getTopLevelAncestor()); } } - // - // @Override public void close() throws Exception { @@ -213,9 +210,7 @@ public void close() throws Exception viewer.close(); } } - // - // @Override public boolean isReferenceable() { @@ -227,9 +222,7 @@ public void searchReferences(Component parent) { new SongReferenceSearcher(entry, parent); } - // - // @Override public void insertUpdate(DocumentEvent event) { @@ -247,9 +240,7 @@ public void changedUpdate(DocumentEvent event) { setDocumentModified(true); } - // - // @Override public void itemStateChanged(ItemEvent event) { @@ -265,17 +256,13 @@ public void itemStateChanged(ItemEvent event) } } } - // - // @Override public ResourceEntry getResourceEntry() { return entry; } - // - // @Override public String getText() { @@ -315,9 +302,7 @@ public void highlightText(int startOfs, int endOfs) } catch (IllegalArgumentException e) { } } - // - // @Override public JComponent makeViewer(ViewableContainer container) { @@ -340,9 +325,7 @@ public JComponent makeViewer(ViewableContainer container) } return panel; } - // - // @Override public void write(OutputStream os) throws IOException { @@ -352,7 +335,6 @@ public void write(OutputStream os) throws IOException StreamUtils.writeString(os, editor.getText(), editor.getText().length()); } } - // public Viewer getViewer() { diff --git a/src/org/infinity/resource/mus/Viewer.java b/src/org/infinity/resource/mus/Viewer.java index 734a8a942..a6775ec74 100644 --- a/src/org/infinity/resource/mus/Viewer.java +++ b/src/org/infinity/resource/mus/Viewer.java @@ -33,10 +33,10 @@ public class Viewer extends JPanel implements Runnable, ActionListener { - private final SimpleListModel listModel = new SimpleListModel(); + private final SimpleListModel listModel = new SimpleListModel<>(); private final JList list = new JList<>(listModel); private final AudioPlayer player = new AudioPlayer(); - private final List entryList = new Vector(); + private final List entryList = new Vector<>(); private JLabel playList; private JButton bPlay, bEnd, bStop; diff --git a/src/org/infinity/resource/other/FntResource.java b/src/org/infinity/resource/other/FntResource.java index df65d50e3..41001db18 100644 --- a/src/org/infinity/resource/other/FntResource.java +++ b/src/org/infinity/resource/other/FntResource.java @@ -17,13 +17,12 @@ import org.infinity.gui.hexview.ResourceDataProvider; import org.infinity.gui.hexview.StructHexViewer; import org.infinity.resource.AbstractStruct; -import org.infinity.resource.Closeable; import org.infinity.resource.HasViewerTabs; import org.infinity.resource.Resource; import org.infinity.resource.key.ResourceEntry; import org.infinity.util.io.StreamUtils; -public final class FntResource extends AbstractStruct implements Resource, Closeable, HasViewerTabs +public final class FntResource extends AbstractStruct implements Resource, HasViewerTabs { // FNT-specific field labels public static final String FNT_NUM_EXTRA_LETTERS = "# extra letters"; diff --git a/src/org/infinity/resource/other/TtfResource.java b/src/org/infinity/resource/other/TtfResource.java index b9fc1c7dc..3d976475f 100644 --- a/src/org/infinity/resource/other/TtfResource.java +++ b/src/org/infinity/resource/other/TtfResource.java @@ -61,8 +61,7 @@ public TtfResource(ResourceEntry entry) throws Exception { this.entry = entry; - try { - InputStream is = this.entry.getResourceDataAsStream(); + try (InputStream is = this.entry.getResourceDataAsStream()) { font = Font.createFont(Font.TRUETYPE_FONT, is); } catch (Exception e) { font = null; diff --git a/src/org/infinity/resource/pro/ProAreaType.java b/src/org/infinity/resource/pro/ProAreaType.java index 696cb8bcc..7c9b3c418 100644 --- a/src/org/infinity/resource/pro/ProAreaType.java +++ b/src/org/infinity/resource/pro/ProAreaType.java @@ -5,23 +5,27 @@ package org.infinity.resource.pro; import java.nio.ByteBuffer; +import java.util.TreeMap; import org.infinity.datatype.ColorValue; import org.infinity.datatype.DecNumber; import org.infinity.datatype.Flag; import org.infinity.datatype.HashBitmap; import org.infinity.datatype.IdsBitmap; +import org.infinity.datatype.IsNumeric; import org.infinity.datatype.ProRef; import org.infinity.datatype.ResourceRef; import org.infinity.datatype.Unknown; import org.infinity.datatype.UnsignDecNumber; +import org.infinity.datatype.UpdateEvent; +import org.infinity.datatype.UpdateListener; import org.infinity.resource.AbstractStruct; import org.infinity.resource.AddRemovable; import org.infinity.resource.Profile; -import org.infinity.util.LongIntegerHashMap; +import org.infinity.resource.StructEntry; import org.infinity.util.io.StreamUtils; -public final class ProAreaType extends AbstractStruct implements AddRemovable +public final class ProAreaType extends AbstractStruct implements AddRemovable, UpdateListener { // PRO/Area-specific field labels public static final String PRO_AREA = "Area effect info"; @@ -39,6 +43,8 @@ public final class ProAreaType extends AbstractStruct implements AddRemovable public static final String PRO_AREA_EXPLOSION_PROJECTILE = "Explosion projectile"; public static final String PRO_AREA_EXPLOSION_ANIMATION = "Explosion animation"; public static final String PRO_AREA_CONE_WIDTH = "Cone width"; + public static final String PRO_AREA_ANGLE_BETWEEN_RAYS = "Angle between rays"; + public static final String PRO_AREA_ROTATE_RAYS = "Rotate rays clockwise"; public static final String PRO_AREA_SPREAD_ANIMATION = "Spread animation"; public static final String PRO_AREA_RING_ANIMATION = "Ring animation"; public static final String PRO_AREA_SOUND = "Area sound"; @@ -48,7 +54,7 @@ public final class ProAreaType extends AbstractStruct implements AddRemovable public static final String PRO_AREA_ANIMATION_GRANULARITY = "Animation granularity"; public static final String PRO_AREA_ANIMATION_GRANULARITY_DIVIDER = "Animation granularity divider"; - public static final LongIntegerHashMap m_proj = new LongIntegerHashMap(); + public static final TreeMap m_proj = new TreeMap<>(); public static final String[] s_areaflags = {"Trap not visible", "Trap visible", "Triggered by inanimates", "Triggered by condition", "Delayed trigger", "Secondary projectile", "Fragments", "Affect only enemies", "Affect only allies*;Only in combination with \"Affect only enemies (6)\"", @@ -97,19 +103,55 @@ public ProAreaType(AbstractStruct superStruct, ByteBuffer buffer, int offset) th super(superStruct, PRO_AREA, buffer, offset); } - // @Override public boolean canRemove() { return false; // can not be removed manually } - // + + @Override + public boolean valueUpdated(UpdateEvent event) + { + boolean retVal = false; + if (event.getSource() instanceof IsNumeric && ((StructEntry)event.getSource()).getName().equals(PRO_AREA_RAY_COUNT)) { + int rayCount = ((IsNumeric)event.getSource()).getValue(); + AbstractStruct struct = event.getStructure(); + if (rayCount > 0) { + StructEntry field = struct.getField(null, 0x224); + if (field instanceof DecNumber && !field.getName().equals(PRO_AREA_ANGLE_BETWEEN_RAYS)) { + field.setName(PRO_AREA_ANGLE_BETWEEN_RAYS); + retVal = true; + } + field = struct.getField(null, 0x226); + if (field instanceof DecNumber && !field.getName().equals(PRO_AREA_ROTATE_RAYS)) { + field.setName(PRO_AREA_ROTATE_RAYS); + retVal = true; + } + } else { + StructEntry field = struct.getField(null, 0x224); + if (field instanceof DecNumber && !field.getName().equals(PRO_AREA_CONE_WIDTH)) { + field.setName(PRO_AREA_CONE_WIDTH); + retVal = true; + } + field = struct.getField(null, 0x226); + if (field instanceof DecNumber && !field.getName().equals(COMMON_UNUSED)) { + field.setName(COMMON_UNUSED); + retVal = true; + } + } + } + return retVal; + } @Override public int read(ByteBuffer buffer, int offset) throws Exception { addField(new Flag(buffer, offset, 2, PRO_AREA_FLAGS, s_areaflags)); - addField(new DecNumber(buffer, offset + 2, 2, PRO_AREA_RAY_COUNT)); + DecNumber rayCount = new DecNumber(buffer, offset + 2, 2, PRO_AREA_RAY_COUNT); + if (Profile.isEnhancedEdition()) { + rayCount.addUpdateListener(this); + } + addField(rayCount); addField(new DecNumber(buffer, offset + 4, 2, PRO_AREA_TRAP_SIZE)); addField(new DecNumber(buffer, offset + 6, 2, PRO_AREA_EXPLOSION_SIZE)); addField(new ResourceRef(buffer, offset + 8, PRO_AREA_EXPLOSION_SOUND, "WAV")); @@ -118,13 +160,21 @@ public int read(ByteBuffer buffer, int offset) throws Exception addField(new ProRef(buffer, offset + 20, PRO_AREA_SECONDARY_PROJECTILE, false)); addField(new DecNumber(buffer, offset + 22, 1, PRO_AREA_NUM_REPETITIONS)); addField(new HashBitmap(buffer, offset + 23, 1, PRO_AREA_EXPLOSION_EFFECT, m_proj)); - addField(new ColorValue(buffer, offset + 24, 1, PRO_AREA_EXPLOSION_COLOR)); + addField(new ColorValue(buffer, offset + 24, 1, PRO_AREA_EXPLOSION_COLOR, false)); addField(new Unknown(buffer, offset + 25, 1, COMMON_UNUSED)); addField(new ProRef(buffer, offset + 26, PRO_AREA_EXPLOSION_PROJECTILE)); addField(new ResourceRef(buffer, offset + 28, PRO_AREA_EXPLOSION_ANIMATION, "VEF", "VVC", "BAM")); - addField(new DecNumber(buffer, offset + 36, 2, PRO_AREA_CONE_WIDTH)); + if (Profile.isEnhancedEdition() && rayCount.getValue() > 0) { + addField(new DecNumber(buffer, offset + 36, 2, PRO_AREA_ANGLE_BETWEEN_RAYS)); + } else { + addField(new DecNumber(buffer, offset + 36, 2, PRO_AREA_CONE_WIDTH)); + } if (Profile.isEnhancedEdition()) { - addField(new Unknown(buffer, offset + 38, 2)); + if (rayCount.getValue() > 0) { + addField(new DecNumber(buffer, offset + 38, 2, PRO_AREA_ROTATE_RAYS)); + } else { + addField(new DecNumber(buffer, offset + 38, 2, COMMON_UNUSED)); + } addField(new ResourceRef(buffer, offset + 40, PRO_AREA_SPREAD_ANIMATION, "BAM")); addField(new ResourceRef(buffer, offset + 48, PRO_AREA_RING_ANIMATION, "BAM")); addField(new ResourceRef(buffer, offset + 56, PRO_AREA_SOUND, "WAV")); diff --git a/src/org/infinity/resource/pro/ProResource.java b/src/org/infinity/resource/pro/ProResource.java index c6df6b946..fee61f8d2 100644 --- a/src/org/infinity/resource/pro/ProResource.java +++ b/src/org/infinity/resource/pro/ProResource.java @@ -5,6 +5,7 @@ package org.infinity.resource.pro; import java.nio.ByteBuffer; +import java.util.TreeMap; import javax.swing.JComponent; @@ -14,6 +15,7 @@ import org.infinity.datatype.Flag; import org.infinity.datatype.HashBitmap; import org.infinity.datatype.IdsTargetType; +import org.infinity.datatype.IsNumeric; import org.infinity.datatype.ProRef; import org.infinity.datatype.ResourceBitmap; import org.infinity.datatype.ResourceRef; @@ -36,7 +38,6 @@ import org.infinity.resource.StructEntry; import org.infinity.resource.key.ResourceEntry; import org.infinity.search.SearchOptions; -import org.infinity.util.LongIntegerHashMap; /** * This resource describes projectiles, and the files are referenced spells and @@ -96,7 +97,7 @@ public final class ProResource extends AbstractStruct implements Resource, HasVi "Touch projectile", "Negate IDS1", "Negate IDS2", "Use either IDS", "Delayed payload", "Limited path count", "IWD style check", "Caster affected"}; - public static final LongIntegerHashMap m_projtype = new LongIntegerHashMap(); + public static final TreeMap m_projtype = new TreeMap<>(); static { m_projtype.put(1L, "No BAM"); m_projtype.put(2L, "Single target"); @@ -131,7 +132,6 @@ public ProResource(ResourceEntry entry) throws Exception super(entry); } - // @Override public boolean valueUpdated(UpdateEvent event) { @@ -149,9 +149,9 @@ public boolean valueUpdated(UpdateEvent event) } return bRet; } - else if (event.getSource() instanceof HashBitmap && + else if (event.getSource() instanceof IsNumeric && ((StructEntry)event.getSource()).getName().equals(PRO_TYPE)) { - HashBitmap proType = (HashBitmap)event.getSource(); + IsNumeric proType = (IsNumeric)event.getSource(); AbstractStruct struct = event.getStructure(); // add/remove extended sections in the parent structure depending on the current value if (struct instanceof Resource && struct instanceof HasChildStructs) { @@ -197,9 +197,7 @@ else if (event.getSource() instanceof HashBitmap && } return false; } - // - // @Override public int getViewerTabCount() { @@ -229,9 +227,7 @@ public boolean viewerTabAddedBefore(int index) { return false; } - // - // @Override public int read(ByteBuffer buffer, int offset) throws Exception { @@ -305,9 +301,7 @@ public int read(ByteBuffer buffer, int offset) throws Exception return offset; } - // - // @Override protected void viewerInitialized(StructViewer viewer) { @@ -347,7 +341,6 @@ protected void datatypeRemovedInChild(AbstractStruct child, AddRemovable datatyp hexViewer.dataModified(); } } - // /** Updates current IDS targeting to IWD style and returns true if changes have been made. */ private boolean setIwdStyleIdsType(AbstractStruct struct, int offset, int nr) diff --git a/src/org/infinity/resource/pro/ProSingleType.java b/src/org/infinity/resource/pro/ProSingleType.java index ab8bde41b..4db1d8ed8 100644 --- a/src/org/infinity/resource/pro/ProSingleType.java +++ b/src/org/infinity/resource/pro/ProSingleType.java @@ -5,6 +5,7 @@ package org.infinity.resource.pro; import java.nio.ByteBuffer; +import java.util.TreeMap; import org.infinity.datatype.ColorValue; import org.infinity.datatype.DecNumber; @@ -15,7 +16,6 @@ import org.infinity.datatype.Unknown; import org.infinity.resource.AbstractStruct; import org.infinity.resource.AddRemovable; -import org.infinity.util.LongIntegerHashMap; import org.infinity.util.io.StreamUtils; public final class ProSingleType extends AbstractStruct implements AddRemovable @@ -40,7 +40,7 @@ public final class ProSingleType extends AbstractStruct implements AddRemovable public static final String PRO_SINGLE_TRAILING_ANIMATION_DELAY_FMT = "Trailing animation delay %d"; public static final String PRO_SINGLE_TRAIL_FLAGS = "Trail flags"; - public static final LongIntegerHashMap m_facetarget = new LongIntegerHashMap(); + public static final TreeMap m_facetarget = new TreeMap<>(); public static final String[] s_flags = {"No flags set", "Colored BAM", "Creates smoke", "Colored smoke", "Not light source", "Modify for height", "Casts shadow", "Light spot enabled", "Translucent", "Mid-level brighten", "Blended"}; @@ -90,11 +90,11 @@ public int read(ByteBuffer buffer, int offset) throws Exception addField(new DecNumber(buffer, offset + 26, 2, PRO_SINGLE_LIGHT_SPOT_HEIGHT)); addField(new ResourceRef(buffer, offset + 28, PRO_SINGLE_PALETTE, "BMP")); for (int i = 0; i < 7; i++) { - addField(new ColorValue(buffer, offset + 36 + i, 1, String.format(PRO_SINGLE_PRO_COLOR_FMT, i+1))); + addField(new ColorValue(buffer, offset + 36 + i, 1, String.format(PRO_SINGLE_PRO_COLOR_FMT, i+1), false)); } addField(new DecNumber(buffer, offset + 43, 1, PRO_SINGLE_SMOKE_PUFF_DELAY)); for (int i = 0; i < 7; i++) { - addField(new ColorValue(buffer, offset + 44 + i, 1, String.format(PRO_SINGLE_SMOKE_COLOR_FMT, i+1))); + addField(new ColorValue(buffer, offset + 44 + i, 1, String.format(PRO_SINGLE_SMOKE_COLOR_FMT, i+1), false)); } addField(new HashBitmap(buffer, offset + 51, 1, PRO_SINGLE_FACE_TARGET_GRANULARITY, m_facetarget)); addField(new IdsBitmap(buffer, offset + 52, 2, PRO_SINGLE_SMOKE_ANIMATION, "ANIMATE.IDS")); diff --git a/src/org/infinity/resource/sav/IOHandler.java b/src/org/infinity/resource/sav/IOHandler.java index d0fc5044e..d5536bf8e 100644 --- a/src/org/infinity/resource/sav/IOHandler.java +++ b/src/org/infinity/resource/sav/IOHandler.java @@ -103,6 +103,7 @@ public List decompress() throws Exception } Files.createDirectory(tempFolder); + // placing content of .sav resource in the temporary folder final List entries = new ArrayList<>(fileEntries.size()); for (final SavResourceEntry entry: fileEntries) { Path file = tempFolder.resolve(entry.getResourceName()); @@ -111,6 +112,15 @@ public List decompress() throws Exception } entries.add(new FileResourceEntry(file)); } + + // placing copy of associated .gam resource in the temporary folder + String gamFile = Profile.getProperty(Profile.Key.GET_GAM_NAME); + Path srcFile = entry.getActualPath().getParent().resolve(gamFile); + if (Files.isRegularFile(srcFile)) { + Path dstFile = tempFolder.resolve(gamFile); + Files.copy(srcFile, dstFile); + } + return entries; } diff --git a/src/org/infinity/resource/sound/AudioPlayer.java b/src/org/infinity/resource/sound/AudioPlayer.java index d1e8f9040..20bb1793d 100644 --- a/src/org/infinity/resource/sound/AudioPlayer.java +++ b/src/org/infinity/resource/sound/AudioPlayer.java @@ -33,31 +33,27 @@ public void play(AudioBuffer audioBuffer) throws Exception setPlaying(true); setStopped(false); - AudioInputStream ais = null; - try { - ais = AudioSystem.getAudioInputStream(new ByteArrayInputStream(audioBuffer.getAudioData())); - } catch (UnsupportedAudioFileException e) { - throw new Exception("Unsupported audio format"); - } - - if (dataLine == null || !ais.getFormat().matches(audioFormat)) { - audioFormat = ais.getFormat(); - DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat); - if (!AudioSystem.isLineSupported(info)) { - throw new Exception("Unsupported audio format"); + try (AudioInputStream ais = AudioSystem.getAudioInputStream(new ByteArrayInputStream(audioBuffer.getAudioData()))) { + if (dataLine == null || !ais.getFormat().matches(audioFormat)) { + audioFormat = ais.getFormat(); + DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat); + if (!AudioSystem.isLineSupported(info)) { + throw new UnsupportedAudioFileException("Unsupported audio format"); + } + dataLine = (SourceDataLine)AudioSystem.getLine(info); + dataLine.open(ais.getFormat(), 16384); } - dataLine = (SourceDataLine)AudioSystem.getLine(info); - dataLine.open(ais.getFormat(), 16384); - } - dataLine.start(); + dataLine.start(); - while (isPlaying()) { - int numBytesRead = ais.read(buffer, 0, buffer.length); - if (numBytesRead < 0) - break; - dataLine.write(buffer, 0, numBytesRead); + while (isPlaying()) { + int numBytesRead = ais.read(buffer, 0, buffer.length); + if (numBytesRead < 0) + break; + dataLine.write(buffer, 0, numBytesRead); + } + } catch (UnsupportedAudioFileException e) { + throw new UnsupportedAudioFileException("Unsupported audio format"); } - ais.close(); if (!isPlaying()) { dataLine.drain(); diff --git a/src/org/infinity/resource/sound/WavBuffer.java b/src/org/infinity/resource/sound/WavBuffer.java index 756ce74f6..610a0450a 100644 --- a/src/org/infinity/resource/sound/WavBuffer.java +++ b/src/org/infinity/resource/sound/WavBuffer.java @@ -152,7 +152,7 @@ private static class WaveFmt private static final short ID_TYPE_PCM = 0x01; // PCM private static final short ID_TYPE_ADPCM = 0x11; // IMA ADPCM private static final HashSet s_audioTypes = - new HashSet(Arrays.asList(new Short[]{ID_TYPE_PCM, ID_TYPE_ADPCM})); + new HashSet<>(Arrays.asList(new Short[]{ID_TYPE_PCM, ID_TYPE_ADPCM})); private final AudioOverride override; private int sampleRate, samplesPerBlock, samplesPerChannel, dataSize; diff --git a/src/org/infinity/resource/spl/Ability.java b/src/org/infinity/resource/spl/Ability.java index 7a3977684..3d5fc45bc 100644 --- a/src/org/infinity/resource/spl/Ability.java +++ b/src/org/infinity/resource/spl/Ability.java @@ -32,6 +32,13 @@ public final class Ability extends AbstractAbility implements AddRemovable, HasC public static final String SPL_ABIL = "Spell ability"; public static final String SPL_ABIL_MIN_LEVEL = "Minimum level"; public static final String SPL_ABIL_CASTING_SPEED = "Casting speed"; + public static final String SPL_ABIL_TIMES_PER_DAY = "Times per day"; + public static final String SPL_ABIL_DICE_SIZE = AbstractAbility.ABILITY_DICE_SIZE + SUFFIX_UNUSED; + public static final String SPL_ABIL_DICE_COUNT = AbstractAbility.ABILITY_DICE_COUNT + SUFFIX_UNUSED; + public static final String SPL_ABIL_DAMAGE_BONUS = AbstractAbility.ABILITY_DAMAGE_BONUS + SUFFIX_UNUSED; + public static final String SPL_ABIL_DAMAGE_TYPE = AbstractAbility.ABILITY_DAMAGE_TYPE + SUFFIX_UNUSED; + public static final String SPL_ABIL_NUM_CHARGES = AbstractAbility.ABILITY_NUM_CHARGES + SUFFIX_UNUSED; + public static final String SPL_ABIL_WHEN_DRAINED = AbstractAbility.ABILITY_WHEN_DRAINED + SUFFIX_UNUSED; Ability() throws Exception { @@ -43,7 +50,6 @@ public final class Ability extends AbstractAbility implements AddRemovable, HasC super(superStruct, SPL_ABIL + " " + number, buffer, offset); } - // @Override public AddRemovable[] getPrototypes() throws Exception { @@ -55,17 +61,13 @@ public AddRemovable confirmAddEntry(AddRemovable entry) throws Exception { return entry; } - // - // @Override public boolean canRemove() { return true; } - // - // @Override public int getViewerTabCount() { @@ -89,9 +91,7 @@ public boolean viewerTabAddedBefore(int index) { return true; } - // - // @Override public int read(ByteBuffer buffer, int offset) throws Exception { @@ -104,15 +104,15 @@ public int read(ByteBuffer buffer, int offset) throws Exception addField(new DecNumber(buffer, offset + 14, 2, ABILITY_RANGE)); addField(new DecNumber(buffer, offset + 16, 2, SPL_ABIL_MIN_LEVEL)); addField(new DecNumber(buffer, offset + 18, 2, SPL_ABIL_CASTING_SPEED)); - addField(new DecNumber(buffer, offset + 20, 2, ABILITY_HIT_BONUS)); - addField(new DecNumber(buffer, offset + 22, 2, ABILITY_DICE_SIZE)); - addField(new DecNumber(buffer, offset + 24, 2, ABILITY_DICE_COUNT)); - addField(new DecNumber(buffer, offset + 26, 2, ABILITY_DAMAGE_BONUS)); - addField(new Bitmap(buffer, offset + 28, 2, ABILITY_DAMAGE_TYPE, s_dmgtype)); + addField(new DecNumber(buffer, offset + 20, 2, SPL_ABIL_TIMES_PER_DAY)); + addField(new DecNumber(buffer, offset + 22, 2, SPL_ABIL_DICE_SIZE)); + addField(new DecNumber(buffer, offset + 24, 2, SPL_ABIL_DICE_COUNT)); + addField(new DecNumber(buffer, offset + 26, 2, SPL_ABIL_DAMAGE_BONUS)); + addField(new DecNumber(buffer, offset + 28, 2, SPL_ABIL_DAMAGE_TYPE)); addField(new SectionCount(buffer, offset + 30, 2, ABILITY_NUM_EFFECTS, Effect.class)); addField(new DecNumber(buffer, offset + 32, 2, ABILITY_FIRST_EFFECT_INDEX)); - addField(new DecNumber(buffer, offset + 34, 2, ABILITY_NUM_CHARGES)); - addField(new Bitmap(buffer, offset + 36, 2, ABILITY_WHEN_DRAINED, s_drain)); + addField(new DecNumber(buffer, offset + 34, 2, SPL_ABIL_NUM_CHARGES)); + addField(new DecNumber(buffer, offset + 36, 2, SPL_ABIL_WHEN_DRAINED)); if (ResourceFactory.resourceExists("PROJECTL.IDS")) { addField(new ProRef(buffer, offset + 38, ABILITY_PROJECTILE)); } else if (Profile.getEngine() == Profile.Engine.PST) { @@ -124,6 +124,5 @@ public int read(ByteBuffer buffer, int offset) throws Exception } return offset + 40; } - // } diff --git a/src/org/infinity/resource/spl/SplResource.java b/src/org/infinity/resource/spl/SplResource.java index 5e20b4136..2a0d35c0d 100644 --- a/src/org/infinity/resource/spl/SplResource.java +++ b/src/org/infinity/resource/spl/SplResource.java @@ -16,9 +16,11 @@ import javax.swing.JComponent; import javax.swing.JScrollPane; +import org.infinity.datatype.AbstractBitmap; import org.infinity.datatype.Bitmap; import org.infinity.datatype.DecNumber; import org.infinity.datatype.Flag; +import org.infinity.datatype.IsNumeric; import org.infinity.datatype.PriTypeBitmap; import org.infinity.datatype.ResourceRef; import org.infinity.datatype.SecTypeBitmap; @@ -67,7 +69,7 @@ public final class SplResource extends AbstractStruct implements Resource, HasCh { // SPL-specific field labels public static final String SPL_NAME = "Spell name"; - public static final String SPL_NAME_IDENTIFIED = org.infinity.resource.itm.ItmResource.ITM_NAME_IDENTIFIED; + public static final String SPL_NAME_IDENTIFIED = org.infinity.resource.itm.ItmResource.ITM_NAME_IDENTIFIED + SUFFIX_UNUSED; public static final String SPL_CASTING_SOUND = "Casting sound"; public static final String SPL_FLAGS = "Flags"; public static final String SPL_TYPE = "Spell type"; @@ -80,7 +82,7 @@ public final class SplResource extends AbstractStruct implements Resource, HasCh public static final String SPL_ICON = "Spell icon"; public static final String SPL_ICON_GROUND = "Ground icon"; public static final String SPL_DESCRIPTION = "Spell description"; - public static final String SPL_DESCRIPTION_IDENTIFIED = org.infinity.resource.itm.ItmResource.ITM_DESCRIPTION_IDENTIFIED; + public static final String SPL_DESCRIPTION_IDENTIFIED = org.infinity.resource.itm.ItmResource.ITM_DESCRIPTION_IDENTIFIED + SUFFIX_UNUSED; public static final String SPL_DESCRIPTION_IMAGE = org.infinity.resource.itm.ItmResource.ITM_DESCRIPTION_IMAGE; public static final String SPL_OFFSET_ABILITIES = org.infinity.resource.itm.ItmResource.ITM_OFFSET_ABILITIES; public static final String SPL_NUM_ABILITIES = org.infinity.resource.itm.ItmResource.ITM_NUM_ABILITIES; @@ -108,10 +110,15 @@ public final class SplResource extends AbstractStruct implements Resource, HasCh "Divination", "Illusion", "Invocation", "Necromancy", "Innate"}; public static final String[] s_spellflag = {"No flags set", "", "", "", "", "", "", "", "", - "", "EE: Break Sanctuary", "Hostile", "No LOS required", + "", "EE: Break Sanctuary/Invisibility", "Hostile", "No LOS required", "Allow spotting", "Outdoors only", "Ignore dead/wild magic", "Ignore wild surge", "Non-combat ability", "", "", "", "", "", "", "", "EE/Ex: Can target invisible", "EE/Ex: Castable when silenced"}; + public static final String[] s_spellflag2 = {"No flags set", "", "", "", "", "", "", "", "", + "", "", "Hostile", "No LOS required", + "Allow spotting", "Outdoors only", "Simplified duration", + "Trigger/Contingency", "", "", "Non-combat ability (?)", "", "", "", + "", "", "", ""}; public static final String[] s_exclude = { "None", "Berserker", "Wizard slayer", "Kensai", "Cavalier", "Inquisitor", "Undead hunter", @@ -154,7 +161,6 @@ public SplResource(ResourceEntry entry) throws Exception super(entry); } - // @Override public AddRemovable[] getPrototypes() throws Exception { @@ -166,9 +172,7 @@ public AddRemovable confirmAddEntry(AddRemovable entry) throws Exception { return entry; } - // - // @Override public int getViewerTabCount() { @@ -213,9 +217,7 @@ public boolean viewerTabAddedBefore(int index) { return (index == 0); } - // - // @Override public void write(OutputStream os) throws IOException { @@ -227,17 +229,15 @@ public void write(OutputStream os) throws IOException } } } - // - // @Override public boolean valueUpdated(UpdateEvent event) { - if (event.getSource() instanceof Bitmap && - SPL_TYPE.equals(((Bitmap)event.getSource()).getName())) { + if (event.getSource() instanceof AbstractBitmap && + SPL_TYPE.equals(((AbstractBitmap)event.getSource()).getName())) { Flag curFlags = (Flag)getAttribute(SPL_EXCLUSION_FLAGS); if (curFlags != null) { - int type = ((Bitmap)event.getSource()).getValue(); + int type = ((IsNumeric)event.getSource()).getValue(); int size = curFlags.getSize(); int offset = curFlags.getOffset(); ByteBuffer b = ByteBuffer.allocate(size).order(ByteOrder.LITTLE_ENDIAN).putInt(curFlags.getValue()); @@ -249,9 +249,7 @@ public boolean valueUpdated(UpdateEvent event) } return false; } - // - // @Override protected void viewerInitialized(StructViewer viewer) { @@ -268,7 +266,7 @@ protected void datatypeAdded(AddRemovable datatype) } } else if (datatype instanceof Ability) { - int effect_count = ((SectionCount)getAttribute(SPL_NUM_GLOBAL_EFFECTS)).getValue(); + int effect_count = ((IsNumeric)getAttribute(SPL_NUM_GLOBAL_EFFECTS)).getValue(); for (final StructEntry o : getFields()) { if (o instanceof Ability) { Ability ability = (Ability)o; @@ -299,7 +297,7 @@ protected void datatypeRemoved(AddRemovable datatype) } } else if (datatype instanceof Ability) { - int effect_count = ((SectionCount)getAttribute(SPL_NUM_GLOBAL_EFFECTS)).getValue(); + int effect_count = ((IsNumeric)getAttribute(SPL_NUM_GLOBAL_EFFECTS)).getValue(); for (final StructEntry o : getFields()) { if (o instanceof Ability) { Ability ability = (Ability)o; @@ -319,9 +317,7 @@ protected void datatypeRemovedInChild(AbstractStruct child, AddRemovable datatyp super.datatypeRemovedInChild(child, datatype); incAbilityEffects(child, datatype, -1); } - // - // @Override public int read(ByteBuffer buffer, int offset) throws Exception { @@ -331,7 +327,11 @@ public int read(ByteBuffer buffer, int offset) throws Exception addField(new StringRef(buffer, offset + 8, SPL_NAME)); addField(new StringRef(buffer, offset + 12, SPL_NAME_IDENTIFIED)); addField(new ResourceRef(buffer, offset + 16, SPL_CASTING_SOUND, "WAV")); - addField(new Flag(buffer, offset + 24, 4, SPL_FLAGS, s_spellflag)); + if (version.getText().equalsIgnoreCase("V2.0")) { + addField(new Flag(buffer, offset + 24, 4, SPL_FLAGS, s_spellflag2)); + } else { + addField(new Flag(buffer, offset + 24, 4, SPL_FLAGS, s_spellflag)); + } Bitmap spellType = new Bitmap(buffer, offset + 28, 2, SPL_TYPE, s_spelltype); // 0x1c spellType.addUpdateListener(this); addField(spellType); @@ -397,7 +397,6 @@ public int read(ByteBuffer buffer, int offset) throws Exception return Math.max(offset, offset2); } - // private void incAbilityEffects(StructEntry child, AddRemovable datatype, int value) { @@ -433,8 +432,8 @@ public static boolean matchSearchOptions(ResourceEntry entry, SearchOptions sear Object o; // preparing substructures - DecNumber ofs = (DecNumber)spl.getAttribute(SPL_OFFSET_EFFECTS, false); - DecNumber cnt = (DecNumber)spl.getAttribute(SPL_NUM_GLOBAL_EFFECTS, false); + IsNumeric ofs = (IsNumeric)spl.getAttribute(SPL_OFFSET_EFFECTS, false); + IsNumeric cnt = (IsNumeric)spl.getAttribute(SPL_NUM_GLOBAL_EFFECTS, false); if (ofs != null && ofs.getValue() > 0 && cnt != null && cnt.getValue() > 0) { effects = new Effect[cnt.getValue()]; for (int idx = 0; idx < cnt.getValue(); idx++) { @@ -445,8 +444,8 @@ public static boolean matchSearchOptions(ResourceEntry entry, SearchOptions sear effects = new Effect[0]; } - ofs = (DecNumber)spl.getAttribute(SPL_OFFSET_ABILITIES, false); - cnt = (DecNumber)spl.getAttribute(SPL_NUM_ABILITIES, false); + ofs = (IsNumeric)spl.getAttribute(SPL_OFFSET_ABILITIES, false); + cnt = (IsNumeric)spl.getAttribute(SPL_NUM_ABILITIES, false); if (ofs != null && ofs.getValue() > 0 && cnt != null && cnt.getValue() > 0) { abilities = new Ability[cnt.getValue()]; for (int idx = 0; idx < cnt.getValue(); idx++) { @@ -460,7 +459,7 @@ public static boolean matchSearchOptions(ResourceEntry entry, SearchOptions sear abilityEffects = new Effect[abilities.length][]; for (int idx = 0; idx < abilities.length; idx++) { if (abilities[idx] != null) { - cnt = (DecNumber)abilities[idx].getAttribute(AbstractAbility.ABILITY_NUM_EFFECTS, false); + cnt = (IsNumeric)abilities[idx].getAttribute(AbstractAbility.ABILITY_NUM_EFFECTS, false); if (cnt != null && cnt.getValue() > 0) { abilityEffects[idx] = new Effect[cnt.getValue()]; for (int idx2 = 0; idx2 < cnt.getValue(); idx2++) { diff --git a/src/org/infinity/resource/spl/Viewer.java b/src/org/infinity/resource/spl/Viewer.java index 303188b52..ce2fbf65e 100644 --- a/src/org/infinity/resource/spl/Viewer.java +++ b/src/org/infinity/resource/spl/Viewer.java @@ -28,8 +28,8 @@ public final class Viewer extends JPanel { - private static final HashMap SpellPrefix = new HashMap(); - private static final HashMap SpellType = new HashMap(); + private static final HashMap SpellPrefix = new HashMap<>(); + private static final HashMap SpellType = new HashMap<>(); static { SpellPrefix.put(Integer.valueOf(0), "MARW"); SpellPrefix.put(Integer.valueOf(1), "SPPR"); diff --git a/src/org/infinity/resource/src/SrcResource.java b/src/org/infinity/resource/src/SrcResource.java index c27d70416..c79421f19 100644 --- a/src/org/infinity/resource/src/SrcResource.java +++ b/src/org/infinity/resource/src/SrcResource.java @@ -43,7 +43,6 @@ public SrcResource(ResourceEntry entry) throws Exception super(entry); } - // @Override public AddRemovable[] getPrototypes() throws Exception { @@ -55,9 +54,7 @@ public AddRemovable confirmAddEntry(AddRemovable entry) throws Exception { return entry; } - // - // @Override public int getViewerTabCount() { @@ -86,9 +83,7 @@ public boolean viewerTabAddedBefore(int index) { return false; } - // - // @Override public int read(ByteBuffer buffer, int offset) throws Exception { @@ -102,9 +97,7 @@ public int read(ByteBuffer buffer, int offset) throws Exception } return offset; } - // - // @Override protected void viewerInitialized(StructViewer viewer) { @@ -144,5 +137,4 @@ protected void datatypeRemovedInChild(AbstractStruct child, AddRemovable datatyp hexViewer.dataModified(); } } - // } diff --git a/src/org/infinity/resource/sto/StoResource.java b/src/org/infinity/resource/sto/StoResource.java index 92d63e06e..f2d35b5b3 100644 --- a/src/org/infinity/resource/sto/StoResource.java +++ b/src/org/infinity/resource/sto/StoResource.java @@ -15,6 +15,7 @@ import org.infinity.datatype.Bitmap; import org.infinity.datatype.DecNumber; import org.infinity.datatype.Flag; +import org.infinity.datatype.IsNumeric; import org.infinity.datatype.ResourceRef; import org.infinity.datatype.SectionCount; import org.infinity.datatype.SectionOffset; @@ -79,11 +80,11 @@ public final class StoResource extends AbstractStruct implements Resource, HasCh // private static final String[] s_flag = {"Can't do anything", "Can buy", "Can sell", "Can identify", // "Can steal", "Can buy cures", "Can donate", // "Can buy drinks", "", "", "Quality Bit 0 (BAM)", "Quality Bit 1 (BAM)"}; - public static final String[] s_flag_bg2 = {"Can only rest", "Can buy", "Can sell", "Can identify", - "Can steal", "Can donate;Unused in Enhanced Editions", "Can buy cures", - "Can buy drinks", null, "EE: Disable donation screen;Disables donation screen in temple stores", - "Tavern quality 1", "Tavern quality 2", null, "Fence", "EE: Ignore reputation", - "Ex: Toggle recharge", "EE: Can sell critical"}; + public static final String[] s_flag_bg2 = {"User can only rest", "User can buy", "User can sell", "User can identify", + "User can steal", "User can donate;Unused in Enhanced Editions", "User can purchase cures", + "User can purchase drinks", null, "EE: Disable donation screen;Disables donation screen in temple stores", + "Tavern quality 1", "Tavern quality 2", null, "User can sell stolen goods", "EE: Ignore reputation", + "Ex: Toggle item recharge", "EE: User can sell critical items"}; public static final String[] s_rooms = {"No rooms available", "Peasant", "Merchant", "Noble", "Royal"}; private StructHexViewer hexViewer; @@ -99,7 +100,6 @@ public StoResource(ResourceEntry entry) throws Exception super(entry); } - // @Override public AddRemovable[] getPrototypes() throws Exception { @@ -115,9 +115,7 @@ public AddRemovable confirmAddEntry(AddRemovable entry) throws Exception { return entry; } - // - // @Override public int getViewerTabCount() { @@ -162,9 +160,7 @@ public boolean viewerTabAddedBefore(int index) { return (index == 0); } - // - // @Override public int read(ByteBuffer buffer, int offset) throws Exception { @@ -289,9 +285,7 @@ else if (version.toString().equals("V1.1")) { } return endoffset; } - // - // @Override protected void viewerInitialized(StructViewer viewer) { @@ -331,7 +325,6 @@ protected void datatypeRemovedInChild(AbstractStruct child, AddRemovable datatyp hexViewer.dataModified(); } } - // /** * Checks whether the specified resource entry matches all available search options. @@ -349,8 +342,8 @@ public static boolean matchSearchOptions(ResourceEntry entry, SearchOptions sear Object o; // preparations - DecNumber ofs = (DecNumber)sto.getAttribute(STO_OFFSET_ITEMS_FOR_SALE, false); - DecNumber cnt = (DecNumber)sto.getAttribute(STO_NUM_ITEMS_FOR_SALE, false); + IsNumeric ofs = (IsNumeric)sto.getAttribute(STO_OFFSET_ITEMS_FOR_SALE, false); + IsNumeric cnt = (IsNumeric)sto.getAttribute(STO_NUM_ITEMS_FOR_SALE, false); if (ofs != null && ofs.getValue() > 0 && cnt != null && cnt.getValue() > 0) { String itemLabel = SearchOptions.getResourceName(SearchOptions.STO_Item_Item1); items = new ResourceRef[cnt.getValue()]; @@ -376,8 +369,8 @@ public static boolean matchSearchOptions(ResourceEntry entry, SearchOptions sear items = new ResourceRef[0]; } - ofs = (DecNumber)sto.getAttribute(STO_OFFSET_ITEMS_PURCHASED, false); - cnt = (DecNumber)sto.getAttribute(STO_NUM_ITEMS_PURCHASED, false); + ofs = (IsNumeric)sto.getAttribute(STO_OFFSET_ITEMS_PURCHASED, false); + cnt = (IsNumeric)sto.getAttribute(STO_NUM_ITEMS_PURCHASED, false); if (ofs != null && ofs.getValue() > 0 && cnt != null && cnt.getValue() > 0) { purchases = new Bitmap[cnt.getValue()]; for (int i = 0; i < cnt.getValue(); i++) { diff --git a/src/org/infinity/resource/text/PlainTextResource.java b/src/org/infinity/resource/text/PlainTextResource.java index 724839df2..029410413 100644 --- a/src/org/infinity/resource/text/PlainTextResource.java +++ b/src/org/infinity/resource/text/PlainTextResource.java @@ -204,7 +204,7 @@ public void itemStateChanged(ItemEvent event) if (bpmFormat.getSelectedItem() == miFormatTrim) { trimSpaces(); } else if (bpmFormat.getSelectedItem() == miFormatAlign) { - alignTableColumns(2, true); + alignTableColumns(2, true, 4); } else if (bpmFormat.getSelectedItem() == miFormatSort) { sortTable(true); } @@ -396,10 +396,13 @@ public void trimSpaces() * @param spaces Min. number of spaces between columns. * @param alignPerColumn specify {@code true} to calculate max width on a per column basis, * or {@code false} to calculate for the whole table. + * @param multipleOf ensures that column position is always a multiple of the specified value. + * (e.g. specify 2 to have every column start at an even horizontal position.) */ - public void alignTableColumns(int spaces, boolean alignPerColumn) + public void alignTableColumns(int spaces, boolean alignPerColumn, int multipleOf) { spaces = Math.max(1, spaces); + multipleOf = Math.max(1, multipleOf); // splitting text into lines String input = editor.getText(); @@ -438,12 +441,20 @@ public void alignTableColumns(int spaces, boolean alignPerColumn) } else { maxLen = maxTokenLength; } - columns[col] = maxLen + spaces; + int len = maxLen + spaces; + if (len % multipleOf != 0) { + len += multipleOf - (len % multipleOf); + } + columns[col] = len; } // normalizing data StringBuilder newText = new StringBuilder(); - String blank = new String(new char[maxTokenLength + spaces]).replace('\0', ' '); + int blankLen = maxTokenLength + spaces; + if (blankLen % multipleOf != 0) { + blankLen += multipleOf - (blankLen % multipleOf); + } + String blank = new String(new char[blankLen]).replace('\0', ' '); for (int row = 0, rows = matrix.size(); row < rows; row++) { StringBuilder sb = new StringBuilder(); for (int col = 0, cols = matrix.get(row).size(); col < cols; col++) { diff --git a/src/org/infinity/resource/text/QuestsPanel.java b/src/org/infinity/resource/text/QuestsPanel.java index 469789180..bd8c623a4 100644 --- a/src/org/infinity/resource/text/QuestsPanel.java +++ b/src/org/infinity/resource/text/QuestsPanel.java @@ -67,7 +67,6 @@ public final class QuestsPanel extends JPanel implements ListSelectionListener private final JTable assignedChecks = new JTable(); private final JTable completeChecks = new JTable(); - // private static final class QuestsModel extends AbstractTableModel { private static final String[] COLUMNS = { @@ -167,7 +166,6 @@ protected void setupCompleted(boolean isSelected) setFont(font.deriveFont(attributes)); } } - // public QuestsPanel(List quests, Variables vars) { @@ -234,7 +232,6 @@ public Component getTableCellRendererComponent(JTable table, Object value, boole setQuests(quests); } - // @Override public void valueChanged(ListSelectionEvent e) { @@ -261,7 +258,6 @@ public void valueChanged(ListSelectionEvent e) completeChecks.setModel(EMPTY_MODEL); } } - // public void setQuests(List quests) { diff --git a/src/org/infinity/resource/text/QuestsResource.java b/src/org/infinity/resource/text/QuestsResource.java index f96c43d58..6ffa939be 100644 --- a/src/org/infinity/resource/text/QuestsResource.java +++ b/src/org/infinity/resource/text/QuestsResource.java @@ -41,7 +41,6 @@ public class QuestsResource extends PlainTextResource implements ChangeListener */ private boolean dirty = false; - // /** Represents one quest in the file. */ public static final class Quest { @@ -214,7 +213,6 @@ public enum State /** Quest is finished. */ Completed; } - // public QuestsResource() throws Exception { @@ -238,7 +236,6 @@ public JComponent makeViewer(ViewableContainer container) return pane; } - // @Override public void changedUpdate(DocumentEvent event) { @@ -259,9 +256,7 @@ public void removeUpdate(DocumentEvent event) super.removeUpdate(event); dirty = true; } - // - // @Override public void stateChanged(ChangeEvent e) { @@ -272,7 +267,6 @@ public void stateChanged(ChangeEvent e) dirty = false; } } - // public List readQuests() { diff --git a/src/org/infinity/resource/text/modes/BCSFoldParser.java b/src/org/infinity/resource/text/modes/BCSFoldParser.java index 2b21ad00a..f121393b7 100644 --- a/src/org/infinity/resource/text/modes/BCSFoldParser.java +++ b/src/org/infinity/resource/text/modes/BCSFoldParser.java @@ -32,7 +32,7 @@ public BCSFoldParser() @Override public List getFolds(RSyntaxTextArea textArea) { - List folds = new ArrayList(); + List folds = new ArrayList<>(); Fold curFold = null; int lineCount = textArea.getLineCount(); diff --git a/src/org/infinity/resource/text/modes/GLSLTokenMaker.java b/src/org/infinity/resource/text/modes/GLSLTokenMaker.java index 5ab5660e6..c7023d22b 100644 --- a/src/org/infinity/resource/text/modes/GLSLTokenMaker.java +++ b/src/org/infinity/resource/text/modes/GLSLTokenMaker.java @@ -3781,6 +3781,7 @@ public String[] getLineCommentStartAndEnd(int languageIndex) { * @return The first {@code Token} in a linked list representing * the syntax highlighted text. */ + @Override public Token getTokenList(Segment text, int initialTokenType, int startOffset) { resetTokenList(); @@ -3919,6 +3920,7 @@ public final int yystate() { * * @param newState the new lexical state */ + @Override public final void yybegin(int newState) { zzLexicalState = newState; } diff --git a/src/org/infinity/resource/var/VarResource.java b/src/org/infinity/resource/var/VarResource.java index 0fc6ea262..d2280a105 100644 --- a/src/org/infinity/resource/var/VarResource.java +++ b/src/org/infinity/resource/var/VarResource.java @@ -38,7 +38,6 @@ public VarResource(ResourceEntry entry) throws Exception super(entry); } - // @Override public AddRemovable[] getPrototypes() throws Exception { @@ -50,9 +49,7 @@ public AddRemovable confirmAddEntry(AddRemovable entry) throws Exception { return entry; } - // - // @Override public int getViewerTabCount() { @@ -81,9 +78,7 @@ public boolean viewerTabAddedBefore(int index) { return false; } - // - // @Override public int read(ByteBuffer buffer, int offset) throws Exception { @@ -92,9 +87,7 @@ public int read(ByteBuffer buffer, int offset) throws Exception addField(new Entry(this, buffer, offset + i * 44, i)); return offset + count * 44; } - // - // @Override protected void viewerInitialized(StructViewer viewer) { @@ -134,5 +127,4 @@ protected void datatypeRemovedInChild(AbstractStruct child, AddRemovable datatyp hexViewer.dataModified(); } } - // } diff --git a/src/org/infinity/resource/vef/AbstractComponent.java b/src/org/infinity/resource/vef/AbstractComponent.java index 7bb599cbd..e1a0da887 100644 --- a/src/org/infinity/resource/vef/AbstractComponent.java +++ b/src/org/infinity/resource/vef/AbstractComponent.java @@ -52,7 +52,7 @@ public int read(ByteBuffer buffer, int offset) throws Exception VefType type = new VefType(buffer, offset + 12, 4); addField(type); - List list = new ArrayList(); + List list = new ArrayList<>(); offset = type.readAttributes(buffer, offset + 16, list); addFields(getFields().size() - 1, list); diff --git a/src/org/infinity/resource/vef/VefResource.java b/src/org/infinity/resource/vef/VefResource.java index 63f0d47ef..156eb630a 100644 --- a/src/org/infinity/resource/vef/VefResource.java +++ b/src/org/infinity/resource/vef/VefResource.java @@ -48,7 +48,6 @@ public VefResource(ResourceEntry entry) throws Exception super(entry); } - // @Override public AddRemovable[] getPrototypes() throws Exception { @@ -60,9 +59,7 @@ public AddRemovable confirmAddEntry(AddRemovable entry) throws Exception { return entry; } - // - // @Override public int getViewerTabCount() { @@ -89,9 +86,7 @@ public boolean viewerTabAddedBefore(int index) { return false; } - // - // @Override public int read(ByteBuffer buffer, int offset) throws Exception { @@ -131,9 +126,7 @@ public int read(ByteBuffer buffer, int offset) throws Exception } return endoffset; } - // - // @Override protected void viewerInitialized(StructViewer viewer) { @@ -173,5 +166,4 @@ protected void datatypeRemovedInChild(AbstractStruct child, AddRemovable datatyp hexViewer.dataModified(); } } - // } diff --git a/src/org/infinity/resource/vertex/Vertex.java b/src/org/infinity/resource/vertex/Vertex.java index 8ea445bea..1465f2196 100644 --- a/src/org/infinity/resource/vertex/Vertex.java +++ b/src/org/infinity/resource/vertex/Vertex.java @@ -8,6 +8,7 @@ import java.util.List; import org.infinity.datatype.DecNumber; +import org.infinity.datatype.IsNumeric; import org.infinity.resource.AbstractStruct; import org.infinity.resource.AddRemovable; import org.infinity.util.io.StreamUtils; @@ -56,7 +57,7 @@ public int read(ByteBuffer buffer, int offset) /** Returns the x coordinate of the vertex. */ public int getX() { - DecNumber dn = (DecNumber)getAttribute(getOffset(), false); + IsNumeric dn = (IsNumeric)getAttribute(getOffset(), false); if (dn != null) { return dn.getValue(); } else { @@ -76,7 +77,7 @@ public void setX(int value) /** Returns the y coordinate of the vertex. */ public int getY() { - DecNumber dn = (DecNumber)getAttribute(getOffset() + 2, false); + IsNumeric dn = (IsNumeric)getAttribute(getOffset() + 2, false); if (dn != null) { return dn.getValue(); } else { diff --git a/src/org/infinity/resource/video/AudioQueue.java b/src/org/infinity/resource/video/AudioQueue.java index c0541aaa5..c18281f2d 100644 --- a/src/org/infinity/resource/video/AudioQueue.java +++ b/src/org/infinity/resource/video/AudioQueue.java @@ -19,7 +19,7 @@ public class AudioQueue */ public AudioQueue() { - deque = new ArrayDeque(); + deque = new ArrayDeque<>(); } /** diff --git a/src/org/infinity/resource/video/MveDecoder.java b/src/org/infinity/resource/video/MveDecoder.java index 683737c63..24dda18b4 100644 --- a/src/org/infinity/resource/video/MveDecoder.java +++ b/src/org/infinity/resource/video/MveDecoder.java @@ -577,7 +577,7 @@ private MveInfo() width = height = 0; isPalette = true; - audioOutput = new Vector(AUDIOSTREAMS_MAX); + audioOutput = new Vector<>(AUDIOSTREAMS_MAX); for (int i = 0; i < AUDIOSTREAMS_MAX; i++) { audioOutput.add(null); } @@ -643,7 +643,7 @@ public static class MveChunk private MveChunk() { initialized = false; - segments = new ArrayList(10); + segments = new ArrayList<>(10); segmentIndex = 0; } diff --git a/src/org/infinity/resource/video/MveResource.java b/src/org/infinity/resource/video/MveResource.java index af236f073..6f34d6266 100644 --- a/src/org/infinity/resource/video/MveResource.java +++ b/src/org/infinity/resource/video/MveResource.java @@ -378,7 +378,7 @@ public static boolean convertAvi(ResourceEntry inEntry, Path outFile, Window par decoder.setDefaultAudioOutput(new AudioQueue()); // prebuffering audio and searching for first video frame - LinkedList audioQueue = new LinkedList(); + LinkedList audioQueue = new LinkedList<>(); while (decoder.hasNextFrame()) { decoder.processNextFrame(); if (!decoder.frameHasVideo()) { diff --git a/src/org/infinity/resource/wed/Door.java b/src/org/infinity/resource/wed/Door.java index 88573be7b..ec16dacb9 100644 --- a/src/org/infinity/resource/wed/Door.java +++ b/src/org/infinity/resource/wed/Door.java @@ -8,7 +8,7 @@ import org.infinity.datatype.Bitmap; import org.infinity.datatype.DecNumber; -import org.infinity.datatype.HexNumber; +import org.infinity.datatype.IsNumeric; import org.infinity.datatype.RemovableDecNumber; import org.infinity.datatype.SectionCount; import org.infinity.datatype.SectionOffset; @@ -43,7 +43,6 @@ public Door(AbstractStruct superStruct, ByteBuffer buffer, int offset, int numbe super(superStruct, WED_DOOR + " " + number, buffer, offset); } - // @Override public AddRemovable[] getPrototypes() throws Exception { @@ -55,27 +54,22 @@ public AddRemovable confirmAddEntry(AddRemovable entry) throws Exception { return entry; } - // - // @Override public boolean canRemove() { return true; } - // - // @Override protected void setAddRemovableOffset(AddRemovable datatype) { if (datatype instanceof RemovableDecNumber) { - final int offset = ((HexNumber)getParent().getAttribute(WedResource.WED_OFFSET_DOOR_TILEMAP_LOOKUP)).getValue(); + final int offset = ((IsNumeric)getParent().getAttribute(WedResource.WED_OFFSET_DOOR_TILEMAP_LOOKUP)).getValue(); int index = getTilemapIndex().getValue(); datatype.setOffset(offset + index * 2); } } - // public DecNumber getTilemapIndex() { @@ -115,7 +109,6 @@ public void updatePolygonsOffset(int offset) } } - // @Override public int read(ByteBuffer buffer, int offset) throws Exception { @@ -147,7 +140,7 @@ public int read(ByteBuffer buffer, int offset) throws Exception } if (getParent() != null) { - final HexNumber offsetTileCell = (HexNumber)getParent().getAttribute(WedResource.WED_OFFSET_DOOR_TILEMAP_LOOKUP); + final IsNumeric offsetTileCell = (IsNumeric)getParent().getAttribute(WedResource.WED_OFFSET_DOOR_TILEMAP_LOOKUP); for (int i = 0; i < countTileCell.getValue(); i++) { addField(new RemovableDecNumber(buffer, offsetTileCell.getValue() + 2 * (indexTileCell.getValue() + i), 2, @@ -156,5 +149,4 @@ public int read(ByteBuffer buffer, int offset) throws Exception } return offset + 26; } - // } diff --git a/src/org/infinity/resource/wed/Polygon.java b/src/org/infinity/resource/wed/Polygon.java index 31bfc8f6d..393c7468d 100644 --- a/src/org/infinity/resource/wed/Polygon.java +++ b/src/org/infinity/resource/wed/Polygon.java @@ -8,7 +8,7 @@ import org.infinity.datatype.DecNumber; import org.infinity.datatype.Flag; -import org.infinity.datatype.HexNumber; +import org.infinity.datatype.IsNumeric; import org.infinity.datatype.SectionCount; import org.infinity.datatype.Unknown; import org.infinity.resource.AbstractStruct; @@ -37,7 +37,6 @@ public Polygon(AbstractStruct superStruct, String name, ByteBuffer buffer, int o super(superStruct, name, buffer, offset, 8); } - // @Override public AddRemovable[] getPrototypes() throws Exception { @@ -49,37 +48,32 @@ public AddRemovable confirmAddEntry(AddRemovable entry) throws Exception { return entry; } - // - // @Override public boolean canRemove() { return true; } - // - // @Override protected void setAddRemovableOffset(AddRemovable datatype) { if (datatype instanceof Vertex) { - int index = ((DecNumber)getAttribute(WED_POLY_VERTEX_INDEX)).getValue(); - index += ((DecNumber)getAttribute(WED_POLY_NUM_VERTICES)).getValue(); + int index = ((IsNumeric)getAttribute(WED_POLY_VERTEX_INDEX)).getValue(); + index += ((IsNumeric)getAttribute(WED_POLY_NUM_VERTICES)).getValue(); AbstractStruct superStruct = getParent(); while (superStruct.getParent() != null) superStruct = superStruct.getParent(); - int offset = ((HexNumber)superStruct.getAttribute(WedResource.WED_OFFSET_VERTICES)).getValue(); + int offset = ((IsNumeric)superStruct.getAttribute(WedResource.WED_OFFSET_VERTICES)).getValue(); datatype.setOffset(offset + 4 * index); ((AbstractStruct)datatype).realignStructOffsets(); } } - // public void readVertices(ByteBuffer buffer, int offset) throws Exception { - DecNumber firstVertex = (DecNumber)getAttribute(WED_POLY_VERTEX_INDEX); - DecNumber numVertices = (DecNumber)getAttribute(WED_POLY_NUM_VERTICES); + IsNumeric firstVertex = (IsNumeric)getAttribute(WED_POLY_VERTEX_INDEX); + IsNumeric numVertices = (IsNumeric)getAttribute(WED_POLY_NUM_VERTICES); for (int i = 0; i < numVertices.getValue(); i++) { addField(new Vertex(this, buffer, offset + 4 * (firstVertex.getValue() + i), i)); } @@ -101,7 +95,6 @@ public int updateVertices(int offset, int startIndex) return count; } - // @Override public int read(ByteBuffer buffer, int offset) throws Exception { @@ -115,5 +108,4 @@ public int read(ByteBuffer buffer, int offset) throws Exception addField(new DecNumber(buffer, offset + 16, 2, WED_POLY_MAX_COORD_Y)); return offset + 18; } - // } diff --git a/src/org/infinity/resource/wed/Tilemap.java b/src/org/infinity/resource/wed/Tilemap.java index efee936d3..48bf32e07 100644 --- a/src/org/infinity/resource/wed/Tilemap.java +++ b/src/org/infinity/resource/wed/Tilemap.java @@ -8,6 +8,7 @@ import org.infinity.datatype.DecNumber; import org.infinity.datatype.Flag; +import org.infinity.datatype.IsNumeric; import org.infinity.datatype.Unknown; import org.infinity.resource.AbstractStruct; @@ -32,7 +33,7 @@ public Tilemap(AbstractStruct superStruct, ByteBuffer buffer, int offset, int nu public int getTileCount() { - return ((DecNumber)getAttribute(WED_TILEMAP_TILE_COUNT_PRI)).getValue(); + return ((IsNumeric)getAttribute(WED_TILEMAP_TILE_COUNT_PRI)).getValue(); } @Override diff --git a/src/org/infinity/resource/wed/Wallgroup.java b/src/org/infinity/resource/wed/Wallgroup.java index 6d68239a8..0a29ad7d7 100644 --- a/src/org/infinity/resource/wed/Wallgroup.java +++ b/src/org/infinity/resource/wed/Wallgroup.java @@ -7,6 +7,7 @@ import java.nio.ByteBuffer; import org.infinity.datatype.DecNumber; +import org.infinity.datatype.IsNumeric; import org.infinity.resource.AbstractStruct; import org.infinity.resource.AddRemovable; import org.infinity.util.io.StreamUtils; @@ -30,8 +31,8 @@ public Wallgroup(AbstractStruct superStruct, ByteBuffer buffer, int offset, int public int getNextPolygonIndex() { - int count = ((DecNumber)getAttribute(WED_WALLGROUP_NUM_POLYGONS)).getValue(); - int index = ((DecNumber)getAttribute(WED_WALLGROUP_POLYGON_INDEX)).getValue(); + int count = ((IsNumeric)getAttribute(WED_WALLGROUP_NUM_POLYGONS)).getValue(); + int index = ((IsNumeric)getAttribute(WED_WALLGROUP_POLYGON_INDEX)).getValue(); return count + index; } diff --git a/src/org/infinity/resource/wed/WedResource.java b/src/org/infinity/resource/wed/WedResource.java index e0b0909aa..80351c8d6 100644 --- a/src/org/infinity/resource/wed/WedResource.java +++ b/src/org/infinity/resource/wed/WedResource.java @@ -15,6 +15,7 @@ import org.infinity.datatype.DecNumber; import org.infinity.datatype.HexNumber; +import org.infinity.datatype.IsNumeric; import org.infinity.datatype.RemovableDecNumber; import org.infinity.datatype.SectionCount; import org.infinity.datatype.SectionOffset; @@ -83,7 +84,6 @@ public WedResource(ResourceEntry entry) throws Exception super(entry); } - // @Override public AddRemovable[] getPrototypes() throws Exception { @@ -95,17 +95,13 @@ public AddRemovable confirmAddEntry(AddRemovable entry) throws Exception { return entry; } - // - // @Override public void write(OutputStream os) throws IOException { super.writeFlatFields(os); } - // - // @Override public int getViewerTabCount() { @@ -132,9 +128,7 @@ public boolean viewerTabAddedBefore(int index) { return false; } - // - // @Override protected void viewerInitialized(StructViewer viewer) { @@ -202,9 +196,7 @@ else if (datatype instanceof RemovableDecNumber && child instanceof Door) { hexViewer.dataModified(); } } - // - // @Override public int read(ByteBuffer buffer, int offset) throws Exception { @@ -307,7 +299,6 @@ public int compare(HexNumber s1, HexNumber s2) } return endoffset; } - // private void updateSectionOffsets(AddRemovable datatype, int size) { @@ -331,8 +322,8 @@ private void updateSectionOffsets(AddRemovable datatype, int size) } // Assumes polygon offset is correct - int offset = ((SectionOffset)getAttribute(WED_OFFSET_WALL_POLYGONS)).getValue(); - offset += ((SectionCount)getAttribute(WED_NUM_WALL_POLYGONS)).getValue() * 18; + int offset = ((IsNumeric)getAttribute(WED_OFFSET_WALL_POLYGONS)).getValue(); + offset += ((IsNumeric)getAttribute(WED_NUM_WALL_POLYGONS)).getValue() * 18; for (final StructEntry o : getFields()) { if (o instanceof Door) { ((Door)o).updatePolygonsOffset(offset); @@ -343,7 +334,7 @@ private void updateSectionOffsets(AddRemovable datatype, int size) private void updateVertices() { // Assumes vertices offset is correct - int offset = ((HexNumber)getAttribute(WED_OFFSET_VERTICES)).getValue(); + int offset = ((IsNumeric)getAttribute(WED_OFFSET_VERTICES)).getValue(); int count = 0; for (final StructEntry o : getFields()) { if (o instanceof Polygon) { diff --git a/src/org/infinity/resource/wmp/AreaEntry.java b/src/org/infinity/resource/wmp/AreaEntry.java index 5cdd2d189..3b6a03e77 100644 --- a/src/org/infinity/resource/wmp/AreaEntry.java +++ b/src/org/infinity/resource/wmp/AreaEntry.java @@ -10,6 +10,7 @@ import org.infinity.datatype.DecNumber; import org.infinity.datatype.Flag; +import org.infinity.datatype.IsNumeric; import org.infinity.datatype.ResourceRef; import org.infinity.datatype.SectionCount; import org.infinity.datatype.StringRef; @@ -105,29 +106,29 @@ public int read(ByteBuffer buffer, int offset) throws Exception void readLinks(ByteBuffer buffer, DecNumber linkOffset) throws Exception { - DecNumber northStart = (DecNumber)getAttribute(WMP_AREA_FIRST_LINK_NORTH); - DecNumber northCount = (DecNumber)getAttribute(WMP_AREA_NUM_LINKS_NORTH); + IsNumeric northStart = (IsNumeric)getAttribute(WMP_AREA_FIRST_LINK_NORTH); + IsNumeric northCount = (IsNumeric)getAttribute(WMP_AREA_NUM_LINKS_NORTH); int offset = linkOffset.getValue() + northStart.getValue() * 216; for (int i = 0; i < northCount.getValue(); i++) { addField(new AreaLinkNorth(this, buffer, offset + i * 216, i)); } - DecNumber westStart = (DecNumber)getAttribute(WMP_AREA_FIRST_LINK_WEST); - DecNumber westCount = (DecNumber)getAttribute(WMP_AREA_NUM_LINKS_WEST); + IsNumeric westStart = (IsNumeric)getAttribute(WMP_AREA_FIRST_LINK_WEST); + IsNumeric westCount = (IsNumeric)getAttribute(WMP_AREA_NUM_LINKS_WEST); offset = linkOffset.getValue() + westStart.getValue() * 216; for (int i = 0; i < westCount.getValue(); i++) { addField(new AreaLinkWest(this, buffer, offset + i * 216, i)); } - DecNumber southStart = (DecNumber)getAttribute(WMP_AREA_FIRST_LINK_SOUTH); - DecNumber southCount = (DecNumber)getAttribute(WMP_AREA_NUM_LINKS_SOUTH); + IsNumeric southStart = (IsNumeric)getAttribute(WMP_AREA_FIRST_LINK_SOUTH); + IsNumeric southCount = (IsNumeric)getAttribute(WMP_AREA_NUM_LINKS_SOUTH); offset = linkOffset.getValue() + southStart.getValue() * 216; for (int i = 0; i < southCount.getValue(); i++) { addField(new AreaLinkSouth(this, buffer, offset + i * 216, i)); } - DecNumber eastStart = (DecNumber)getAttribute(WMP_AREA_FIRST_LINK_EAST); - DecNumber eastCount = (DecNumber)getAttribute(WMP_AREA_NUM_LINKS_EAST); + IsNumeric eastStart = (IsNumeric)getAttribute(WMP_AREA_FIRST_LINK_EAST); + IsNumeric eastCount = (IsNumeric)getAttribute(WMP_AREA_NUM_LINKS_EAST); offset = linkOffset.getValue() + eastStart.getValue() * 216; for (int i = 0; i < eastCount.getValue(); i++) { addField(new AreaLinkEast(this, buffer, offset + i * 216, i)); diff --git a/src/org/infinity/resource/wmp/ViewerArea.java b/src/org/infinity/resource/wmp/ViewerArea.java index 4ae0d0d76..605de4bfc 100644 --- a/src/org/infinity/resource/wmp/ViewerArea.java +++ b/src/org/infinity/resource/wmp/ViewerArea.java @@ -17,8 +17,8 @@ import javax.swing.JList; import javax.swing.JPanel; -import org.infinity.datatype.DecNumber; import org.infinity.datatype.Flag; +import org.infinity.datatype.IsNumeric; import org.infinity.datatype.ResourceRef; import org.infinity.gui.ViewFrame; import org.infinity.gui.ViewerUtil; @@ -48,7 +48,7 @@ private static JPanel makeInfoPanel(AreaEntry areaEntry) JPanel infoPane = makeInfoPanel(areaEntry); JComponent icon = ViewerUtil.makeBamPanel( (ResourceRef)areaEntry.getParent().getAttribute(MapEntry.WMP_MAP_ICONS), - ((DecNumber)areaEntry.getAttribute(AreaEntry.WMP_AREA_ICON_INDEX)).getValue(), + ((IsNumeric)areaEntry.getAttribute(AreaEntry.WMP_AREA_ICON_INDEX)).getValue(), 0); JPanel linkPanelN = ViewerUtil.makeListPanel("North links", areaEntry, AreaLinkNorth.class, AreaLink.WMP_LINK_TARGET_ENTRANCE); JPanel linkPanelS = ViewerUtil.makeListPanel("South links", areaEntry, AreaLinkSouth.class, AreaLink.WMP_LINK_TARGET_ENTRANCE); diff --git a/src/org/infinity/resource/wmp/ViewerMap.java b/src/org/infinity/resource/wmp/ViewerMap.java index 836134c74..9221d6f20 100644 --- a/src/org/infinity/resource/wmp/ViewerMap.java +++ b/src/org/infinity/resource/wmp/ViewerMap.java @@ -44,12 +44,9 @@ import javax.swing.event.ListSelectionListener; import org.infinity.NearInfinity; -import org.infinity.datatype.DecNumber; import org.infinity.datatype.Flag; import org.infinity.datatype.IsNumeric; -import org.infinity.datatype.ResourceRef; -import org.infinity.datatype.SectionCount; -import org.infinity.datatype.SectionOffset; +import org.infinity.datatype.IsReference; import org.infinity.datatype.StringRef; import org.infinity.gui.BrowserMenuBar; import org.infinity.gui.RenderCanvas; @@ -136,7 +133,7 @@ private enum Direction { NORTH, WEST, SOUTH, EAST } try { mapIcons = null; - ResourceRef iconRef = (ResourceRef)wmpMap.getAttribute(MapEntry.WMP_MAP_ICONS); + IsReference iconRef = (IsReference)wmpMap.getAttribute(MapEntry.WMP_MAP_ICONS); if (iconRef != null) { ResourceEntry iconEntry = ResourceFactory.getResourceEntry(iconRef.getResourceName()); if (iconEntry != null) { @@ -204,7 +201,7 @@ private MapEntry getEntry() /** Load and return map graphics. */ private BufferedImage loadMap() { - String mapName = ((ResourceRef)getEntry().getAttribute(MapEntry.WMP_MAP_RESREF)).getResourceName(); + String mapName = ((IsReference)getEntry().getAttribute(MapEntry.WMP_MAP_RESREF)).getResourceName(); if (ResourceFactory.resourceExists(mapName)) { MosDecoder mos = MosDecoder.loadMos(ResourceFactory.getResourceEntry(mapName)); if (mos != null) { @@ -246,7 +243,7 @@ private void showMapIcons() for (int i = 0, count = listPanel.getList().getModel().getSize(); i < count; i++) { AreaEntry area = getAreaEntry(i, true); if (area != null) { - int iconIndex = ((DecNumber)area.getAttribute(AreaEntry.WMP_AREA_ICON_INDEX)).getValue(); + int iconIndex = ((IsNumeric)area.getAttribute(AreaEntry.WMP_AREA_ICON_INDEX)).getValue(); int frameIndex = mapIconsCtrl.cycleGetFrameIndexAbsolute(iconIndex, 0); if (frameIndex >= 0) { final Image mapIcon = mapIcons.frameGet(mapIconsCtrl, frameIndex); @@ -280,13 +277,13 @@ private void showMapIconLabels() for (int i = 0, count = listPanel.getList().getModel().getSize(); i < count; i++) { AreaEntry area = getAreaEntry(i, true); if (area != null) { - int iconIndex = ((DecNumber)area.getAttribute(AreaEntry.WMP_AREA_ICON_INDEX)).getValue(); + int iconIndex = ((IsNumeric)area.getAttribute(AreaEntry.WMP_AREA_ICON_INDEX)).getValue(); int frameIndex = mapIconsCtrl.cycleGetFrameIndexAbsolute(iconIndex, 0); if (frameIndex >= 0) { // getting area name - int strref = ((StringRef)area.getAttribute(AreaEntry.WMP_AREA_NAME)).getValue(); + int strref = ((IsNumeric)area.getAttribute(AreaEntry.WMP_AREA_NAME)).getValue(); if (strref < 0) { - strref = ((StringRef)area.getAttribute(AreaEntry.WMP_AREA_TOOLTIP)).getValue(); + strref = ((IsNumeric)area.getAttribute(AreaEntry.WMP_AREA_TOOLTIP)).getValue(); } String mapName = (strref >= 0) ? StringTable.getStringRef(strref) : null; if (mapName != null && mapName.trim().length() == 0) { @@ -294,7 +291,7 @@ private void showMapIconLabels() } // getting area code - String mapCode = ((ResourceRef)area.getAttribute(AreaEntry.WMP_AREA_CURRENT)).getResourceName(); + String mapCode = ((IsReference)area.getAttribute(AreaEntry.WMP_AREA_CURRENT)).getResourceName(); if (ResourceFactory.resourceExists(mapCode)) { mapCode = mapCode.replace(".ARE", ""); } else { @@ -357,16 +354,16 @@ private void showMapDistances(int areaIndex) final Color[] dirColor = { Color.GREEN, Color.RED, Color.CYAN, Color.YELLOW }; final int[] links = new int[8]; final int linkSize = 216; // size of a single area link structure - int ofsLinkBase = ((SectionOffset)getEntry().getAttribute(MapEntry.WMP_MAP_OFFSET_AREA_LINKS)).getValue(); - - links[0] = ((DecNumber)area.getAttribute(AreaEntry.WMP_AREA_FIRST_LINK_NORTH)).getValue(); - links[1] = ((SectionCount)area.getAttribute(AreaEntry.WMP_AREA_NUM_LINKS_NORTH)).getValue(); - links[2] = ((DecNumber)area.getAttribute(AreaEntry.WMP_AREA_FIRST_LINK_WEST)).getValue(); - links[3] = ((SectionCount)area.getAttribute(AreaEntry.WMP_AREA_NUM_LINKS_WEST)).getValue(); - links[4] = ((DecNumber)area.getAttribute(AreaEntry.WMP_AREA_FIRST_LINK_SOUTH)).getValue(); - links[5] = ((SectionCount)area.getAttribute(AreaEntry.WMP_AREA_NUM_LINKS_SOUTH)).getValue(); - links[6] = ((DecNumber)area.getAttribute(AreaEntry.WMP_AREA_FIRST_LINK_EAST)).getValue(); - links[7] = ((SectionCount)area.getAttribute(AreaEntry.WMP_AREA_NUM_LINKS_EAST)).getValue(); + int ofsLinkBase = ((IsNumeric)getEntry().getAttribute(MapEntry.WMP_MAP_OFFSET_AREA_LINKS)).getValue(); + + links[0] = ((IsNumeric)area.getAttribute(AreaEntry.WMP_AREA_FIRST_LINK_NORTH)).getValue(); + links[1] = ((IsNumeric)area.getAttribute(AreaEntry.WMP_AREA_NUM_LINKS_NORTH)).getValue(); + links[2] = ((IsNumeric)area.getAttribute(AreaEntry.WMP_AREA_FIRST_LINK_WEST)).getValue(); + links[3] = ((IsNumeric)area.getAttribute(AreaEntry.WMP_AREA_NUM_LINKS_WEST)).getValue(); + links[4] = ((IsNumeric)area.getAttribute(AreaEntry.WMP_AREA_FIRST_LINK_SOUTH)).getValue(); + links[5] = ((IsNumeric)area.getAttribute(AreaEntry.WMP_AREA_NUM_LINKS_SOUTH)).getValue(); + links[6] = ((IsNumeric)area.getAttribute(AreaEntry.WMP_AREA_FIRST_LINK_EAST)).getValue(); + links[7] = ((IsNumeric)area.getAttribute(AreaEntry.WMP_AREA_NUM_LINKS_EAST)).getValue(); for (int dir = 0; dir < srcDir.length; dir++) { Direction curDir = srcDir[dir]; Point ptOrigin = getMapIconCoordinate(areaIndex, curDir, true); @@ -375,7 +372,7 @@ private void showMapDistances(int areaIndex) AreaLink destLink = (AreaLink)area.getAttribute(ofsLink, false); if (destLink != null) { - int dstAreaIndex = ((DecNumber)destLink.getAttribute(AreaLink.WMP_LINK_TARGET_AREA)).getValue(); + int dstAreaIndex = ((IsNumeric)destLink.getAttribute(AreaLink.WMP_LINK_TARGET_AREA)).getValue(); Flag flag = (Flag)destLink.getAttribute(AreaLink.WMP_LINK_DEFAULT_ENTRANCE); Direction dstDir = Direction.NORTH; if (flag.isFlagSet(1)) { @@ -389,9 +386,9 @@ private void showMapDistances(int areaIndex) // checking for random encounters during travels boolean hasRandomEncounters = false; - if (((DecNumber)destLink.getAttribute(AreaLink.WMP_LINK_RANDOM_ENCOUNTER_PROBABILITY)).getValue() > 0) { + if (((IsNumeric)destLink.getAttribute(AreaLink.WMP_LINK_RANDOM_ENCOUNTER_PROBABILITY)).getValue() > 0) { for (int rnd = 1; rnd < 6; rnd++) { - String rndArea = ((ResourceRef)destLink + String rndArea = ((IsReference)destLink .getAttribute(String.format(AreaLink.WMP_LINK_RANDOM_ENCOUNTER_AREA_FMT, rnd))) .getResourceName(); if (ResourceFactory.resourceExists(rndArea)) { @@ -415,7 +412,7 @@ private void showMapDistances(int areaIndex) g.drawLine(ptOrigin.x, ptOrigin.y, ptTarget.x, ptTarget.y); // printing travel time (in hours) - String duration = String.format("%d h", ((DecNumber)destLink.getAttribute(AreaLink.WMP_LINK_DISTANCE_SCALE)).getValue() * 4); + String duration = String.format("%d h", ((IsNumeric)destLink.getAttribute(AreaLink.WMP_LINK_DISTANCE_SCALE)).getValue() * 4); LineMetrics lm = g.getFont().getLineMetrics(duration, g.getFontRenderContext()); Rectangle2D rectText = g.getFont().getStringBounds(duration, g.getFontRenderContext()); int textX = ptOrigin.x + ((ptTarget.x - ptOrigin.x) - rectText.getBounds().width) / 2; @@ -457,7 +454,7 @@ private Point getMapIconCoordinate(int areaIndex, Direction dir, boolean byPanel AreaEntry area = getAreaEntry(areaIndex, byPanel); if (area != null) { Point p = getAreaEntryPosition(area, isScaling()); - int iconIndex = ((DecNumber)area.getAttribute(AreaEntry.WMP_AREA_ICON_INDEX)).getValue(); + int iconIndex = ((IsNumeric)area.getAttribute(AreaEntry.WMP_AREA_ICON_INDEX)).getValue(); int frameIndex = mapIconsCtrl.cycleGetFrameIndexAbsolute(iconIndex, 0); int width, height; if (frameIndex >= 0) { @@ -519,9 +516,9 @@ private void showDot(AreaEntry entry, boolean restore) } if (entry != null) { storeDot(entry); - int x = ((DecNumber)entry.getAttribute(AreaEntry.WMP_AREA_COORDINATE_X)).getValue(); + int x = ((IsNumeric)entry.getAttribute(AreaEntry.WMP_AREA_COORDINATE_X)).getValue(); x = (int)(x * mapScaleX); - int y = ((DecNumber)entry.getAttribute(AreaEntry.WMP_AREA_COORDINATE_Y)).getValue(); + int y = ((IsNumeric)entry.getAttribute(AreaEntry.WMP_AREA_COORDINATE_Y)).getValue(); y = (int)(y * mapScaleY); int width = iconDot.getWidth(); int height = iconDot.getHeight(); @@ -542,9 +539,9 @@ private void showDot(AreaEntry entry, boolean restore) private void storeDot(AreaEntry entry) { if (entry != null) { - int x = ((DecNumber)entry.getAttribute(AreaEntry.WMP_AREA_COORDINATE_X)).getValue(); + int x = ((IsNumeric)entry.getAttribute(AreaEntry.WMP_AREA_COORDINATE_X)).getValue(); x = (int)(x * mapScaleX); - int y = ((DecNumber)entry.getAttribute(AreaEntry.WMP_AREA_COORDINATE_Y)).getValue(); + int y = ((IsNumeric)entry.getAttribute(AreaEntry.WMP_AREA_COORDINATE_Y)).getValue(); y = (int)(y * mapScaleY); int width = dotBackup.getWidth(); int height = dotBackup.getHeight(); @@ -814,7 +811,7 @@ public Component getListCellRendererComponent(JList list, Object value, int i label.setText(getListValue(value, true)); AbstractStruct struct = (AbstractStruct)value; - DecNumber animNr = (DecNumber)struct.getAttribute(AreaEntry.WMP_AREA_ICON_INDEX); + IsNumeric animNr = (IsNumeric)struct.getAttribute(AreaEntry.WMP_AREA_ICON_INDEX); setIcon(null); if (ctrl != null) { setIcon(new ImageIcon(bam.frameGet(ctrl, ctrl.cycleGetFrameIndexAbsolute(animNr.getValue(), 0)))); @@ -834,7 +831,7 @@ private String getListValue(Object value, boolean showFull) AbstractStruct struct = (AbstractStruct)value; StringRef areaName = (StringRef)struct.getAttribute(AreaEntry.WMP_AREA_NAME); - ResourceRef areaRef = (ResourceRef)struct.getAttribute(AreaEntry.WMP_AREA_CURRENT); + IsReference areaRef = (IsReference)struct.getAttribute(AreaEntry.WMP_AREA_CURRENT); String text1 = null, text2 = null; if (areaName.getValue() >= 0) { StringTable.Format fmt = BrowserMenuBar.getInstance().showStrrefs() ? StringTable.Format.STRREF_SUFFIX diff --git a/src/org/infinity/resource/wmp/WmpResource.java b/src/org/infinity/resource/wmp/WmpResource.java index 01c6bd020..81cb7a4a1 100644 --- a/src/org/infinity/resource/wmp/WmpResource.java +++ b/src/org/infinity/resource/wmp/WmpResource.java @@ -11,7 +11,7 @@ import javax.swing.JComponent; import javax.swing.JTabbedPane; -import org.infinity.datatype.DecNumber; +import org.infinity.datatype.IsNumeric; import org.infinity.datatype.SectionCount; import org.infinity.datatype.SectionOffset; import org.infinity.datatype.TextString; @@ -82,7 +82,7 @@ public JComponent getViewerTab(int index) case 0: { JTabbedPane tabbedPane = new JTabbedPane(); - int count = ((DecNumber)getAttribute(WMP_NUM_MAPS)).getValue(); + int count = ((IsNumeric)getAttribute(WMP_NUM_MAPS)).getValue(); for (int i = 0; i < count; i++) { MapEntry entry = (MapEntry)getAttribute(MapEntry.WMP_MAP + " " + i); tabbedPane.addTab(entry.getName(), entry.getViewerTab(0)); diff --git a/src/org/infinity/search/AbstractSearcher.java b/src/org/infinity/search/AbstractSearcher.java index ea57bf4e4..c81f8147d 100644 --- a/src/org/infinity/search/AbstractSearcher.java +++ b/src/org/infinity/search/AbstractSearcher.java @@ -7,8 +7,10 @@ import java.awt.Component; import java.util.List; import java.util.concurrent.ThreadPoolExecutor; + import javax.swing.JOptionPane; import javax.swing.ProgressMonitor; + import org.infinity.NearInfinity; import org.infinity.resource.key.ResourceEntry; import org.infinity.util.Debugging; diff --git a/src/org/infinity/search/AttributeSearcher.java b/src/org/infinity/search/AttributeSearcher.java index 0f35a5180..9aeaa2a82 100644 --- a/src/org/infinity/search/AttributeSearcher.java +++ b/src/org/infinity/search/AttributeSearcher.java @@ -28,6 +28,7 @@ import javax.swing.JTextField; import org.infinity.datatype.DecNumber; +import org.infinity.datatype.IsNumeric; import org.infinity.gui.Center; import org.infinity.gui.ChildFrame; import org.infinity.icon.Icons; @@ -247,9 +248,9 @@ protected Runnable newWorker(ResourceEntry entry) if (rbexact.isSelected()) { hit = regPattern.matcher(searchEntry.toString()).matches(); } else if (rbless.isSelected()) { - hit = searchNumber > ((DecNumber)searchEntry).getValue(); + hit = searchNumber > ((IsNumeric)searchEntry).getValue(); } else if (rbgreater.isSelected()) { - hit = searchNumber < ((DecNumber)searchEntry).getValue(); + hit = searchNumber < ((IsNumeric)searchEntry).getValue(); } if (cbnot.isSelected()) { diff --git a/src/org/infinity/search/FileTypeSelector.java b/src/org/infinity/search/FileTypeSelector.java index ad4986dc0..b80280e02 100644 --- a/src/org/infinity/search/FileTypeSelector.java +++ b/src/org/infinity/search/FileTypeSelector.java @@ -15,10 +15,12 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; + import javax.swing.BorderFactory; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JPanel; + import org.infinity.icon.Icons; import org.infinity.resource.ResourceFactory; import org.infinity.resource.key.ResourceEntry; @@ -31,7 +33,6 @@ */ public class FileTypeSelector extends JPanel implements ActionListener { - // /** * Stores last chosed array of selected checkboxes for each resource type. * Each array has length equals to length of {@link #filetypes} @@ -52,7 +53,6 @@ public class FileTypeSelector extends JPanel implements ActionListener private final JButton bDefault = new JButton("Default", Icons.getIcon(Icons.ICON_UNDO_16)); /** Button that inverts check state of each checkbox. */ private final JButton bInvert = new JButton("Invert", Icons.getIcon(Icons.ICON_REFRESH_16)); - // /** * Creates panel with tho columns of checkboxex with abilities to check/unckeck all, @@ -148,7 +148,6 @@ public List getResources(String key) { return result; } - // @Override public void actionPerformed(ActionEvent e) { final Object src = e.getSource(); @@ -174,5 +173,4 @@ public void actionPerformed(ActionEvent e) { } } } - // } diff --git a/src/org/infinity/search/ReferenceHitFrame.java b/src/org/infinity/search/ReferenceHitFrame.java index e8957e501..2062a8e60 100644 --- a/src/org/infinity/search/ReferenceHitFrame.java +++ b/src/org/infinity/search/ReferenceHitFrame.java @@ -319,6 +319,19 @@ public String toString() } } + @Override + public int hashCode() + { + int hash = 7; + hash = 31 * hash + ((mode == null) ? 0 : mode.hashCode()); + hash = 31 * hash + ((entry == null) ? 0 : entry.hashCode()); + hash = 31 * hash + ((name == null) ? 0 : name.hashCode()); + hash = 31 * hash + ((ref == null) ? 0 : ref.hashCode()); + hash = 31 * hash + ((line == null) ? 0 : line.hashCode()); + hash = 31 * hash + lineNr; + return hash; + } + @Override public boolean equals(Object obj) { diff --git a/src/org/infinity/search/SearchFrame.java b/src/org/infinity/search/SearchFrame.java index e773d689b..93615f2be 100644 --- a/src/org/infinity/search/SearchFrame.java +++ b/src/org/infinity/search/SearchFrame.java @@ -294,7 +294,7 @@ else if (rbsto.isSelected()) List resources = ResourceFactory.getResources(selectedtype); String expr = tfield.getText().toLowerCase(Locale.ENGLISH); - List found = new ArrayList(); + List found = new ArrayList<>(); cards.show(bpanel, "Progress"); progress.setMaximum(resources.size()); for (int i = 0; i < resources.size(); i++) { diff --git a/src/org/infinity/search/SearchOptions.java b/src/org/infinity/search/SearchOptions.java index dac86327a..1a5afa891 100644 --- a/src/org/infinity/search/SearchOptions.java +++ b/src/org/infinity/search/SearchOptions.java @@ -19,8 +19,8 @@ import org.infinity.resource.AbstractStruct; import org.infinity.resource.ResourceFactory; import org.infinity.resource.StructEntry; -import org.infinity.util.Pair; import org.infinity.util.io.StreamUtils; +import org.infinity.util.tuples.Couple; /** * Stores a list of search options specified in SearchResource (Extended search) for use in the @@ -278,7 +278,7 @@ public class SearchOptions public static final String VVC_Custom4 = "VVC.+Custom.3"; - private final HashMap mapOptions = new HashMap(); + private final HashMap mapOptions = new HashMap<>(); private final String resourceType; /** @@ -552,10 +552,10 @@ public static boolean matchFlags(StructEntry flag, Object value) int v; boolean isExact; - if (value instanceof Pair && ((Pair)value).getFirst() instanceof Integer && - ((Pair)value).getSecond() instanceof Boolean) { - v = (Integer)((Pair)value).getFirst(); - isExact = (Boolean)((Pair)value).getSecond(); + if (value instanceof Couple && ((Couple)value).getValue0() instanceof Integer && + ((Couple)value).getValue1() instanceof Boolean) { + v = (Integer)((Couple)value).getValue0(); + isExact = (Boolean)((Couple)value).getValue1(); } else if (value instanceof Integer) { v = (Integer)value; isExact = false; @@ -589,11 +589,11 @@ public static boolean matchNumber(StructEntry number, Object value) int n1, n2, n3; if (value instanceof Integer) { n1 = n2 = (Integer)value; - } else if (value instanceof Pair && - ((Pair)value).getFirst() instanceof Integer && - ((Pair)value).getSecond() instanceof Integer) { - n1 = (Integer)((Pair)value).getFirst(); - n2 = (Integer)((Pair)value).getSecond(); + } else if (value instanceof Couple && + ((Couple)value).getValue0() instanceof Integer && + ((Couple)value).getValue1() instanceof Integer) { + n1 = (Integer)((Couple)value).getValue0(); + n2 = (Integer)((Couple)value).getValue1(); if (n1 > n2) { int tmp = n1; n1 = n2; n2 = tmp; } } else { return false; @@ -614,10 +614,10 @@ public static boolean matchNumber(StructEntry number, Object value) public static boolean matchCustomFilter(AbstractStruct struct, Object match) { - if (struct != null && match != null && match instanceof Pair && - ((Pair)match).getFirst() instanceof String) { - String fieldName = (String)((Pair)match).getFirst(); - Object value = ((Pair)match).getSecond(); + if (struct != null && match != null && match instanceof Couple && + ((Couple)match).getValue0() instanceof String) { + String fieldName = (String)((Couple)match).getValue0(); + Object value = ((Couple)match).getValue1(); if (!fieldName.isEmpty() && value != null) { boolean bRet = false; final List structList = struct.getFlatFields(); @@ -630,11 +630,11 @@ public static boolean matchCustomFilter(AbstractStruct struct, Object match) // field name matches if (value instanceof String) { bRet |= matchResourceRef(entry, value, false) || matchString(entry, value, false, false); - } else if (value instanceof Pair) { - if (((Pair)value).getFirst() instanceof Integer) { - if (((Pair)value).getSecond() instanceof Integer) { + } else if (value instanceof Couple) { + if (((Couple)value).getValue0() instanceof Integer) { + if (((Couple)value).getValue1() instanceof Integer) { bRet |= matchNumber(entry, value); - } else if (((Pair)value).getSecond() instanceof Boolean) { + } else if (((Couple)value).getValue1() instanceof Boolean) { bRet |= matchFlags(entry, value); } } diff --git a/src/org/infinity/search/SearchResource.java b/src/org/infinity/search/SearchResource.java index 5f39fc9c4..27ead6983 100644 --- a/src/org/infinity/search/SearchResource.java +++ b/src/org/infinity/search/SearchResource.java @@ -60,15 +60,13 @@ import javax.swing.text.PlainDocument; import org.infinity.NearInfinity; -import org.infinity.datatype.Bitmap; -import org.infinity.datatype.HashBitmap; import org.infinity.datatype.IdsBitmap; -import org.infinity.datatype.IwdRef; +import org.infinity.datatype.IsNumeric; import org.infinity.datatype.KitIdsBitmap; import org.infinity.datatype.PriTypeBitmap; import org.infinity.datatype.ProRef; +import org.infinity.datatype.ResourceBitmap; import org.infinity.datatype.SecTypeBitmap; -import org.infinity.datatype.Song2daBitmap; import org.infinity.datatype.TextBitmap; import org.infinity.gui.ButtonPopupWindow; import org.infinity.gui.ChildFrame; @@ -98,9 +96,9 @@ import org.infinity.util.Debugging; import org.infinity.util.IdsMapEntry; import org.infinity.util.Misc; -import org.infinity.util.Pair; import org.infinity.util.SimpleListModel; import org.infinity.util.io.StreamUtils; +import org.infinity.util.tuples.Couple; public class SearchResource extends ChildFrame implements ActionListener, PropertyChangeListener, Runnable @@ -138,7 +136,6 @@ public Void doInBackground() }).execute(); } - // @Override public void actionPerformed(ActionEvent event) { @@ -192,9 +189,7 @@ public void actionPerformed(ActionEvent event) } } } - // - // @Override public void propertyChange(PropertyChangeEvent event) { @@ -206,9 +201,7 @@ public void propertyChange(PropertyChangeEvent event) } } } - // - // @Override public void run() { @@ -278,7 +271,6 @@ public void run() } } } - // /** Initialize dialog. */ private void init() throws Exception @@ -660,7 +652,6 @@ public OptionsAREPanel(SearchResource searchResource) init(); } - // @Override public void actionPerformed(ActionEvent event) { @@ -701,7 +692,6 @@ public void actionPerformed(ActionEvent event) } } } - // @Override public SearchOptions getOptions() @@ -933,7 +923,6 @@ public OptionsCREPanel(SearchResource searchResource) init(); } - // @Override public void actionPerformed(ActionEvent event) { @@ -994,7 +983,6 @@ public void actionPerformed(ActionEvent event) } } } - // @Override public SearchOptions getOptions() @@ -1423,7 +1411,6 @@ public OptionsEFFPanel(SearchResource searchResource) init(); } - // @Override public void actionPerformed(ActionEvent event) { @@ -1475,7 +1462,6 @@ public void actionPerformed(ActionEvent event) } } } - // @Override public SearchOptions getOptions() @@ -1535,17 +1521,17 @@ public SearchOptions getOptions() return retVal; } - public Pair getOptionEffect() + public Couple getOptionEffect() { return Utils.getRangeValues(cbOptions[ID_Effect], sOpcode); } - public Pair getOptionParam1() + public Couple getOptionParam1() { return Utils.getRangeValues(cbOptions[ID_Param1], sParam1); } - public Pair getOptionParam2() + public Couple getOptionParam2() { return Utils.getRangeValues(cbOptions[ID_Param2], sParam2); } @@ -1728,7 +1714,6 @@ public OptionsITMPanel(SearchResource searchResource) init(); } - // @Override public void actionPerformed(ActionEvent event) { @@ -1787,7 +1772,6 @@ public void actionPerformed(ActionEvent event) } } } - // @Override public SearchOptions getOptions() @@ -1965,12 +1949,12 @@ public String getOptionAppearance() return Utils.getObjectFromString(cbOptions[ID_Appearance], cbAppearance).toString(); } - public Pair getOptionPrice() + public Couple getOptionPrice() { return Utils.getRangeValues(cbOptions[ID_Price], sPrice); } - public Pair getOptionEnchantment() + public Couple getOptionEnchantment() { return Utils.getRangeValues(cbOptions[ID_Enchantment], sEnchantment); } @@ -2177,7 +2161,6 @@ public OptionsPROPanel(SearchResource searchResource) init(); } - // @Override public void actionPerformed(ActionEvent event) { @@ -2233,7 +2216,6 @@ public void actionPerformed(ActionEvent event) } } } - // @Override public SearchOptions getOptions() @@ -2299,17 +2281,17 @@ public int getOptionType() return (Integer)Utils.getObjectFromString(cbOptions[ID_Type], cbType); } - public Pair getOptionSpeed() + public Couple getOptionSpeed() { return Utils.getRangeValues(cbOptions[ID_Speed], sSpeed); } - public Pair getOptionTrapSize() + public Couple getOptionTrapSize() { return Utils.getRangeValues(cbOptions[ID_TrapSize], sTrapSize); } - public Pair getOptionExplosionSize() + public Couple getOptionExplosionSize() { return Utils.getRangeValues(cbOptions[ID_ExplosionSize], sExplosionSize); } @@ -2495,7 +2477,6 @@ public OptionsSPLPanel(SearchResource searchResource) init(); } - // @Override public void actionPerformed(ActionEvent event) { @@ -2553,7 +2534,6 @@ public void actionPerformed(ActionEvent event) } } } - // @Override public SearchOptions getOptions() @@ -2684,7 +2664,7 @@ public int getOptionSecondaryType() return (Integer)Utils.getObjectFromString(cbOptions[ID_SecondaryType], cbSecondary); } - public Pair getOptionLevel() + public Couple getOptionLevel() { return Utils.getRangeValues(cbOptions[ID_Level], sLevel); } @@ -2881,7 +2861,6 @@ public OptionsSTOPanel(SearchResource searchResource) init(); } - // @Override public void actionPerformed(ActionEvent event) { @@ -2947,7 +2926,6 @@ public void actionPerformed(ActionEvent event) } } } - // @Override public SearchOptions getOptions() @@ -3047,27 +3025,27 @@ public int getOptionType() return (Integer)Utils.getObjectFromString(cbOptions[ID_Type], cbType); } - public Pair getOptionDepreciationRate() + public Couple getOptionDepreciationRate() { return Utils.getRangeValues(cbOptions[ID_Depreciation], sDepreciation); } - public Pair getOptionSellMarkup() + public Couple getOptionSellMarkup() { return Utils.getRangeValues(cbOptions[ID_SellMarkup], sSellMarkup); } - public Pair getOptionBuyMarkup() + public Couple getOptionBuyMarkup() { return Utils.getRangeValues(cbOptions[ID_BuyMarkup], sBuyMarkup); } - public Pair getOptionStealing() + public Couple getOptionStealing() { return Utils.getRangeValues(cbOptions[ID_Stealing], sStealing); } - public Pair getOptionCapacity() + public Couple getOptionCapacity() { return Utils.getRangeValues(cbOptions[ID_Capacity], sCapacity); } @@ -3265,7 +3243,6 @@ public OptionsVVCPanel(SearchResource searchResource) init(); } - // @Override public void actionPerformed(ActionEvent event) { @@ -3302,7 +3279,6 @@ public void actionPerformed(ActionEvent event) } } } - // @Override public SearchOptions getOptions() @@ -3464,7 +3440,7 @@ protected void triggerActions(Object[] sources) /** Creates a dialog that allows to specify flags. */ - private static final class FlagsPanel extends BasePanel implements ActionListener + private static final class FlagsPanel extends BasePanel { private final int size; private final JCheckBox[] cbFlags; @@ -3481,7 +3457,6 @@ public FlagsPanel(int size, String[] table) init(table); } - // @Override public void actionPerformed(ActionEvent event) { @@ -3499,11 +3474,10 @@ public void actionPerformed(ActionEvent event) } } } - // - public Pair getOptionFlags() + public Couple getOptionFlags() { - return new Pair<>(getFlagData(), isExact()); + return Couple.with(getFlagData(), isExact()); } private boolean isExact() @@ -3607,7 +3581,7 @@ private void init(String[] table) /** Creates a dialog that allows to specify effect opcodes for CRE resources. */ - private static final class EffectsPanel extends BasePanel implements ActionListener + private static final class EffectsPanel extends BasePanel { private static final int MaxEntryCount = 16; @@ -3627,7 +3601,6 @@ public EffectsPanel(int effectCount, String label) init(); } - // @Override public void actionPerformed(ActionEvent event) { @@ -3642,7 +3615,6 @@ public void actionPerformed(ActionEvent event) } } } - // public boolean isActive(int id) { @@ -3650,7 +3622,7 @@ public boolean isActive(int id) return cbLabel[id].isSelected(); } - public Pair getOptionEffect(int id) + public Couple getOptionEffect(int id) { return Utils.getRangeValues(cbLabel[id], sEffects[id]); } @@ -3698,7 +3670,7 @@ private void init() /** Creates a dialog that allows to specify custom filters for the selected resource type. */ - private static final class CustomFilterPanel extends BasePanel implements ActionListener + private static final class CustomFilterPanel extends BasePanel { private static final int FILTER_STRING = 0; private static final int FILTER_NUMBER = 1; @@ -3766,7 +3738,6 @@ public CustomFilterPanel(int filterCount, String label) init(); } - // @Override public void actionPerformed(ActionEvent event) { @@ -3802,7 +3773,6 @@ public void actionPerformed(ActionEvent event) } } } - // public boolean isActive(int id) { @@ -3810,23 +3780,23 @@ public boolean isActive(int id) return (cbLabel[id].isSelected() && !tfFieldName[id].getText().isEmpty()); } - public Pair getOptionFilter(int id) + public Couple getOptionFilter(int id) { if (id < 0) id = 0; else if (id >= entryCount) id = entryCount - 1; final String name = tfFieldName[id].getText(); if (cbLabel[id].isSelected() && !name.isEmpty()) { switch (cbFilterType[id].getSelectedIndex()) { case FILTER_STRING: - return new Pair<>(name, tfFieldValueString[id].getText()); + return Couple.with(name, tfFieldValueString[id].getText()); case FILTER_NUMBER: final Integer min = (Integer)sFieldValueNumber[id][0].getValue(); final Integer max = (Integer)sFieldValueNumber[id][1].getValue(); - return new Pair<>(name, new Pair<>(min, max)); + return Couple.with(name, Couple.with(min, max)); case FILTER_RESOURCE: final ResourceEntry entry = (ResourceEntry)cbFieldValueResource[id].getSelectedItem(); - return new Pair<>(name, entry.getResourceName()); + return Couple.with(name, entry.getResourceName()); case FILTER_FLAGS: - return new Pair<>(name, pFieldValueFlags[id].getOptionFlags()); + return Couple.with(name, pFieldValueFlags[id].getOptionFlags()); } } return null; @@ -3980,7 +3950,7 @@ private void init() /** Creates a dialog that allows to specify spell effect timing modes. */ - private static final class TimingModePanel extends BasePanel implements ActionListener + private static final class TimingModePanel extends BasePanel { public static final int TIMING_MODE = 0; public static final int TIMING_DURATION = 1; @@ -4000,7 +3970,6 @@ public TimingModePanel() init(); } - // @Override public void actionPerformed(ActionEvent event) { @@ -4024,7 +3993,6 @@ public void actionPerformed(ActionEvent event) } } } - // public boolean isActive(int id) { @@ -4038,7 +4006,7 @@ public int getOptionMode() ((IndexedString)cbMode.getSelectedItem()).index : 0; } - public Pair getOptionDuration() + public Couple getOptionDuration() { return Utils.getRangeValues(cbTiming[TIMING_DURATION], sDuration); } @@ -4083,7 +4051,7 @@ private void init() /** Creates a dialog that allows to specify creature level ranges. */ - private static final class CreLevelPanel extends BasePanel implements ActionListener + private static final class CreLevelPanel extends BasePanel { private static final String[] LABELS = { "First class level:", @@ -4100,7 +4068,6 @@ public CreLevelPanel() init(); } - // @Override public void actionPerformed(ActionEvent event) { @@ -4115,7 +4082,6 @@ public void actionPerformed(ActionEvent event) } } } - // public boolean isActive(int classIdx) { @@ -4123,7 +4089,7 @@ public boolean isActive(int classIdx) return cbLevel[classIdx].isSelected(); } - public Pair getOptionLevel(int classIdx) + public Couple getOptionLevel(int classIdx) { return Utils.getRangeValues(cbLevel[classIdx], sLevel[classIdx]); } @@ -4169,7 +4135,7 @@ private void init() /** Creates a dialog that allows to specify creature level ranges (IWD2-specific). */ - private static final class CreLevelIWD2Panel extends BasePanel implements ActionListener + private static final class CreLevelIWD2Panel extends BasePanel { public static final int LEVEL_TOTAL = 0; public static final int LEVEL_BARBARIAN = 1; @@ -4208,7 +4174,6 @@ public CreLevelIWD2Panel() init(); } - // @Override public void actionPerformed(ActionEvent event) { @@ -4223,7 +4188,6 @@ public void actionPerformed(ActionEvent event) } } } - // public boolean isActive(int id) { @@ -4231,7 +4195,7 @@ public boolean isActive(int id) return cbLevel[id].isSelected(); } - public Pair getOptionLevel(int id) + public Couple getOptionLevel(int id) { return Utils.getRangeValues(cbLevel[id], sLevel[id]); } @@ -4274,7 +4238,7 @@ private void init() /** Creates a dialog that allows to specify creature types. */ - private static final class CreTypePanel extends BasePanel implements ActionListener + private static final class CreTypePanel extends BasePanel { public static final int TYPE_GENERAL = 0; public static final int TYPE_CLASS = 1; @@ -4310,7 +4274,6 @@ public CreTypePanel() init(); } - // @Override public void actionPerformed(ActionEvent event) { @@ -4324,7 +4287,6 @@ public void actionPerformed(ActionEvent event) } } } - // public boolean isActive(int id) { @@ -4402,7 +4364,7 @@ private void init() /** Creates a dialog that allows to specify game-specific settings for CRE resources. */ - private static final class CreGameSpecificPanel extends BasePanel implements ActionListener + private static final class CreGameSpecificPanel extends BasePanel { public static final int TYPE_FEATS1 = 0; public static final int TYPE_FEATS2 = 1; @@ -4432,7 +4394,6 @@ public CreGameSpecificPanel() init(); } - // @Override public void actionPerformed(ActionEvent event) { @@ -4446,7 +4407,6 @@ public void actionPerformed(ActionEvent event) } } } - // public boolean isActive(int id) { @@ -4454,10 +4414,10 @@ public boolean isActive(int id) return cbLabel[id].isSelected(); } - public Pair getOptionFlags(int id) + public Couple getOptionFlags(int id) { if (id < 0) id = 0; else if (id >= LABELS.length) id = LABELS.length - 1; - return cbLabel[id].isSelected() ? pFlags[id].getOptionFlags() : new Pair<>(0, false); + return cbLabel[id].isSelected() ? pFlags[id].getOptionFlags() : Couple.with(0, false); } @@ -4515,7 +4475,7 @@ private void init() } /** Panel with several filters by resource reference. */ - private static class ResourcesFilterPanel extends BasePanel implements ActionListener + private static class ResourcesFilterPanel extends BasePanel { private final JCheckBox[] cbLabel; private final JComboBox[] cbItems; @@ -4563,7 +4523,6 @@ public ResourcesFilterPanel(int filterCount, String nameTemplate, String ext, bo add(panel, BorderLayout.CENTER); } - // @Override public void actionPerformed(ActionEvent event) { @@ -4577,7 +4536,6 @@ public void actionPerformed(ActionEvent event) } } } - // public boolean isActive(int id) { @@ -4592,7 +4550,7 @@ public String getResourceName(int id) /** Creates a dialog that allows to specify usability flags. */ - private static final class ItmUsabilityPanel extends BasePanel implements ActionListener + private static final class ItmUsabilityPanel extends BasePanel { public static final int ITEM_UNUSABLE = 0; public static final int ITEM_KITSUNUSABLE1 = 1; @@ -4618,7 +4576,6 @@ public ItmUsabilityPanel() init(); } - // @Override public void actionPerformed(ActionEvent event) { @@ -4632,7 +4589,6 @@ public void actionPerformed(ActionEvent event) } } } - // public boolean isActive(int id) { @@ -4640,9 +4596,9 @@ public boolean isActive(int id) return cbLabel[id].isSelected(); } - public Pair getOptionFlags(int id) + public Couple getOptionFlags(int id) { - return cbLabel[id].isSelected() ? pFlags[id].getOptionFlags() : new Pair<>(0, false); + return cbLabel[id].isSelected() ? pFlags[id].getOptionFlags() : Couple.with(0, false); } @@ -4711,7 +4667,7 @@ private void init() /** Creates a dialog that allows to specify minimum stats ranges. */ - private static final class ItmStatsPanel extends BasePanel implements ActionListener + private static final class ItmStatsPanel extends BasePanel { // supported stats public static final int STAT_LEVEL = 0; @@ -4743,7 +4699,6 @@ public ItmStatsPanel() init(); } - // @Override public void actionPerformed(ActionEvent event) { @@ -4758,7 +4713,6 @@ public void actionPerformed(ActionEvent event) } } } - // public boolean isActive(int statID) { @@ -4768,7 +4722,7 @@ public boolean isActive(int statID) return false; } - public Pair getOptionValue(int statID) + public Couple getOptionValue(int statID) { return Utils.getRangeValues(cbStats[statID], sStats[statID]); } @@ -4824,7 +4778,7 @@ private void init() /** Creates a dialog that allows to specify item ability properties. */ - private static final class ItmAbilityPanel extends BasePanel implements ActionListener + private static final class ItmAbilityPanel extends BasePanel { private static final int ITEM_TYPE = 0; private static final int ITEM_TARGET = 1; @@ -4874,7 +4828,6 @@ public ItmAbilityPanel() init(); } - // @Override public void actionPerformed(ActionEvent event) { @@ -4942,7 +4895,6 @@ public void actionPerformed(ActionEvent event) } } } - // public boolean isActive(int itemID) { @@ -4988,39 +4940,39 @@ public int getOptionDamageType() ((IndexedString)cbDamageType.getSelectedItem()).index : 0; } - public Pair getOptionRange() + public Couple getOptionRange() { return Utils.getRangeValues(cbItems[ITEM_RANGE], sRange); } - public Pair getOptionSpeed() + public Couple getOptionSpeed() { return Utils.getRangeValues(cbItems[ITEM_SPEED], sSpeed); } - public Pair getOptionDiceCount() + public Couple getOptionDiceCount() { return Utils.getRangeValues(cbItems[ITEM_DICECOUNT], sDiceCount); } - public Pair getOptionDiceSize() + public Couple getOptionDiceSize() { return Utils.getRangeValues(cbItems[ITEM_DICESIZE], sDiceSize); } - public Pair getOptionCharges() + public Couple getOptionCharges() { return Utils.getRangeValues(cbItems[ITEM_CHARGES], sCharges); } - public Pair getOptionEffects(int idx) + public Couple getOptionEffects(int idx) { - return cbItems[ITEM_EFFECTS].isSelected() ? pEffects.getOptionEffect(idx) : new Pair<>(0, 0); + return cbItems[ITEM_EFFECTS].isSelected() ? pEffects.getOptionEffect(idx) : Couple.with(0, 0); } - public Pair getOptionFlags() + public Couple getOptionFlags() { - return cbItems[ITEM_FLAGS].isSelected() ? flagsPanel.getOptionFlags() : new Pair<>(0, false); + return cbItems[ITEM_FLAGS].isSelected() ? flagsPanel.getOptionFlags() : Couple.with(0, false); } private void init() @@ -5038,10 +4990,12 @@ private void init() final IndexedString[] pro; if (ResourceFactory.resourceExists("PROJECTL.IDS")) { ProRef proRef = new ProRef(StreamUtils.getByteBuffer(2), 0, "Projectile"); - pro = new IndexedString[proRef.getResourceList().size()]; - for (int i = 0; i < pro.length; i++) { - long id = proRef.getResourceList().get(i).getValue(); - pro[i] = new IndexedString(proRef.getResourceList().get(i).getResourceName(), (int)id); + pro = new IndexedString[proRef.getBitmap().size()]; + int i = 0; + for (final ResourceBitmap.RefEntry entry : proRef.getBitmap().values()) { + long id = entry.getValue(); + pro[i] = new IndexedString(entry.getResourceName(), (int)id); + i++; } } else if (Profile.getEngine() == Profile.Engine.PST) { pro = IndexedString.createArray(AbstractAbility.s_proj_pst, 0, 0); @@ -5173,7 +5127,7 @@ private void init() /** Creates a dialog that allows to specify spell ability properties. */ - private static final class SplAbilityPanel extends BasePanel implements ActionListener + private static final class SplAbilityPanel extends BasePanel { private static final int SPELL_TYPE = 0; private static final int SPELL_LOCATION = 1; @@ -5211,7 +5165,6 @@ public SplAbilityPanel() init(); } - // @Override public void actionPerformed(ActionEvent event) { @@ -5261,7 +5214,6 @@ public void actionPerformed(ActionEvent event) } } } - // public boolean isActive(int spellID) { @@ -5295,17 +5247,17 @@ public int getOptionTarget() ((IndexedString)cbTarget.getSelectedItem()).index : 0; } - public Pair getOptionRange() + public Couple getOptionRange() { return Utils.getRangeValues(cbSpells[SPELL_RANGE], sRange); } - public Pair getOptionLevel() + public Couple getOptionLevel() { return Utils.getRangeValues(cbSpells[SPELL_LEVEL], sLevel); } - public Pair getOptionSpeed() + public Couple getOptionSpeed() { return Utils.getRangeValues(cbSpells[SPELL_SPEED], sSpeed); } @@ -5316,9 +5268,9 @@ public int getOptionProjectile() ((IndexedString)cbProjectile.getSelectedItem()).index : 0; } - public Pair getOptionEffects(int effectIdx) + public Couple getOptionEffects(int effectIdx) { - return cbSpells[SPELL_EFFECTS].isSelected() ? pEffects.getOptionEffect(effectIdx) : new Pair<>(0, 0); + return cbSpells[SPELL_EFFECTS].isSelected() ? pEffects.getOptionEffect(effectIdx) : Couple.with(0, 0); } @@ -5336,10 +5288,12 @@ private void init() final IndexedString[] pro; if (ResourceFactory.resourceExists("PROJECTL.IDS")) { ProRef proRef = new ProRef(StreamUtils.getByteBuffer(2), 0, "Projectile"); - pro = new IndexedString[proRef.getResourceList().size()]; - for (int i = 0; i < pro.length; i++) { - long id = proRef.getResourceList().get(i).getValue(); - pro[i] = new IndexedString(proRef.getResourceList().get(i).getResourceName(), (int)id); + pro = new IndexedString[proRef.getBitmap().size()]; + int i = 0; + for (final ResourceBitmap.RefEntry entry : proRef.getBitmap().values()) { + long id = entry.getValue(); + pro[i] = new IndexedString(entry.getResourceName(), (int)id); + i++; } } else if (Profile.getEngine() == Profile.Engine.PST) { pro = IndexedString.createArray(AbstractAbility.s_proj_pst, 1, 0); @@ -5439,7 +5393,7 @@ private void init() /** Creates a dialog that allows to specify item categories allowed in STO resources. */ - private static final class StoCategoriesPanel extends BasePanel implements ActionListener + private static final class StoCategoriesPanel extends BasePanel { private static final int MaxEntryCount = 16; @@ -5458,7 +5412,6 @@ public StoCategoriesPanel(int purchasedCount) init(); } - // @Override public void actionPerformed(ActionEvent event) { @@ -5472,7 +5425,6 @@ public void actionPerformed(ActionEvent event) } } } - // public boolean isActive(int index) { @@ -5768,7 +5720,7 @@ public static JComboBox getIdsMapEntryList(int bufSize, String name public static JComboBox getIdsMapEntryList(IdsBitmap ids) { - final SortedMap map = ids.getHashBitmap(); + final SortedMap map = ids.getBitmap(); final IdsMapEntry[] list = map.values().toArray(new IdsMapEntry[map.size()]); Arrays.sort(list); return defaultWidth(new AutoComboBox<>(list), 160); @@ -5812,11 +5764,11 @@ public static Object getObjectFromString(JCheckBox enabled, JComboBox getRangeValues(JCheckBox enabled, JSpinner[] spinner) + public static Couple getRangeValues(JCheckBox enabled, JSpinner[] spinner) { return enabled.isSelected() - ? new Pair<>((Integer)spinner[0].getValue(), (Integer)spinner[1].getValue()) - : new Pair<>(0, 0); + ? Couple.with((Integer)spinner[0].getValue(), (Integer)spinner[1].getValue()) + : Couple.with(0, 0); } /** Returns a protype dimension object based on the height of @(code c} and the width of (@code prototype}. */ @@ -5982,18 +5934,8 @@ private boolean compareItem(Object item, String pattern) // Check against specific datatypes for more accurate results if (curItem instanceof IdsMapEntry) { id = Long.toString(((IdsMapEntry)curItem).getID()); - } else if (curItem instanceof Bitmap) { - id = Integer.toString(((Bitmap)curItem).getValue()); - } else if (curItem instanceof HashBitmap) { - id = Long.toString(((HashBitmap)curItem).getValue()); - } else if (curItem instanceof IdsBitmap) { - id = Long.toString(((HashBitmap)curItem).getValue()); - } else if (curItem instanceof IwdRef) { - id = Long.toString(((IwdRef)curItem).getValue()); - } else if (curItem instanceof ProRef) { - id = Long.toString(((ProRef)curItem).getValue()); - } else if (curItem instanceof Song2daBitmap) { - id = Long.toString(((Song2daBitmap)curItem).getValue()); + } else if (curItem instanceof IsNumeric) { + id = Long.toString(((IsNumeric)curItem).getValue()); } else if (curItem instanceof TextBitmap) { id = ((TextBitmap)curItem).getDescription(); } else { diff --git a/src/org/infinity/search/SongReferenceSearcher.java b/src/org/infinity/search/SongReferenceSearcher.java index 310dd1539..3a4d2b9c2 100644 --- a/src/org/infinity/search/SongReferenceSearcher.java +++ b/src/org/infinity/search/SongReferenceSearcher.java @@ -50,7 +50,7 @@ public SongReferenceSearcher(ResourceEntry musPlaylist, Component parent) new boolean[]{true, true, false, true}, parent); long songId = -1L; final Song2daBitmap songBitmap = new Song2daBitmap(StreamUtils.getByteBuffer(4), 0, 4); - for (final RefEntry refEntry : songBitmap.getResourceList()) { + for (final RefEntry refEntry : songBitmap.getBitmap().values()) { final ResourceEntry entry = refEntry.getResourceEntry(); if (entry != null && entry.equals(musPlaylist)) { songId = refEntry.getValue(); diff --git a/src/org/infinity/search/advanced/AdvancedSearchWorker.java b/src/org/infinity/search/advanced/AdvancedSearchWorker.java index b51e104a7..76ffc0293 100644 --- a/src/org/infinity/search/advanced/AdvancedSearchWorker.java +++ b/src/org/infinity/search/advanced/AdvancedSearchWorker.java @@ -186,7 +186,7 @@ private void collapseGroupFilters(Map, Set> groupCache Set groupSet = entry.getValue(); Map> structureMap = new HashMap<>(); for (StructEntry se : groupSet) { - Set structureSet = structureMap.computeIfAbsent(se.getParent(), e -> new HashSet()); + Set structureSet = structureMap.computeIfAbsent(se.getParent(), e -> new HashSet<>()); structureSet.add(se); } @@ -225,7 +225,7 @@ private void collapseGroupFilters(Map, Set> groupCache // 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 set = groupCache.computeIfAbsent(so.getStructure(), s -> new HashSet<>()); set.add(se); } @@ -392,8 +392,7 @@ private boolean isMatchResource(StructEntry se, String resref, String ext) private boolean isMatchBitfield(StructEntry se, int value, SearchOptions.BitFieldMode mode) { if (se instanceof Flag) { - Flag flag = (Flag)se; - int bits = flag.getValue(); + int bits = ((IsNumeric)se).getValue(); switch (mode) { case Exact: return bits == value; diff --git a/src/org/infinity/search/advanced/FilterInput.java b/src/org/infinity/search/advanced/FilterInput.java index 24e04b6c4..06702cf03 100644 --- a/src/org/infinity/search/advanced/FilterInput.java +++ b/src/org/infinity/search/advanced/FilterInput.java @@ -68,7 +68,7 @@ import org.infinity.resource.Profile; import org.infinity.resource.key.ResourceEntry; import org.infinity.resource.ui.ResourceListModel; -import org.infinity.util.ObjectString; +import org.infinity.util.DataString; /** * Input dialog for a advanced search filter definition. @@ -105,14 +105,15 @@ public class FilterInput extends ChildFrame private JPanel pFieldInput; private CardLayout clFieldInput, clValueInput; - private JComboBox cbFieldType; + private JComboBox> cbFieldType; private JTextField tfFieldName, tfValueStringInput; private JFormattedTextField ftfFieldOffsetInput; private JCheckBox cbFieldNameCase, cbFieldNameRegex; private JPopupMenu menuFieldOffset; private JPanel pValueInput; - private JComboBox cbValueType, cbValueBitfieldMode; + private JComboBox> cbValueType; + private JComboBox> cbValueBitfieldMode; private JComboBox cbValueResourceInput; private JComboBox cbValueResourceType; private JFormattedTextField ftfValueInputMin, ftfValueInputMax; @@ -323,11 +324,13 @@ private JPanel initFieldInput() { GridBagConstraints c = new GridBagConstraints(); JLabel lFieldType = new JLabel("Search field by:"); - cbFieldType = new JComboBox<>(new ObjectString[] { - new ObjectString("Name", SearchOptions.FieldMode.ByName, "%s"), - new ObjectString("Relative Offset", SearchOptions.FieldMode.ByRelativeOffset, "%s"), - new ObjectString("Absolute Offset", SearchOptions.FieldMode.ByAbsoluteOffset, "%s"), - }); + DefaultComboBoxModel> modelFieldType = + new DefaultComboBoxModel>() {{ + addElement(DataString.with("Name", SearchOptions.FieldMode.ByName, "%s")); + addElement(DataString.with("Relative Offset", SearchOptions.FieldMode.ByRelativeOffset, "%s")); + addElement(DataString.with("Absolute Offset", SearchOptions.FieldMode.ByAbsoluteOffset, "%s")); + }}; + cbFieldType = new JComboBox<>(modelFieldType); cbFieldType.setSelectedIndex(0); cbFieldType.addActionListener(listeners); JPanel pFieldType = new JPanel(new GridBagLayout()); @@ -384,12 +387,14 @@ private JPanel initValueInput() { GridBagConstraints c = new GridBagConstraints(); JLabel lValueType = new JLabel("Value type:"); - cbValueType = new JComboBox<>(new ObjectString[] { - new ObjectString("Text", SearchOptions.ValueType.Text, "%s"), - new ObjectString("Number", SearchOptions.ValueType.Number, "%s"), - new ObjectString("Resource", SearchOptions.ValueType.Resource, "%s"), - new ObjectString("Bitfield", SearchOptions.ValueType.Bitfield, "%s"), - }); + DefaultComboBoxModel> modelValueType = + new DefaultComboBoxModel>() {{ + addElement(DataString.with("Text", SearchOptions.ValueType.Text, "%s")); + addElement(DataString.with("Number", SearchOptions.ValueType.Number, "%s")); + addElement(DataString.with("Resource", SearchOptions.ValueType.Resource, "%s")); + addElement(DataString.with("Bitfield", SearchOptions.ValueType.Bitfield, "%s")); + }}; + cbValueType = new JComboBox<>(modelValueType); cbValueType.setSelectedIndex(0); cbValueType.addActionListener(listeners); @@ -454,12 +459,14 @@ private JPanel initValueInput() bpwValueBitfield = new ButtonPopupWindow("Set options...", new FlagsPanel(4, flagsDesc)); JLabel lValueBitfieldMode = new JLabel("Mode:"); - cbValueBitfieldMode = new JComboBox<>(new ObjectString[] { - new ObjectString("Exact match", SearchOptions.BitFieldMode.Exact, "%s"), - new ObjectString("Match all set bits (AND)", SearchOptions.BitFieldMode.And, "%s"), - new ObjectString("Match any set bits (OR)", SearchOptions.BitFieldMode.Or, "%s"), - new ObjectString("Match one set bit (XOR)", SearchOptions.BitFieldMode.Xor, "%s"), - }); + DefaultComboBoxModel> modelValueBitfieldMode = + new DefaultComboBoxModel>() {{ + addElement(DataString.with("Exact match", SearchOptions.BitFieldMode.Exact, "%s")); + addElement(DataString.with("Match all set bits (AND)", SearchOptions.BitFieldMode.And, "%s")); + addElement(DataString.with("Match any set bits (OR)", SearchOptions.BitFieldMode.Or, "%s")); + addElement(DataString.with("Match one set bit (XOR)", SearchOptions.BitFieldMode.Xor, "%s")); + }}; + cbValueBitfieldMode = new JComboBox<>(modelValueBitfieldMode); JPanel pValueBitfield = new JPanel(new GridBagLayout()); c = ViewerUtil.setGBC(c, 0, 0, 2, 1, 1, 0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); pValueBitfield.add(bpwValueBitfield, c); @@ -548,11 +555,12 @@ private void importOptions(SearchOptions so) // field input { - DefaultComboBoxModel model = (DefaultComboBoxModel)cbFieldType.getModel(); + DefaultComboBoxModel> model = + (DefaultComboBoxModel>)cbFieldType.getModel(); int idx = 0; for (int i = 0; i < model.getSize(); i++) { - ObjectString os = model.getElementAt(i); - if (so.getSearchType().equals(os.getObject())) { + DataString os = model.getElementAt(i); + if (so.getSearchType().equals(os.getData())) { idx = i; break; } @@ -567,11 +575,12 @@ private void importOptions(SearchOptions so) // value input { - DefaultComboBoxModel modelValue = (DefaultComboBoxModel)cbValueType.getModel(); + DefaultComboBoxModel> modelValue = + (DefaultComboBoxModel>)cbValueType.getModel(); int idx = 0; for (int i = 0; i < modelValue.getSize(); i++) { - ObjectString os = modelValue.getElementAt(i); - if (so.getValueType().equals(os.getObject())) { + DataString os = modelValue.getElementAt(i); + if (so.getValueType().equals(os.getData())) { idx = i; break; } @@ -612,11 +621,12 @@ private void importOptions(SearchOptions so) FlagsPanel fp = (FlagsPanel)bpwValueBitfield.getContent(); fp.setValue(so.getValueBitfield()); - modelValue = (DefaultComboBoxModel)cbValueBitfieldMode.getModel(); + DefaultComboBoxModel> modelMode = + (DefaultComboBoxModel>)cbValueBitfieldMode.getModel(); idx = 0; for (int i = 0; i < modelValue.getSize(); i++) { - ObjectString os = modelValue.getElementAt(i); - if (so.getBitfieldMode().equals(os.getObject())) { + DataString os = modelMode.getElementAt(i); + if (so.getBitfieldMode().equals(os.getData())) { idx = i; break; } @@ -639,9 +649,9 @@ private void exportOptions(SearchOptions so) // value input { - ObjectString os = cbValueType.getModel().getElementAt(cbValueType.getSelectedIndex()); + DataString os = cbValueType.getModel().getElementAt(cbValueType.getSelectedIndex()); if (os != null) { - SearchOptions.ValueType type = (SearchOptions.ValueType)os.getObject(); + SearchOptions.ValueType type = os.getData(); switch (type) { case Text: so.setValueText(tfValueStringInput.getText(), cbValueStringCase.isSelected(), cbValueStringRegex.isSelected()); @@ -662,8 +672,9 @@ private void exportOptions(SearchOptions so) case Bitfield: { FlagsPanel fp = (FlagsPanel)bpwValueBitfield.getContent(); - ObjectString osMode = cbValueBitfieldMode.getModel().getElementAt(cbValueBitfieldMode.getSelectedIndex()); - so.setValueBitfield(fp.getValue(), (SearchOptions.BitFieldMode)osMode.getObject()); + DataString osMode = + cbValueBitfieldMode.getModel().getElementAt(cbValueBitfieldMode.getSelectedIndex()); + so.setValueBitfield(fp.getValue(), osMode.getData()); break; } } @@ -674,9 +685,9 @@ private void exportOptions(SearchOptions so) // field input { - ObjectString os = cbFieldType.getModel().getElementAt(cbFieldType.getSelectedIndex()); + DataString os = cbFieldType.getModel().getElementAt(cbFieldType.getSelectedIndex()); if (os != null) { - SearchOptions.FieldMode mode = (SearchOptions.FieldMode)os.getObject(); + SearchOptions.FieldMode mode = os.getData(); switch (mode) { case ByName: so.setSearchName(tfFieldName.getText(), cbFieldNameCase.isSelected(), cbFieldNameRegex.isSelected()); @@ -886,8 +897,8 @@ public void actionPerformed(ActionEvent event) } else if (event.getSource() == cbFieldType) { // switch field type panel if (cbFieldType.getSelectedIndex() >= 0) { - ObjectString os = cbFieldType.getModel().getElementAt(cbFieldType.getSelectedIndex()); - SearchOptions.FieldMode mode = (SearchOptions.FieldMode)os.getObject(); + DataString os = cbFieldType.getModel().getElementAt(cbFieldType.getSelectedIndex()); + SearchOptions.FieldMode mode = os.getData(); switch (mode) { case ByName: clFieldInput.show(pFieldInput, FIELD_TYPE_TEXT); @@ -907,8 +918,8 @@ public void actionPerformed(ActionEvent event) } else if (event.getSource() == cbValueType) { // switch value type panel if (cbValueType.getSelectedIndex() >= 0) { - ObjectString os = cbValueType.getModel().getElementAt(cbValueType.getSelectedIndex()); - SearchOptions.ValueType mode = (SearchOptions.ValueType)os.getObject(); + DataString os = cbValueType.getModel().getElementAt(cbValueType.getSelectedIndex()); + SearchOptions.ValueType mode = os.getData(); switch (mode) { case Number: clValueInput.show(pValueInput, VALUE_TYPE_NUMBER); diff --git a/src/org/infinity/search/advanced/NumberFormatterEx.java b/src/org/infinity/search/advanced/NumberFormatterEx.java index b4d7b76f5..8280cf589 100644 --- a/src/org/infinity/search/advanced/NumberFormatterEx.java +++ b/src/org/infinity/search/advanced/NumberFormatterEx.java @@ -10,7 +10,7 @@ import javax.swing.JFormattedTextField.AbstractFormatter; -import org.infinity.util.ObjectString; +import org.infinity.util.DataString; /** * A simplified NumberFormatter that handles conversion from Object to String and back. @@ -85,9 +85,9 @@ public Object stringToValue(String text) throws ParseException currentValue = convertToNumber(text); switch (numberFormat) { case hexadecimal: - return new ObjectString(String.format(fmtHex, currentValue), currentValue, ""); + return DataString.with(String.format(fmtHex, currentValue), Long.valueOf(currentValue), ""); default: - return new ObjectString(String.format(fmtDec, currentValue), currentValue, ""); + return DataString.with(String.format(fmtDec, currentValue), Long.valueOf(currentValue), ""); } } @@ -96,9 +96,9 @@ public String valueToString(Object value) throws ParseException { long retVal = currentValue; if (value != null) { - if (value instanceof ObjectString && ((ObjectString) value).getObject() instanceof Number) { - ObjectString os = (ObjectString) value; - Number n = (Number) os.getObject(); + if (value instanceof DataString && ((DataString)value).getData() instanceof Number) { + DataString os = (DataString) value; + Number n = (Number)os.getData(); retVal = getBoundedValue(n.longValue()); } else { retVal = convertToNumber(value.toString()); @@ -168,13 +168,13 @@ public long getNumericValue() public long getNumericValue(Object value) throws ParseException { Number n = null; - if (value instanceof ObjectString && ((ObjectString) value).getObject() instanceof Number) { - n = (Number) ((ObjectString) value).getObject(); + if (value instanceof DataString && ((DataString)value).getData() instanceof Number) { + n = (Number)((DataString)value).getData(); } else if (value instanceof Number) { n = (Number) value; } else { - ObjectString os = (ObjectString) stringToValue(value.toString()); - n = (Number) os.getObject(); + DataString os = (DataString)stringToValue(value.toString()); + n = (Number)os.getData(); } return getBoundedValue(n.longValue()); diff --git a/src/org/infinity/updater/UpdateInfo.java b/src/org/infinity/updater/UpdateInfo.java index 4aca4687e..713945b7e 100644 --- a/src/org/infinity/updater/UpdateInfo.java +++ b/src/org/infinity/updater/UpdateInfo.java @@ -22,8 +22,8 @@ import javax.xml.parsers.DocumentBuilderFactory; import org.infinity.util.Misc; -import org.infinity.util.Pair; import org.infinity.util.io.StreamUtils; +import org.infinity.util.tuples.Couple; import org.w3c.dom.Document; import org.w3c.dom.DocumentType; import org.w3c.dom.Element; @@ -78,7 +78,7 @@ public enum FileType { private static final String ATTR_VERSION = "version"; private static final String ATTR_TYPE = "type"; - private final EnumMap releases = new EnumMap(ReleaseType.class); + private final EnumMap releases = new EnumMap<>(ReleaseType.class); private General general; private int version; @@ -279,8 +279,8 @@ private void parseGeneral(Element elemGeneral) throws Exception throw new Exception("Update.xml: Node \"" + NODE_GENERAL + "\" expected"); } - List serverList = new ArrayList(); - List> infoList = new ArrayList>(); + List serverList = new ArrayList<>(); + List> infoList = new ArrayList<>(); NodeList children = elemGeneral.getChildNodes(); for (int idx = 0, size = children.getLength(); idx < size; idx++) { @@ -322,7 +322,7 @@ private void parseGeneral(Element elemGeneral) throws Exception try { String name = n1.getTextContent().trim(); URL url = new URL(n2.getTextContent().trim()); - infoList.add(new Pair(name, url.toExternalForm())); + infoList.add(Couple.with(name, url.toExternalForm())); } catch (MalformedURLException e) { // don't add invalid URLs } @@ -402,7 +402,7 @@ private void parseRelease(Element elemRelease) throws Exception // processing optional element "changelog" if (elemChangelog != null) { - changelog = new ArrayList(); + changelog = new ArrayList<>(); children = elemChangelog.getElementsByTagName(NODE_ENTRY); for (int idx = 0, size = children.getLength(); idx < size; idx++) { Element elem = (Element)children.item(idx); @@ -427,10 +427,10 @@ private void parseRelease(Element elemRelease) throws Exception // Manages "General" information public static class General { - private final List servers = new ArrayList(); - private final List> information = new ArrayList>(); + private final List servers = new ArrayList<>(); + private final List> information = new ArrayList<>(); - private General(List servers, List> information) throws Exception + private General(List servers, List> information) throws Exception { if (servers != null) { this.servers.addAll(servers); @@ -460,7 +460,7 @@ public String getServer(int index) public String getInformationName(int index) { if (index >= 0 && index < getInformationCount()) { - return information.get(index).getFirst(); + return information.get(index).getValue0(); } else { return null; } @@ -470,7 +470,7 @@ public String getInformationName(int index) public String getInformationLink(int index) { if (index >= 0 && index < getInformationCount()) { - return information.get(index).getSecond(); + return information.get(index).getValue1(); } else { return null; } @@ -500,7 +500,7 @@ public boolean isValid() // Manages "Release" information public static class Release { - private final List changelog = new ArrayList(); + private final List changelog = new ArrayList<>(); private final ReleaseType type; private String fileName, link, linkManual, version, hash; private FileType linkType; diff --git a/src/org/infinity/updater/Updater.java b/src/org/infinity/updater/Updater.java index fb4348ca7..380794d28 100644 --- a/src/org/infinity/updater/Updater.java +++ b/src/org/infinity/updater/Updater.java @@ -11,7 +11,12 @@ import java.net.Proxy; import java.net.URL; import java.nio.file.Path; -import java.util.*; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; import java.util.jar.JarFile; import java.util.prefs.Preferences; import java.util.zip.ZipEntry; @@ -81,7 +86,7 @@ public class Updater private static Updater instance = null; - private final List serverList = new ArrayList(); + private final List serverList = new ArrayList<>(); private Preferences prefs; private String hash, version, timestamp; @@ -160,21 +165,16 @@ static Calendar getJarFileDate() { String jarPath = Utils.getJarFileName(NearInfinity.class); if (jarPath != null && !jarPath.isEmpty()) { - try { - JarFile jf = new JarFile(jarPath); - try { - ZipEntry manifest = jf.getEntry("META-INF/MANIFEST.MF"); - if (manifest != null) { - Calendar cal = Calendar.getInstance(); - if (manifest.getTime() >= 0L) { - cal.setTimeInMillis(manifest.getTime()); - return cal; - } + try (JarFile jf = new JarFile(jarPath)) { + ZipEntry manifest = jf.getEntry("META-INF/MANIFEST.MF"); + if (manifest != null) { + Calendar cal = Calendar.getInstance(); + if (manifest.getTime() >= 0L) { + cal.setTimeInMillis(manifest.getTime()); + return cal; } - } finally { - jf.close(); } - } catch (Exception e) { + } catch (IOException e) { e.printStackTrace(); } } diff --git a/src/org/infinity/updater/UpdaterSettings.java b/src/org/infinity/updater/UpdaterSettings.java index 594a04039..2dbfd7c1a 100644 --- a/src/org/infinity/updater/UpdaterSettings.java +++ b/src/org/infinity/updater/UpdaterSettings.java @@ -480,9 +480,9 @@ public void changedUpdate(DocumentEvent e) // Manages server URL entries private class Server { - private final List listServer = new ArrayList(); - private final List listCheck = new ArrayList(); - private final List listValidated = new ArrayList(); + private final List listServer = new ArrayList<>(); + private final List listCheck = new ArrayList<>(); + private final List listValidated = new ArrayList<>(); private final int numServers; diff --git a/src/org/infinity/updater/Utils.java b/src/org/infinity/updater/Utils.java index 0bc13ccff..4a456d44c 100644 --- a/src/org/infinity/updater/Utils.java +++ b/src/org/infinity/updater/Utils.java @@ -4,6 +4,8 @@ package org.infinity.updater; +import static org.infinity.util.Misc.toNumber; + import java.awt.Desktop; import java.io.ByteArrayOutputStream; import java.io.File; @@ -48,8 +50,6 @@ import org.infinity.util.io.FileEx; -import static org.infinity.util.Misc.toNumber; - /** * Generic collection of updater-related methods. */ @@ -322,7 +322,6 @@ public static boolean isUrlAvailable(URL url, Proxy proxy) } } else { // more generic method - InputStream is = null; URLConnection conn = null; if (proxy != null) { conn = url.openConnection(proxy); @@ -330,9 +329,10 @@ public static boolean isUrlAvailable(URL url, Proxy proxy) conn = url.openConnection(); } if (conn != null) { - is = url.openStream(); - is.close(); - return true; + try (InputStream is = url.openStream()) { + return true; + } catch (IOException e) { + } } } } @@ -554,9 +554,8 @@ public static boolean downloadFromUrl(URL url, Proxy proxy, OutputStream os, Upd if (conn != null) { int timeout = conn.getConnectTimeout(); conn.setConnectTimeout(6000); // wait max. 6 seconds - InputStream is = conn.getInputStream(); - conn.setConnectTimeout(timeout); - try { + try (InputStream is = conn.getInputStream()) { + conn.setConnectTimeout(timeout); switch (type) { case ORIGINAL: return downloadRaw(is, os, url, proxy, listeners); @@ -567,9 +566,6 @@ public static boolean downloadFromUrl(URL url, Proxy proxy, OutputStream os, Upd case UNKNOWN: return false; } - } finally { - is.close(); - is = null; } } } @@ -624,9 +620,8 @@ static boolean downloadZip(InputStream is, OutputStream os, URL url, Proxy proxy throws IOException, ZipException { if (is != null && os != null) { - ZipInputStream zis = new ZipInputStream(is); - byte[] buffer = new byte[4096]; - try { + try (ZipInputStream zis = new ZipInputStream(is)) { + byte[] buffer = new byte[4096]; ZipEntry entry = zis.getNextEntry(); if (entry != null) { int totalSize = (int)entry.getSize(); @@ -644,10 +639,6 @@ static boolean downloadZip(InputStream is, OutputStream os, URL url, Proxy proxy fireProgressEvent(listeners, url, curSize, totalSize, true); return true; } - } finally { - zis.close(); - zis = null; - buffer = null; } } return false; diff --git a/src/org/infinity/util/BinPack2D.java b/src/org/infinity/util/BinPack2D.java index cc99d4c70..ab9e7542e 100644 --- a/src/org/infinity/util/BinPack2D.java +++ b/src/org/infinity/util/BinPack2D.java @@ -10,6 +10,8 @@ import java.util.Iterator; import java.util.List; +import org.infinity.util.tuples.Couple; + /** * Implementation of a two-dimensional rectangle bin packing algorithm. * (Port of Jukka Jylänki's C++ implementation of RectangleBinPack->MaxRectsBinPack.) @@ -33,8 +35,8 @@ public enum HeuristicRules { CONTACT_POINT_RULE, } - private final List usedRectangles = new ArrayList(); - private final List freeRectangles = new ArrayList(); + private final List usedRectangles = new ArrayList<>(); + private final List freeRectangles = new ArrayList<>(); private int binWidth, binHeight; @@ -159,14 +161,14 @@ public void insert(List rects, List dst, HeuristicRules ru int bestRectIndex = -1; Rectangle bestNode = null; - Pair score = new Pair(0, 0); + Couple score = Couple.with(0, 0); for (int i = 0, size = rects.size(); i < size; i++) { Dimension d = rects.get(i); Rectangle newNode = scoreRect(d.width, d.height, rule, score); - if (score.getFirst() < bestScore1 || - (score.getFirst() == bestScore1 && score.getSecond() < bestScore2)) { - bestScore1 = score.getFirst(); - bestScore2 = score.getSecond(); + if (score.getValue0() < bestScore1 || + (score.getValue0() == bestScore1 && score.getValue1() < bestScore2)) { + bestScore1 = score.getValue0(); + bestScore2 = score.getValue1(); bestNode = newNode; bestRectIndex = i; } @@ -253,13 +255,13 @@ public float getOccupancy() * @param score (out) Returns the primary and secondary placement score. * @return This struct identifies where the rectangle would be placed if it were placed. */ - private Rectangle scoreRect(int width, int height, HeuristicRules rule, Pair score) + private Rectangle scoreRect(int width, int height, HeuristicRules rule, Couple score) { - if (score == null) { score = new Pair(0, 0); } + if (score == null) { score = Couple.with(0, 0); } Rectangle newNode = null; - score.setFirst(Integer.MAX_VALUE); - score.setSecond(Integer.MAX_VALUE); + score.setValue0(Integer.MAX_VALUE); + score.setValue1(Integer.MAX_VALUE); switch (rule) { case BEST_SHORT_SIDE_FIT: newNode = findPositionForNewNodeBestShortSideFit(width, height, score); @@ -269,7 +271,7 @@ private Rectangle scoreRect(int width, int height, HeuristicRules rule, Pair bestPos) + // bestPos.getValue0(): bestY, bestPos.getValue1(): bestX + private Rectangle findPositionForNewNodeBottomLeft(int width, int height, Couple bestPos) { - if (bestPos == null) { bestPos = new Pair(0, 0); } + if (bestPos == null) { bestPos = Couple.with(0, 0); } Rectangle bestNode = new Rectangle(); - bestPos.setFirst(Integer.MAX_VALUE); + bestPos.setValue0(Integer.MAX_VALUE); for (int i = 0, size = freeRectangles.size(); i < size; i++) { // Try to place the rectangle in upright (non-flipped) orientation. Rectangle r = freeRectangles.get(i); if (r.width >= width && r.height >= height) { int topSideY = r.y + height; - if (topSideY < bestPos.getFirst() || - (topSideY == bestPos.getFirst() && r.x < bestPos.getSecond())) { + if (topSideY < bestPos.getValue0() || + (topSideY == bestPos.getValue0() && r.x < bestPos.getValue1())) { bestNode.x = r.x; bestNode.y = r.y; bestNode.width = width; bestNode.height = height; - bestPos.setSecond(r.x); - bestPos.setFirst(topSideY); + bestPos.setValue1(r.x); + bestPos.setValue0(topSideY); } } } @@ -361,13 +363,13 @@ private Rectangle findPositionForNewNodeBottomLeft(int width, int height, Pair bestFit) + // bestFit.getValue0(): short side, bestFit.getValue1(): long side + private Rectangle findPositionForNewNodeBestShortSideFit(int width, int height, Couple bestFit) { - if (bestFit == null) { bestFit = new Pair(0, 0); } + if (bestFit == null) { bestFit = Couple.with(0, 0); } Rectangle bestNode = new Rectangle(); - bestFit.setFirst(Integer.MAX_VALUE); + bestFit.setValue0(Integer.MAX_VALUE); for (int i = 0, size = freeRectangles.size(); i < size; i++) { // Try to place the rectangle in upright (non-flipped) orientation. Rectangle r = freeRectangles.get(i); @@ -377,14 +379,14 @@ private Rectangle findPositionForNewNodeBestShortSideFit(int width, int height, int shortSideFit = Math.min(leftoverHoriz, leftoverVert); int longSideFit = Math.max(leftoverHoriz, leftoverVert); - if (shortSideFit < bestFit.getFirst() || - (shortSideFit == bestFit.getFirst() && longSideFit < bestFit.getSecond())) { + if (shortSideFit < bestFit.getValue0() || + (shortSideFit == bestFit.getValue0() && longSideFit < bestFit.getValue1())) { bestNode.x = r.x; bestNode.y = r.y; bestNode.width = width; bestNode.height = height; - bestFit.setFirst(shortSideFit); - bestFit.setSecond(longSideFit); + bestFit.setValue0(shortSideFit); + bestFit.setValue1(longSideFit); } } } @@ -392,13 +394,13 @@ private Rectangle findPositionForNewNodeBestShortSideFit(int width, int height, return bestNode; } - // bestFit.getFirst(): short side, bestFit.getSecond(): long side - private Rectangle findPositionForNewNodeBestLongSideFit(int width, int height, Pair bestFit) + // bestFit.getValue0(): short side, bestFit.getValue1(): long side + private Rectangle findPositionForNewNodeBestLongSideFit(int width, int height, Couple bestFit) { - if (bestFit == null) { bestFit = new Pair(0, 0); } + if (bestFit == null) { bestFit = Couple.with(0, 0); } Rectangle bestNode = new Rectangle(); - bestFit.setSecond(Integer.MAX_VALUE); + bestFit.setValue1(Integer.MAX_VALUE); for (int i = 0, size = freeRectangles.size(); i < size; i++) { // Try to place the rectangle in upright (non-flipped) orientation. Rectangle r = freeRectangles.get(i); @@ -408,14 +410,14 @@ private Rectangle findPositionForNewNodeBestLongSideFit(int width, int height, P int shortSideFit = Math.min(leftoverHoriz, leftoverVert); int longSideFit = Math.max(leftoverHoriz, leftoverVert); - if (longSideFit < bestFit.getSecond() || - (longSideFit == bestFit.getSecond() && shortSideFit < bestFit.getFirst())) { + if (longSideFit < bestFit.getValue1() || + (longSideFit == bestFit.getValue1() && shortSideFit < bestFit.getValue0())) { bestNode.x = r.x; bestNode.y = r.y; bestNode.width = width; bestNode.height = height; - bestFit.setFirst(shortSideFit); - bestFit.setSecond(longSideFit); + bestFit.setValue0(shortSideFit); + bestFit.setValue1(longSideFit); } } } @@ -423,13 +425,13 @@ private Rectangle findPositionForNewNodeBestLongSideFit(int width, int height, P return bestNode; } - // bestFit.getFirst(): area, bestFit.getSecond(): short side - private Rectangle findPositionForNewNodeBestAreaFit(int width, int height, Pair bestFit) + // bestFit.getValue0(): area, bestFit.getValue1(): short side + private Rectangle findPositionForNewNodeBestAreaFit(int width, int height, Couple bestFit) { - if (bestFit == null) { bestFit = new Pair(0, 0); } + if (bestFit == null) { bestFit = Couple.with(0, 0); } Rectangle bestNode = new Rectangle(); - bestFit.setFirst(Integer.MAX_VALUE); + bestFit.setValue0(Integer.MAX_VALUE); for (int i = 0, size = freeRectangles.size(); i < size; i++) { Rectangle r = freeRectangles.get(i); int areaFit = r.width*r.height - width*height; @@ -440,14 +442,14 @@ private Rectangle findPositionForNewNodeBestAreaFit(int width, int height, Pair< int leftoverVert = Math.abs(r.height - height); int shortSideFit = Math.min(leftoverHoriz, leftoverVert); - if (areaFit < bestFit.getFirst() || - (areaFit == bestFit.getFirst() && shortSideFit < bestFit.getSecond())) { + if (areaFit < bestFit.getValue0() || + (areaFit == bestFit.getValue0() && shortSideFit < bestFit.getValue1())) { bestNode.x = r.x; bestNode.y = r.y; bestNode.width = width; bestNode.height = height; - bestFit.setFirst(areaFit); - bestFit.setSecond(shortSideFit); + bestFit.setValue0(areaFit); + bestFit.setValue1(shortSideFit); } } } @@ -455,25 +457,25 @@ private Rectangle findPositionForNewNodeBestAreaFit(int width, int height, Pair< return bestNode; } - // bestScore.getFirst(): contact, bestScore.getSecond(): unused - private Rectangle findPositionForNewNodeContactPoint(int width, int height, Pair bestScore) + // bestScore.getValue0(): contact, bestScore.getValue1(): unused + private Rectangle findPositionForNewNodeContactPoint(int width, int height, Couple bestScore) { - if (bestScore == null) { bestScore = new Pair(0, 0); } + if (bestScore == null) { bestScore = Couple.with(0, 0); } Rectangle bestNode = new Rectangle(); - bestScore.setFirst(-1); + bestScore.setValue0(-1); for (int i = 0, size = freeRectangles.size(); i < size; i++) { // Try to place the rectangle in upright (non-flipped) orientation. Rectangle r = freeRectangles.get(i); if (r.width >= width && r.height >= height) { int score = contactPointScoreNode(r.x, r.y, width, height); - if (score > bestScore.getFirst()) + if (score > bestScore.getValue0()) { bestNode.x = r.x; bestNode.y = r.y; bestNode.width = width; bestNode.height = height; - bestScore.setFirst(score); + bestScore.setValue0(score); } } } diff --git a/src/org/infinity/util/DataString.java b/src/org/infinity/util/DataString.java new file mode 100644 index 000000000..92dccb170 --- /dev/null +++ b/src/org/infinity/util/DataString.java @@ -0,0 +1,232 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.util; + +/** + * Associates a string with a typed object. Both string and object definitions are immutable. + * @param type of the data object associated with the {@code DataString}. + */ +public class DataString implements CharSequence, Comparable +{ + /** Print {@code String} together with associated data object in parentheses. */ + public static final String FMT_DATA_BRACKETS = "%s (%s)"; + + /** Print associated data object together with {@code String} in parentheses. */ + public static final String FMT_DATA_BRACKETS_REVERSED = "%2$s (%1$s)"; + + /** Print {@code String} together with associated data object after hyphen. */ + public static final String FMT_DATA_HYPHEN = "%s - %s"; + + /** Print associated data object together with {@code String} after hyphen. */ + public static final String FMT_DATA_HYPHEN_REVERSED = "%2$s - %1$s"; + + /** Print only the String part of the {@code DataString} instance. */ + public static final String FMT_STRING_ONLY = "%1$s"; + + /** Print only the data object part of the {@code DataString} instance. */ + public static final String FMT_DATA_ONLY = "%2$s"; + + + private final String text; + private final T data; + + private String fmt; + + /** + * Creates a new {@code DataString} instance with the specified parameters. + * @param the data object type. + * @param s the associated {@code String}. + * @param data the associated data of type {@code T}. + * @return a new {@code DataString} object. + */ + public static DataString with(String s, T data) + { + return new DataString<>(s, data); + } + + /** + * Creates a new {@code DataString} instance with the specified parameters. + * @param the data object type. + * @param s the associated {@code String}. + * @param data the associated data of type {@code T}. + * @param fmt a formatter string used by the {@link #toString()} method. Specifying {@code null} results in using + * the {@link #FMT_STRING_ONLY} formatter string. + * @return a new {@code DataString} object. + */ + public static DataString with(String s, T data, String fmt) + { + return new DataString<>(s, data, fmt); + } + + /** + * Constructs a new {@code DataString} object and initializes it with the specified arguments. + * @param s the associated {@code String}. + * @param data the associated data of type {@code T}. + */ + public DataString(String s, T data) + { + this(s, data, null); + } + + /** + * Constructs a new {@code DataString} object and initializes it with the specified arguments. + * @param s the associated {@code String}. + * @param data the associated data of type {@code T}. + * @param fmt a formatter string used by the {@link #toString()} method. Specifying {@code null} results in using + * the {@link #FMT_STRING_ONLY} formatter string. + */ + public DataString(String s, T data, String fmt) + { + super(); + this.text = (s != null) ? s : ""; + this.data = data; + setFormatter(fmt); + } + +//--------------------- Begin Interface CharSequence --------------------- + + /** Returns the length of the formatted string returned by {@link #toString()}. */ + @Override + public int length() + { + return toString().length(); + } + + /** + * Returns the {@code char} value at the specified index. An index ranges from {@code 0} to {@code length() - 1}. + * The first char value of the sequence is at index {@code 0}, the next at index {@code 1}, and so on, + * as for array indexing. + *

    The method is applied to the formatted string returned by {@link #toString()}. + *

    If the {@code char} value specified by the index is a surrogate, the surrogatevalue is returned. + */ + @Override + public char charAt(int index) + { + return toString().charAt(index); + } + + /** + * Returns a {@code CharSequence} that is a subsequence of this sequence. The subsequence starts with the + * {@code char} value at the specified index and ends with the char value at index {@code end - 1}. + * The length (in {@code char}s) of there turned sequence is {@code end - start}, so if {@code start == end} + * then an empty sequence is returned. + *

    The method is applied to the formatted string returned by {@link #toString()}. + * @param start Index the begin index, inclusive. + * @param end the end index, exclusive. + * @return the specified subsequence. + */ + @Override + public CharSequence subSequence(int start, int end) + { + return toString().subSequence(start, end); + } + + /** + * Returns a string with the content controlled by the associated formatter string. + */ + @Override + public String toString() + { + return String.format(getFormatter(), getString(), getData()); + } + +//--------------------- End Interface CharSequence --------------------- + +//--------------------- Begin Interface Comparable --------------------- + + /** + * Compares this object with the specified object for order. Returns anegative integer, zero, + * or a positive integer as this object is lessthan, equal to, or greater than the specified object. + *

    The method is applied to the formatted string returned by {@link #toString()}. + */ + @Override + public int compareTo(CharSequence o) + { + if (o == null) { + return 1; + } + return toString().compareTo(o.toString()); + } + +//--------------------- End Interface Comparable --------------------- + + @Override + public boolean equals(Object anObject) + { + if (this == anObject) { + return true; + } + if (!(anObject instanceof DataString)) { + return false; + } + DataString other = (DataString)anObject; + boolean retVal = text.equals(other.text); + retVal &= (data == null && other.data == null) || + (data != null && data.equals(other.data)); + return retVal; + } + + @Override + public int hashCode() + { + int hash = 7; + hash = 31 * hash + text.hashCode(); + hash = 31 * hash + ((data == null) ? 0 : data.hashCode()); + return hash; + } + + /** + * Returns the raw {@code String} associated with the {@code DataString}. + * @return {@code String} part of the {@code DataString} + */ + public String getString() + { + return text; + } + + /** + * Returns the data object associated with the {@code DataString}. + * @return data object of type {@link T}. + */ + public T getData() + { + return data; + } + + /** + * Returns the formatter string used by {@link #toString()} to generate the return value. + */ + public String getFormatter() + { + return fmt; + } + + /** + *

    Sets the formatter string that is used by {@link #toString()} to generate the return value. + *

    The formatter string may have up to two format specifiers. The first specifier is used for the string portion + * of the {@code DataString}. The second specifier is used for the data portion of the {@code DataString}. + *

    The position flag of the specifiers can be used to control order of output. + * @param fmt the formatter string. Specifying {@code null} results in using the {@link #FMT_STRING_ONLY} + * formatter string. + */ + public void setFormatter(String fmt) + { + if (fmt == null) { + fmt = FMT_STRING_ONLY; + } + if (!fmt.equals(this.fmt)) { + this.fmt = fmt; + } + } + + /** + * Returns {@code true} if, and only if, {@link #length()} is {@code 0}. + * @return {@code true} if {@link #length()} is {@code null}, otherwise {@code false}. + */ + public boolean isEmpty() + { + return toString().isEmpty(); + } +} diff --git a/src/org/infinity/util/FilteredListModel.java b/src/org/infinity/util/FilteredListModel.java index ed1b29a80..bb011e787 100644 --- a/src/org/infinity/util/FilteredListModel.java +++ b/src/org/infinity/util/FilteredListModel.java @@ -22,8 +22,8 @@ */ public class FilteredListModel extends AbstractListModel { - private final Vector list = new Vector(); - private final Vector filteredList = new Vector(); + private final Vector list = new Vector<>(); + private final Vector filteredList = new Vector<>(); private final ArrayList listeners = new ArrayList<>(); private boolean filtered; @@ -94,7 +94,7 @@ public ChangeListener[] getFilterChangeListeners() public void removeFilterChangeListener(ChangeListener listener) { if (listener != null) { - while (listeners.remove(listener)) ; + while (listeners.remove(listener)) {} } } diff --git a/src/org/infinity/util/IdsMap.java b/src/org/infinity/util/IdsMap.java index 7d54529ee..5804da5d7 100644 --- a/src/org/infinity/util/IdsMap.java +++ b/src/org/infinity/util/IdsMap.java @@ -61,13 +61,13 @@ public int size() /** Returns a copy of the values contained in the IDS map. */ public List getAllValues() { - return new ArrayList(idsMap.values()); + return new ArrayList<>(idsMap.values()); } /** Returns a copy of the keys contained in the IDS map as a sorted set. */ public SortedSet getKeys() { - return new TreeSet(idsMap.keySet()); + return new TreeSet<>(idsMap.keySet()); } /** Returns the entry structure defined by the specified IDS value, or {@code null} otherwise. */ @@ -221,7 +221,8 @@ private void extractIDS(String line) private long normalizedKey(long key) { - return key & 0xffffffffL; + // cap to 32 bit but preserve sign + return key % 0x100000000L; } private String normalizedString(String s) diff --git a/src/org/infinity/util/IniMap.java b/src/org/infinity/util/IniMap.java index 244eaea9f..ade9eda19 100644 --- a/src/org/infinity/util/IniMap.java +++ b/src/org/infinity/util/IniMap.java @@ -26,6 +26,27 @@ public class IniMap implements Iterable private static final Pattern LINE_SPLIT = Pattern.compile("\r?\n"); private final List entries = new ArrayList<>(); + /** + * Creates a {@code IniMap} instance from the specified collection of strings. + * Each string is considered as a separate line. + * @param lines Collection of strings + * @return an {@code IniMap} object with the content of the strings. Returns {@code null} if no strings were provided. + */ + public static IniMap from(List lines) + { + IniMap ini = null; + if (lines != null) { + StringBuilder sb = new StringBuilder(); + for(final String line : lines) { + sb.append(line != null ? line : "").append('\n'); + } + if (sb.length() > 0) { + ini = new IniMap(sb.toString(), true); + } + } + return ini; + } + /** * Parses specified text content as {@code ini} file with comments (comment * starts from {@code //} and continues to end of string). @@ -67,7 +88,10 @@ public IniMap(CharSequence content, boolean ignoreComments) curSectionLine = i; section.clear(); } else { // potential section entry - section.add(parseEntry(line, i, ignoreComments)); + IniMapEntry entry = parseEntry(line, i, ignoreComments); + if (entry != null) { + section.add(entry); + } } } @@ -143,7 +167,7 @@ public String toString() * to end of line) will not be treated specially (i.e. will not be considered * as comments) * - * @return New object, that represent entry in INI. Never {@code null} + * @return New object, that represent entry in INI. Returns {@code null} if entry is not valid. */ private IniMapEntry parseEntry(String line, int lineNr, boolean ignoreComments) { @@ -167,7 +191,11 @@ private IniMapEntry parseEntry(String line, int lineNr, boolean ignoreComments) value = line.substring(start, pos).trim(); } - return new IniMapEntry(key, value, lineNr); + if (key != null || value != null) { + return new IniMapEntry(key, value, lineNr); + } else { + return null; + } } private static String readResource(ResourceEntry entry) diff --git a/src/org/infinity/util/IniMapCache.java b/src/org/infinity/util/IniMapCache.java index 2f9799ade..907c42985 100644 --- a/src/org/infinity/util/IniMapCache.java +++ b/src/org/infinity/util/IniMapCache.java @@ -12,7 +12,7 @@ public class IniMapCache { - private static final HashMap map = new HashMap(); + private static final HashMap map = new HashMap<>(); public static void cacheInvalid(ResourceEntry entry) { diff --git a/src/org/infinity/util/IniMapEntry.java b/src/org/infinity/util/IniMapEntry.java index 2c6127902..156e94728 100644 --- a/src/org/infinity/util/IniMapEntry.java +++ b/src/org/infinity/util/IniMapEntry.java @@ -40,6 +40,9 @@ public IniMapEntry(String key, String value, int line) public Integer getIntValue() { return value == null ? null : Integer.valueOf(value); } public int getIntValue(int defValue) { return value == null ? defValue : Integer.valueOf(value); } + public Double getDoubleValue() { return value == null ? null : Double.valueOf(value); } + public double getDoubleValue(double defValue) { return value == null ? defValue : Double.valueOf(value); } + public StringRef getStringRefValue() { return value == null ? null : new StringRef(key, Integer.valueOf(value)); } @@ -81,7 +84,7 @@ public static String[] splitValues(String value, String pattern) { String[] retVal = null; if (value != null) { - List results = new ArrayList(); + List results = new ArrayList<>(); try { Matcher matcher = Pattern.compile(pattern).matcher(value); while (matcher.find()) { @@ -109,7 +112,7 @@ public static int[] splitObjectValue(String value) { int[] retVal = null; if (value != null && Pattern.matches("^\\[(-?\\d+\\.?)+\\]$", value)) { - List results = new ArrayList(); + List results = new ArrayList<>(); Pattern p = Pattern.compile("-?\\d+"); Matcher m = p.matcher(value); while (m.find()) { @@ -138,7 +141,7 @@ public static int[] splitPositionValue(String value) { int[] retVal = null; if (value != null && Pattern.matches("^\\[[-0-9]+\\.[-0-9]+(:[0-9]+)?\\]$", value)) { - List results = new ArrayList(); + List results = new ArrayList<>(); Pattern p = Pattern.compile("-?\\d+"); Matcher m = p.matcher(value); while (m.find()) { diff --git a/src/org/infinity/util/IniMapSection.java b/src/org/infinity/util/IniMapSection.java index 02ae35345..0e7a13d57 100644 --- a/src/org/infinity/util/IniMapSection.java +++ b/src/org/infinity/util/IniMapSection.java @@ -75,7 +75,6 @@ public int getEntryCount() return entries.size(); } - // /** * Returns the first instance with {@code "key"} matching the key value of the * entry. Performs case-insensitive search. @@ -109,13 +108,29 @@ public IniMapEntry getEntry(String key) * {@code key}, or {@code null}, if no such pair exists */ public String getAsString(String key) + { + return getAsString(key, null); + } + /** + * Returns value for specified key as string. Returns a default value if key or + * value are not present. + * + * @param key Name of value in key-value pair. This key must not contain spaces + * because all spaces are cut off during parsing if ini-file, so key with + * leading or trailing spaces never will be found + * + * @return Value of first key-value pair, which key equals (ignoring case) parameter + * {@code key}, or {@code defValue}, if no such pair exists or value is + * not defined. + */ + public String getAsString(String key, String defValue) { final IniMapEntry entry = getEntry(key); - return entry == null ? null : entry.getValue(); + return entry == null ? defValue : entry.getValue(); } /** * Returns value for specified key as integer or returns default value, if key - * or valus is not presented in the file. + * or value is not presented in the file. * * @param key Name of value in key-value pair. This key must not contain spaces * because all spaces are cut off during parsing if ini-file, so key with @@ -133,6 +148,26 @@ public int getAsInteger(String key, int defValue) final IniMapEntry entry = getEntry(key); return entry == null ? defValue : entry.getIntValue(defValue); } + /** + * Returns value for specified key as double or returns default value, if key + * or value is not presented in the file. + * + * @param key Name of value in key-value pair. This key must not contain spaces + * because all spaces are cut off during parsing if ini-file, so key with + * leading or trailing spaces never will be found + * @param defValue Default value that will be returned, if key or value does not exist + * + * @return Value of first key-value pair, which key equals (ignoring case) parameter + * {@code key}, converted to integer, or {@code defValue}, if no such pair + * exists or value is not defined + * + * @throws NumberFormatException If value is not a double + */ + public double getAsDouble(String key, double defValue) + { + final IniMapEntry entry = getEntry(key); + return entry == null ? defValue : entry.getDoubleValue(defValue); + } /** * Returns value for specified key as string reference. {@link StringRef#getName} * will return {@code key} as its name. @@ -152,7 +187,6 @@ public StringRef getAsStringRef(String key) final IniMapEntry entry = getEntry(key); return entry == null ? null : entry.getStringRefValue(); } - // @Override public Iterator iterator() { return entries.iterator(); } diff --git a/src/org/infinity/util/LongIntegerHashMap.java b/src/org/infinity/util/LongIntegerHashMap.java deleted file mode 100644 index c1d420a2b..000000000 --- a/src/org/infinity/util/LongIntegerHashMap.java +++ /dev/null @@ -1,44 +0,0 @@ -// Near Infinity - An Infinity Engine Browser and Editor -// Copyright (C) 2001 - 2019 Jon Olav Hauglid -// See LICENSE.txt for license information - -package org.infinity.util; - -import java.util.Iterator; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; - -public final class LongIntegerHashMap extends TreeMap -{ - public LongIntegerHashMap() - { - super(); - } - - @Override - public String toString() - { - final StringBuilder buf = new StringBuilder(); - buf.append('{'); - Set> set = entrySet(); - Iterator> i = set.iterator(); - boolean hasNext = i.hasNext(); - while (hasNext) { - Map.Entry e = i.next(); - long key = e.getKey(); - V value = e.getValue(); - buf.append(key); - buf.append('='); - if (value == this) - buf.append("(this Map)"); - else - buf.append(value); - hasNext = i.hasNext(); - if (hasNext) - buf.append(", "); - } - buf.append('}'); - return buf.toString(); - } -} diff --git a/src/org/infinity/util/LuaParser.java b/src/org/infinity/util/LuaParser.java index b96b6abe2..210738302 100644 --- a/src/org/infinity/util/LuaParser.java +++ b/src/org/infinity/util/LuaParser.java @@ -91,7 +91,7 @@ public static LuaEntry Parse(String data, String name, boolean exactMatch) throw name = Pattern.quote(name); LuaEntry root = new LuaEntry(0); - root.children = new ArrayList(); + root.children = new ArrayList<>(); // search for table definitions // Example: mytable = {"value1", "value2"} @@ -153,7 +153,7 @@ private static LuaEntry ParseElement(CharBuffer buffer, LuaEntry parent) throws if (retVal == null) retVal = (key.length() > 0) ? new LuaEntry(key.toString()) : new LuaEntry(parent); if (retVal.children == null) - retVal.children = new ArrayList(); + retVal.children = new ArrayList<>(); LuaEntry el = ParseElement(buffer, retVal); if (el != null) retVal.children.add(el); @@ -223,7 +223,7 @@ private static LuaEntry ParseElement(CharBuffer buffer, LuaEntry parent) throws buffer.position(buffer.position() + 4); } else if (ch == '{') { if (retVal.children == null) - retVal.children = new ArrayList(); + retVal.children = new ArrayList<>(); LuaEntry el = ParseElement(buffer, retVal); if (el != null) retVal.children.add(el); diff --git a/src/org/infinity/util/MapTree.java b/src/org/infinity/util/MapTree.java index c9144d61a..087225d4a 100644 --- a/src/org/infinity/util/MapTree.java +++ b/src/org/infinity/util/MapTree.java @@ -31,9 +31,9 @@ public class MapTree implements Cloneable /** Creates a path of MapTree objects, starting from the root node up to the specified node. */ public static Collection> getNodePath(MapTree node) { - Collection> retVal = new Vector>(); + Collection> retVal = new Vector<>(); if (node != null) { - Stack> stack = new Stack>(); + Stack> stack = new Stack<>(); MapTree curNode = node; while (curNode != null) { stack.push(curNode); @@ -71,7 +71,7 @@ public MapTree(K key, V value) throw new NullPointerException("key must not be null"); } this.parent = null; - this.children = new HashMap>(); + this.children = new HashMap<>(); this.key = key; this.value = value; } @@ -86,7 +86,7 @@ public MapTree(K key, V value) @SuppressWarnings("unchecked") public Object clone() { - MapTree node = new MapTree(key, value); + MapTree node = new MapTree<>(key, value); for (Iterator> iter = children.values().iterator(); iter.hasNext();) { node.addChild((MapTree)iter.next().clone()); } @@ -172,7 +172,7 @@ public MapTree findNode(K key) */ public Collection> findNodes(K key) { - Collection> retVal = new Vector>(); + Collection> retVal = new Vector<>(); if (key != null) { retVal = findNodesRecursive(retVal, parent, key, false); } @@ -246,7 +246,7 @@ public MapTree removeChild(K key) */ public Collection> removeChildren(Collection keys) { - Collection> retVal = new Vector>(); + Collection> retVal = new Vector<>(); if (keys != null && !keys.isEmpty()) { for (Iterator iter = keys.iterator(); iter.hasNext();) { MapTree node = removeChild(iter.next()); @@ -302,7 +302,7 @@ private static Collection> findNodesRecursive(Collection>(); + retVal = new Vector<>(); } if (firstMatch && !retVal.isEmpty()) { diff --git a/src/org/infinity/util/MassExporter.java b/src/org/infinity/util/MassExporter.java index 5b7c43005..5b61dd454 100644 --- a/src/org/infinity/util/MassExporter.java +++ b/src/org/infinity/util/MassExporter.java @@ -288,7 +288,7 @@ public void run() } } - selectedFiles = new ArrayList(1000); + selectedFiles = new ArrayList<>(1000); for (final String newVar : selectedTypes) { selectedFiles.addAll(ResourceFactory.getResources(newVar, extraDirs)); } diff --git a/src/org/infinity/util/Misc.java b/src/org/infinity/util/Misc.java index b4af02055..9e8e1b73c 100644 --- a/src/org/infinity/util/Misc.java +++ b/src/org/infinity/util/Misc.java @@ -8,6 +8,7 @@ import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; +import java.lang.reflect.Constructor; import java.nio.charset.Charset; import java.util.Comparator; import java.util.concurrent.ArrayBlockingQueue; @@ -409,27 +410,60 @@ public static int getScaledValue(int value) public static String prettifySymbol(String symbol) { if (symbol != null) { - StringBuilder sb = new StringBuilder(symbol); - boolean upper = true; - for (int idx = 0, len = sb.length(); idx < len; idx++) { - char ch = sb.charAt(idx); - if (upper) { - ch = Character.toUpperCase(ch); - upper = false; - } else { - ch = Character.toLowerCase(ch); - } + StringBuilder sb = new StringBuilder(); + boolean isUpper = false; + boolean isDigit = false; + boolean isPrevUpper = false; + boolean isPrevDigit = false; + boolean toUpper = true; + for (int idx = 0, len = symbol.length(); idx < len; idx++) { + char ch = symbol.charAt(idx); if (" ,-_".indexOf(ch) >= 0) { - if (ch == '_') ch = ' '; - if (ch == '-') { - sb.insert(idx, " -"); - ch = ' '; - idx += 2; - len += 2; + // improve spacing + switch (ch) { + case '_': + sb.append(' '); + break; + case '-': + sb.append(" - "); + break; + default: + sb.append(ch); + } + toUpper = true; + } else { + if (toUpper) { + ch = Character.toUpperCase(ch); + toUpper = false; + } + isPrevUpper = isUpper; + isPrevDigit = isDigit; + isUpper = Character.isUpperCase(ch); + isDigit = Character.isDigit(ch); + if (idx > 0) { + // detect word boundaries + char chPrev = sb.charAt(sb.length() - 1); + if (chPrev != ' ') { + if (isUpper && !isPrevUpper && !isPrevDigit) { + sb.append(' '); + } else if (isDigit && !isPrevDigit) { + sb.append(' '); + } + } + + chPrev = sb.charAt(sb.length() - 1); + if (isUpper && chPrev != ' ') { + // prevent upper case characters in the middle of words + ch = Character.toLowerCase(ch); + } + + if (!isUpper && chPrev == ' ') { + // new words start with upper case character + ch = Character.toUpperCase(ch); + } } - upper = true; + sb.append(ch); } - sb.setCharAt(idx, ch); } symbol = sb.toString(); } @@ -540,6 +574,75 @@ public static String safeToString(Object o) return (o != null) ? o.toString() : ""; } + /** + * This method throws a general {@link Exception} without message if the specified condition isn't met. + * @param cond the condition to meet. + * @throws Exception + */ + public static void requireCondition(boolean cond) throws Exception + { + requireCondition(cond, null, null); + } + + /** + * This method throws a general {@link Exception} with associated message if the specified condition isn't met. + * @param cond the condition to meet. + * @param message the exception message. Can be {@code null}. + * @throws Exception + */ + public static void requireCondition(boolean cond, String message) throws Exception + { + requireCondition(cond, message, null); + } + + /** + * This method throws a specialized exception without message if the specified condition isn't met. + * @param cond the condition to meet. + * @param classEx the exception class to throw. + * @throws Exception + */ + public static void requireCondition(boolean cond, Class classEx) throws Exception + { + requireCondition(cond, null, classEx); + } + + /** + * This method throws a specialized exception with associated message if the specified condition isn't met. + * @param cond the condition to meet. + * @param message the exception message. Can be {@code null}. + * @param classEx the exception class to throw. + * @throws Exception + */ + public static void requireCondition(boolean cond, String message, Class classEx) throws Exception + { + if (!cond) { + if (message != null && message.isEmpty()) + { + message = null; + } + + if (classEx == null) { + classEx = Exception.class; + } + + for (final Class cls : new Class[] { classEx, Exception.class }) { + Object ex = null; + if (message != null) { + Constructor ctor = cls.getConstructor(String.class); + ex = ctor.newInstance(message); + } else { + Constructor ctor = cls.getConstructor(); + ex = ctor.newInstance(); + } + + if (ex instanceof Exception) { + throw (Exception)ex; + } + } + } + } + + // Contains static functions only private Misc() {} } diff --git a/src/org/infinity/util/ObjectString.java b/src/org/infinity/util/ObjectString.java deleted file mode 100644 index eb2b6d4cf..000000000 --- a/src/org/infinity/util/ObjectString.java +++ /dev/null @@ -1,171 +0,0 @@ -// Near Infinity - An Infinity Engine Browser and Editor -// Copyright (C) 2001 - 2019 Jon Olav Hauglid -// See LICENSE.txt for license information - -package org.infinity.util; - -import java.util.Map; - -/** - * Associates a string with an object. Both string and object definitions are immutable. - */ -public class ObjectString implements CharSequence, Comparable -{ - /** Print String together with associated object in parentheses. */ - public static final String FMT_OBJECT_BRACKETS = "%1$s (%2$s)"; - /** Print String together with associated object after hyphen. */ - public static final String FMT_OBJECT_HYPHEN = "%1$s - %2$s"; - /** Print only Object part of the ObjectString instance. */ - public static final String FMT_STRING_ONLY = "%1$s"; - /** Print only String part of the ObjectString instance. */ - public static final String FMT_OBJECT_ONLY = "%2$s"; - - private final String string; - private final Object object; - - private final String displayFormat; - - /** - * Helper routine: Associate strings with objects. - */ - public static ObjectString[] createObjectStrings(String[] strings, Object[] objects, String fmt) - { - ObjectString[] retVal = null; - if (strings != null && objects != null) { - int size = Math.max(strings.length, objects.length); - retVal = new ObjectString[size]; - for (int i = 0; i < size; i++) { - String s = (i < strings.length) ? strings[i] : ""; - Object o = (i < objects.length) ? objects[i] : null; - retVal[i] = new ObjectString(s, o, fmt); - } - } else { - retVal = new ObjectString[0]; - } - return retVal; - } - - /** - * Helper routine: Automatically create string/index pairs from string array. - */ - public static ObjectString[] createIndexedStrings(String[] strings, int startIndex, int ofsIndex, - String fmt) - { - ObjectString[] retVal = null; - if (strings != null && startIndex < strings.length) { - retVal = new ObjectString[strings.length - startIndex]; - for (int i = startIndex; i < strings.length; i++) { - retVal[i - startIndex] = new ObjectString(strings[i], Integer.valueOf(i - startIndex + ofsIndex), fmt); - } - } else { - retVal = new ObjectString[0]; - } - return retVal; - } - - /** - * Helper routine: Automatically create string/index pairs from HashBitmap source. - */ - public static ObjectString[] createIndexedStrings(LongIntegerHashMap map, String fmt) - { - if (map == null) { - return new ObjectString[0]; - } - final ObjectString[] retVal = new ObjectString[map.size()]; - int i = 0; - for (Map.Entry e : map.entrySet()) { - retVal[i] = new ObjectString(e.getValue().toString(), Integer.valueOf(e.getKey().intValue()), fmt); - ++i; - } - return retVal; - } - - /** - * Constructs an ObjectString with the specified String and object fields. - */ - public ObjectString(String s, Object object) - { - this(s, object, null); - } - - /** - * Constructs an ObjectString with the specified String and object fields. - * @param fmt Indicates how {@code toString()} should display the formatted string. - */ - public ObjectString(String s, Object object, String fmt) - { - this.string = (s != null) ? s : ""; - this.object = object; - this.displayFormat = (fmt != null) ? fmt : FMT_OBJECT_BRACKETS; - } - - @Override - public boolean equals(Object o) - { - boolean eq = false; - if (o instanceof ObjectString) { - ObjectString os = (ObjectString)o; - eq = string.equals(os.getString()); - if (eq) { - eq = (object == null && os.getObject() == null) || - (object != null && object.equals(os.getObject())); - } - } - return eq; - } - -//--------------------- Begin Interface CharSequence --------------------- - - @Override - public int length() - { - return string.length(); - } - - @Override - public char charAt(int index) - { - return string.charAt(index); - } - - @Override - public CharSequence subSequence(int start, int end) - { - return string.subSequence(start, end); - } - - @Override - public String toString() - { - return String.format(displayFormat, string, (object != null) ? object.toString() : "null"); - } - -//--------------------- End Interface CharSequence --------------------- - -//--------------------- Begin Interface Comparable --------------------- - - @Override - public int compareTo(ObjectString o) - { - if (o != null) { - return getString().compareTo(o.getString()); - } else { - throw new NullPointerException(); - } - } - -//--------------------- End Interface Comparable --------------------- - - /** Returns associated string. */ - public String getString() - { - return string; - } - - /** Returns the associated object. */ - @SuppressWarnings("unchecked") - public T getObject() - { - return (T)object; - } -} diff --git a/src/org/infinity/util/Pair.java b/src/org/infinity/util/Pair.java deleted file mode 100644 index 33e21337d..000000000 --- a/src/org/infinity/util/Pair.java +++ /dev/null @@ -1,163 +0,0 @@ -// Near Infinity - An Infinity Engine Browser and Editor -// Copyright (C) 2001 - 2005 Jon Olav Hauglid -// See LICENSE.txt for license information - -package org.infinity.util; - -/** - * A simple Pair class to store arbitrary objects A and B. - * Both objects must share a common base class. - * @param The common base class for both objects. - */ -public class Pair implements Cloneable -{ - private V first, second; - - /** - * Creates a pair object with both elements initialized to {@code null}. - */ - public Pair() - { - this.first = null; - this.second = null; - } - - /** - * Creates a pair object, setting the first element to the specified value and - * setting the second element to {@code null}. - * @param first The value of the first element. - */ - public Pair(V first) - { - this.first = first; - this.second = null; - } - - /** - * Creates a pair object, setting both elements to the specified values. - * @param first The value of the first element. - * @param second The value of the second element. - */ - public Pair(V first, V second) - { - this.first = first; - this.second = second; - } - - /** - * Returns the value of the specified element. - * @param index Specifies the element to return (0=first, 1=second). - * @return The value specified by index. - */ - public V get(int index) - { - return ((index & 1) == 0) ? first : second; - } - - /** - * Sets the value of the element specified by index. - * @param index Specifies the element to set (0=first, 1=second). - * @param value The value to set to the specified element. - * @return The old value of the specified element. - */ - public V set(int index, V value) - { - V retVal; - if ((index & 1) == 0) { - retVal = first; - first = value; - } else { - retVal = second; - second = value; - } - return retVal; - } - - /** - * Returns the value of the first element. - * @return The value of the first element. - */ - public V getFirst() - { - return first; - } - - /** - * Sets the value of the first element. - * @param value The value for the first element. - * @return The old value of the first element. - */ - public V setFirst(V value) - { - V retVal = first; - first = value; - return retVal; - } - - /** - * Returns the value of the second element. - * @return The value of the second element. - */ - public V getSecond() - { - return second; - } - - /** - * Sets the value of the second element. - * @param value The value for the second element. - * @return The old value of the second element. - */ - public V setSecond(V value) - { - V retVal = second; - second = value; - return retVal; - } - - /** - * Swaps the content of both elements. - * After the swap the first element contains the value of the second element - * and the second element contains the value of the first element. - * @return A reference to this object. Useful for chaining actions. - */ - public Pair swap() - { - V tmp = first; - first = second; - second = tmp; - return this; - } - - /** - * Returns a shallow copy of this Pair<V> instance. (The elements themselves are not cloned.) - * @return a clone of this Pair<V> instance - */ - @Override - public Object clone() - { - return new Pair(getFirst(), getSecond()); - } - - @Override - public boolean equals(Object o) - { - if (o != null && o instanceof Pair) { - boolean retVal = true; - if (getFirst() != null) { - retVal &= getFirst().equals(((Pair)o).getFirst()); - } - if (getSecond() != null) { - retVal &= getSecond().equals(((Pair)o).getSecond()); - } - return retVal; - } - return false; - } - - @Override - public String toString() - { - return String.format("[%s, %s]", getFirst(), getSecond()); - } -} diff --git a/src/org/infinity/util/ResourceStructure.java b/src/org/infinity/util/ResourceStructure.java index a074595e2..ec92d4297 100644 --- a/src/org/infinity/util/ResourceStructure.java +++ b/src/org/infinity/util/ResourceStructure.java @@ -29,7 +29,7 @@ public class ResourceStructure implements Cloneable public ResourceStructure() { super(); - list = new Vector(); + list = new Vector<>(); cursize = 0; } @@ -329,7 +329,7 @@ private Item(int type, int size, Object value) if (value instanceof byte[]) { this.value = StreamUtils.getByteBuffer((byte[])value); } else if (value instanceof ByteBuffer) { - this.value = (ByteBuffer)value; + this.value = value; } else { this.value = StreamUtils.getByteBuffer(size); } diff --git a/src/org/infinity/util/SimpleListModel.java b/src/org/infinity/util/SimpleListModel.java index cf0ad2ec9..b46ac3f1d 100644 --- a/src/org/infinity/util/SimpleListModel.java +++ b/src/org/infinity/util/SimpleListModel.java @@ -17,7 +17,7 @@ */ public class SimpleListModel extends AbstractListModel { - private final Vector delegate = new Vector(); + private final Vector delegate = new Vector<>(); /** Constructs an empty ListModel object with a default capacity of 10 elements. */ public SimpleListModel() diff --git a/src/org/infinity/util/StringTable.java b/src/org/infinity/util/StringTable.java index fcc802785..f6539390b 100644 --- a/src/org/infinity/util/StringTable.java +++ b/src/org/infinity/util/StringTable.java @@ -869,9 +869,7 @@ public static boolean exportTra(Path outFile, ProgressCallback callback) if (callback != null) { callback.init(tableMale._getNumEntries()); } boolean retVal = true; - PrintWriter writer = null; - try { - writer = new PrintWriter(outFile.toFile(), getCharset().name()); + try (PrintWriter writer = new PrintWriter(outFile.toFile(), getCharset().name())) { String newline = System.getProperty("line.separator"); // writing header String niPath = Utils.getJarFileName(NearInfinity.class); @@ -947,7 +945,6 @@ public static boolean exportTra(Path outFile, ProgressCallback callback) e.printStackTrace(); retVal = false; } finally { - if (writer != null) { writer.close(); } if (callback != null) { callback.done(retVal); } } @@ -1452,9 +1449,7 @@ private void _exportText(Path outFile, ProgressCallback callback) throws IOExcep synchronized (entries) { if (callback != null) { callback.init(_getNumEntries()); } boolean success = false; - PrintWriter writer = null; - try { - writer = new PrintWriter(outFile.toFile(), getCharset().name()); + try (PrintWriter writer = new PrintWriter(outFile.toFile(), getCharset().name())) { String newline = System.getProperty("line.separator"); for (int idx = 0; idx < _getNumEntries(); idx++) { if (callback != null) { @@ -1472,7 +1467,6 @@ private void _exportText(Path outFile, ProgressCallback callback) throws IOExcep success = false; throw e; } finally { - writer.close(); if (callback != null) { callback.done(success); } } } @@ -1481,7 +1475,7 @@ private void _exportText(Path outFile, ProgressCallback callback) throws IOExcep //-------------------------- INNER CLASSES -------------------------- // Manages a single string entry - public static class StringEntry extends AbstractStruct implements Cloneable + public static class StringEntry extends AbstractStruct { // Default entry for non-existing indices private static final StringEntry INVALID = new StringEntry(null, FLAGS_HAS_TEXT, "", 0, 0, "No such index"); diff --git a/src/org/infinity/util/Table2da.java b/src/org/infinity/util/Table2da.java index 656ce8cb3..57e717c7a 100644 --- a/src/org/infinity/util/Table2da.java +++ b/src/org/infinity/util/Table2da.java @@ -21,8 +21,8 @@ public class Table2da /** Row index pointing to row labels. */ public static final int ROW_HEADER = 0; - private final List header = new ArrayList(); - private final List> table = new ArrayList>(); + private final List header = new ArrayList<>(); + private final List> table = new ArrayList<>(); private final ResourceEntry entry; private String defaultValue; @@ -142,7 +142,7 @@ private void init(ResourceEntry entry) String curLine = lines[idx].trim(); String[] elements = curLine.split("\\s+"); if (elements.length > 0 && !elements[0].isEmpty()) { - List listLine = new ArrayList(); + List listLine = new ArrayList<>(); for (final String s: elements) { if (!s.isEmpty()) { listLine.add(s); diff --git a/src/org/infinity/util/Table2daCache.java b/src/org/infinity/util/Table2daCache.java index 8dbc6c294..49a9023e8 100644 --- a/src/org/infinity/util/Table2daCache.java +++ b/src/org/infinity/util/Table2daCache.java @@ -11,7 +11,7 @@ public class Table2daCache { - private static final HashMap map = new HashMap(); + private static final HashMap map = new HashMap<>(); /** Removes the specified 2DA resource from the cache. */ public static synchronized void cacheInvalid(ResourceEntry entry) diff --git a/src/org/infinity/util/io/ByteBufferInputStream.java b/src/org/infinity/util/io/ByteBufferInputStream.java index 2700dce42..c78a18853 100644 --- a/src/org/infinity/util/io/ByteBufferInputStream.java +++ b/src/org/infinity/util/io/ByteBufferInputStream.java @@ -80,7 +80,7 @@ public int read(byte[] bytes, int off, int len) throws IOException if (remaining <= 0) { break; } - buf.get(bytes, read, remaining); + buf.get(bytes, off + read, remaining); read += remaining; } diff --git a/src/org/infinity/util/io/ByteBufferOutputStream.java b/src/org/infinity/util/io/ByteBufferOutputStream.java index f60896cdf..34f3a3709 100644 --- a/src/org/infinity/util/io/ByteBufferOutputStream.java +++ b/src/org/infinity/util/io/ByteBufferOutputStream.java @@ -77,7 +77,7 @@ public void write(byte[] bytes, int off, int len) throws IOException if (remaining <= 0) { throw new IndexOutOfBoundsException(); } - buf.put(bytes, written, remaining); + buf.put(bytes, off + written, remaining); written += remaining; } } diff --git a/src/org/infinity/util/io/DlcManager.java b/src/org/infinity/util/io/DlcManager.java index 36c22f7b6..3de12e136 100644 --- a/src/org/infinity/util/io/DlcManager.java +++ b/src/org/infinity/util/io/DlcManager.java @@ -76,7 +76,7 @@ private DlcManager() this.fileSystems = new HashMap<>(); } - private FileSystem _register(Path dlcFile) throws IOException + private FileSystem _register(Path dlcFile) { FileSystem fs = _getDlc(dlcFile); if (fs == null) { @@ -124,7 +124,7 @@ private Path _queryKey(Path path) return null; } - private FileSystem _validateDlc(Path dlcFile) throws IOException + private FileSystem _validateDlc(Path dlcFile) { if (dlcFile == null || !FileEx.create(dlcFile).isFile()) { return null; diff --git a/src/org/infinity/util/io/FileManager.java b/src/org/infinity/util/io/FileManager.java index 69880d2ca..8280b7dda 100644 --- a/src/org/infinity/util/io/FileManager.java +++ b/src/org/infinity/util/io/FileManager.java @@ -18,6 +18,7 @@ import java.util.Locale; import java.util.stream.Stream; +import org.infinity.resource.Profile; import org.infinity.util.io.FileWatcher.FileWatchEvent; import org.infinity.util.io.FileWatcher.FileWatchListener; @@ -562,8 +563,17 @@ private static Path _resolveExisting(Path path) if (list == null) { list = _cacheDirectory(folder, false); } - if (list == null || !list.contains(retVal)) { + if (list == null) { retVal = null; + } else { + final String pathString = path.getFileName().toString(); + final boolean isCase = isFileSystemCaseSensitive(path.getFileSystem()); + boolean match = list + .parallelStream() + .anyMatch(p -> isCase ? pathString.equals(p.getFileName().toString()) : pathString.equalsIgnoreCase(p.getFileName().toString())); + if (!match) { + retVal = null; + } } } // if (retVal != null && !FileEx.fromPath(retVal).exists()) { @@ -600,20 +610,26 @@ private static boolean isFileSystemCaseSensitive(FileSystem fs) if (fs != null) { retVal = getInstance().mapCaseSensitive.get(fs); if (retVal == null) { - final char[] separators = { '/', '\\', ':' }; - final String name = "/tmp/aaaBBB"; - for (final char sep: separators) { - String s = (sep != '/') ? name.replace('/', sep) : name; - try { - Path path = fs.getPath(s); - Path path2 = path.getParent().resolve(path.getFileName().toString().toUpperCase(Locale.ENGLISH)); - Path path3 = path.getParent().resolve(path.getFileName().toString().toLowerCase(Locale.ENGLISH)); - retVal = Boolean.valueOf(!(path.equals(path2) && path.equals(path3))); - getInstance().mapCaseSensitive.put(fs, retVal); - break; - } catch (Throwable t) { - retVal = Boolean.TRUE; + if (Profile.getProperty(Profile.Key.GET_GLOBAL_FILE_CASE_CHECK)) { + final char[] separators = { '/', '\\', ':' }; + final String name = "/tmp/aaaBBB"; + for (final char sep: separators) { + String s = (sep != '/') ? name.replace('/', sep) : name; + try { + Path path = fs.getPath(s); + Path path2 = path.getParent().resolve(path.getFileName().toString().toUpperCase(Locale.ENGLISH)); + Path path3 = path.getParent().resolve(path.getFileName().toString().toLowerCase(Locale.ENGLISH)); + retVal = Boolean.valueOf(!(path.equals(path2) && path.equals(path3))); + getInstance().mapCaseSensitive.put(fs, retVal); + break; + } catch (Throwable t) { + retVal = Boolean.TRUE; + } } + } else { + // forced + retVal = Boolean.valueOf(false); + getInstance().mapCaseSensitive.put(fs, retVal); } } } diff --git a/src/org/infinity/util/io/zip/DlcFileAttributes.java b/src/org/infinity/util/io/zip/DlcFileAttributes.java index af73305c3..aa7025beb 100644 --- a/src/org/infinity/util/io/zip/DlcFileAttributes.java +++ b/src/org/infinity/util/io/zip/DlcFileAttributes.java @@ -110,32 +110,33 @@ public byte[] comment() return null; } + @Override public String toString() { StringBuilder sb = new StringBuilder(1024); - Formatter fm = new Formatter(sb); - if (creationTime() != null) { - fm.format(" creationTime : %tc%n", creationTime().toMillis()); - } else { - fm.format(" creationTime : null%n"); - } - - if (lastAccessTime() != null) { - fm.format(" lastAccessTime : %tc%n", lastAccessTime().toMillis()); - } else { - fm.format(" lastAccessTime : null%n"); + try (Formatter fm = new Formatter(sb)) { + if (creationTime() != null) { + fm.format(" creationTime : %tc%n", creationTime().toMillis()); + } else { + fm.format(" creationTime : null%n"); + } + + if (lastAccessTime() != null) { + fm.format(" lastAccessTime : %tc%n", lastAccessTime().toMillis()); + } else { + fm.format(" lastAccessTime : null%n"); + } + fm.format(" lastModifiedTime: %tc%n", lastModifiedTime().toMillis()); + fm.format(" isRegularFile : %b%n", isRegularFile()); + fm.format(" isDirectory : %b%n", isDirectory()); + fm.format(" isSymbolicLink : %b%n", isSymbolicLink()); + fm.format(" isOther : %b%n", isOther()); + fm.format(" fileKey : %s%n", fileKey()); + fm.format(" size : %d%n", size()); + fm.format(" compressedSize : %d%n", compressedSize()); + fm.format(" crc : %x%n", crc()); + fm.format(" method : %d%n", method()); } - fm.format(" lastModifiedTime: %tc%n", lastModifiedTime().toMillis()); - fm.format(" isRegularFile : %b%n", isRegularFile()); - fm.format(" isDirectory : %b%n", isDirectory()); - fm.format(" isSymbolicLink : %b%n", isSymbolicLink()); - fm.format(" isOther : %b%n", isOther()); - fm.format(" fileKey : %s%n", fileKey()); - fm.format(" size : %d%n", size()); - fm.format(" compressedSize : %d%n", compressedSize()); - fm.format(" crc : %x%n", crc()); - fm.format(" method : %d%n", method()); - fm.close(); return sb.toString(); } } diff --git a/src/org/infinity/util/io/zip/DlcFileStore.java b/src/org/infinity/util/io/zip/DlcFileStore.java index aa230e320..4ebacba14 100644 --- a/src/org/infinity/util/io/zip/DlcFileStore.java +++ b/src/org/infinity/util/io/zip/DlcFileStore.java @@ -113,7 +113,7 @@ public V getFileStoreAttributeView(Class t if (type == null) { throw new NullPointerException(); } - return (V) null; + return null; } @Override diff --git a/src/org/infinity/util/io/zip/DlcFileSystem.java b/src/org/infinity/util/io/zip/DlcFileSystem.java index 01e839336..40520bfcc 100644 --- a/src/org/infinity/util/io/zip/DlcFileSystem.java +++ b/src/org/infinity/util/io/zip/DlcFileSystem.java @@ -11,11 +11,11 @@ import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; +import java.nio.channels.FileChannel.MapMode; import java.nio.channels.FileLock; import java.nio.channels.ReadableByteChannel; import java.nio.channels.SeekableByteChannel; import java.nio.channels.WritableByteChannel; -import java.nio.channels.FileChannel.MapMode; import java.nio.file.AccessMode; import java.nio.file.ClosedFileSystemException; import java.nio.file.DirectoryStream; @@ -61,9 +61,8 @@ */ public class DlcFileSystem extends FileSystem { - private static final Set supportedFileAttributeViews = - Collections.unmodifiableSet(new HashSet( - Arrays.asList(DlcFileAttributeView.VIEW_BASIC, DlcFileAttributeView.VIEW_ZIP))); + private static final Set supportedFileAttributeViews = Collections.unmodifiableSet( + new HashSet<>(Arrays.asList(DlcFileAttributeView.VIEW_BASIC, DlcFileAttributeView.VIEW_ZIP))); private static final String GLOB_SYNTAX = "glob"; private static final String REGEX_SYNTAX = "regex"; @@ -265,7 +264,7 @@ FileStore getFileStore(DlcPath path) return new DlcFileStore(path); } - DlcFileAttributes getFileAttributes(byte[] path) throws IOException + DlcFileAttributes getFileAttributes(byte[] path) { ZipNode folder = null; beginRead(); @@ -282,7 +281,7 @@ DlcFileAttributes getFileAttributes(byte[] path) throws IOException } } - boolean exists(byte[] path) throws IOException + boolean exists(byte[] path) { beginRead(); try { @@ -293,7 +292,7 @@ boolean exists(byte[] path) throws IOException } } - boolean isDirectory(byte[] path) throws IOException + boolean isDirectory(byte[] path) { beginRead(); try { @@ -688,7 +687,7 @@ private void checkOpen() throws IOException } } - private void checkWritable() throws IOException + private void checkWritable() { throw new ReadOnlyFileSystemException(); } @@ -717,7 +716,7 @@ private final void endRead() rwlock.readLock().unlock(); } - private void ensureOpen() throws IOException + private void ensureOpen() { if (!isOpen) { throw new ClosedFileSystemException(); diff --git a/src/org/infinity/util/io/zip/DlcPath.java b/src/org/infinity/util/io/zip/DlcPath.java index 8f783319f..4297dc168 100644 --- a/src/org/infinity/util/io/zip/DlcPath.java +++ b/src/org/infinity/util/io/zip/DlcPath.java @@ -741,7 +741,7 @@ private byte[] resolve0() ///////////////////////////////////////////////////////////////////// - void createDirectory(FileAttribute... attrs) throws IOException + void createDirectory(FileAttribute... attrs) { throw new UnsupportedOperationException(); } @@ -763,12 +763,12 @@ DirectoryStream newDirectoryStream(Filter filter) throws IOE return new DlcDirectoryStream(this, filter); } - void delete() throws IOException + void delete() { throw new UnsupportedOperationException(); } - void deleteIfExists() throws IOException + void deleteIfExists() { throw new UnsupportedOperationException(); } @@ -782,12 +782,12 @@ DlcFileAttributes getAttributes() throws IOException return zfas; } - void setAttribute(String attribute, Object value, LinkOption... options) throws IOException + void setAttribute(String attribute, Object value, LinkOption... options) { throw new UnsupportedOperationException(); } - void setTimes(FileTime mtime, FileTime atime, FileTime ctime) throws IOException + void setTimes(FileTime mtime, FileTime atime, FileTime ctime) { throw new UnsupportedOperationException(); } @@ -882,24 +882,20 @@ boolean exists() if (path.length == 1 && path[0] == '/') { return true; } - try { - return dfs.exists(getResolvedPath()); - } catch (IOException x) { - } - return false; + return dfs.exists(getResolvedPath()); } - OutputStream newOutputStream(OpenOption... options) throws IOException + OutputStream newOutputStream(OpenOption... options) { throw new UnsupportedOperationException(); } - void move(DlcPath target, CopyOption... options) throws IOException + void move(DlcPath target, CopyOption... options) { throw new UnsupportedOperationException(); } - void copy(DlcPath target, CopyOption... options) throws IOException + void copy(DlcPath target, CopyOption... options) { throw new UnsupportedOperationException(); } diff --git a/src/org/infinity/util/io/zip/ZipBaseHeader.java b/src/org/infinity/util/io/zip/ZipBaseHeader.java index f423439a7..a98d123fd 100644 --- a/src/org/infinity/util/io/zip/ZipBaseHeader.java +++ b/src/org/infinity/util/io/zip/ZipBaseHeader.java @@ -34,6 +34,16 @@ protected ZipBaseHeader(long offset, long signature) this.signature = signature; } + @Override + public int hashCode() + { + int hash = 7; + hash = 31 * hash + Long.hashCode(offset); + hash = 31 * hash + Long.hashCode(size); + hash = 31 * hash + Long.hashCode(signature); + return hash; + } + @Override public boolean equals(Object o) { diff --git a/src/org/infinity/util/io/zip/ZipCentralEndHeader.java b/src/org/infinity/util/io/zip/ZipCentralEndHeader.java index ca67adb51..f46431735 100644 --- a/src/org/infinity/util/io/zip/ZipCentralEndHeader.java +++ b/src/org/infinity/util/io/zip/ZipCentralEndHeader.java @@ -4,7 +4,31 @@ package org.infinity.util.io.zip; -import static org.infinity.util.io.zip.ZipConstants.*; +import static org.infinity.util.io.zip.ZipConstants.CENCOM; +import static org.infinity.util.io.zip.ZipConstants.CENEXT; +import static org.infinity.util.io.zip.ZipConstants.CENHDR; +import static org.infinity.util.io.zip.ZipConstants.CENNAM; +import static org.infinity.util.io.zip.ZipConstants.CENSIG; +import static org.infinity.util.io.zip.ZipConstants.ENDCOM; +import static org.infinity.util.io.zip.ZipConstants.ENDHDR; +import static org.infinity.util.io.zip.ZipConstants.ENDSIG; +import static org.infinity.util.io.zip.ZipConstants.END_MAXLEN; +import static org.infinity.util.io.zip.ZipConstants.LL; +import static org.infinity.util.io.zip.ZipConstants.LOCHDR; +import static org.infinity.util.io.zip.ZipConstants.LOCLEN; +import static org.infinity.util.io.zip.ZipConstants.LOCSIG; +import static org.infinity.util.io.zip.ZipConstants.LOCSIZ; +import static org.infinity.util.io.zip.ZipConstants.READBLOCKSZ; +import static org.infinity.util.io.zip.ZipConstants.ZIP64_ENDHDR; +import static org.infinity.util.io.zip.ZipConstants.ZIP64_ENDOFF; +import static org.infinity.util.io.zip.ZipConstants.ZIP64_ENDSIG; +import static org.infinity.util.io.zip.ZipConstants.ZIP64_ENDSIZ; +import static org.infinity.util.io.zip.ZipConstants.ZIP64_ENDTOT; +import static org.infinity.util.io.zip.ZipConstants.ZIP64_LOCHDR; +import static org.infinity.util.io.zip.ZipConstants.ZIP64_LOCOFF; +import static org.infinity.util.io.zip.ZipConstants.ZIP64_LOCSIG; +import static org.infinity.util.io.zip.ZipConstants.ZIP64_MINVAL; +import static org.infinity.util.io.zip.ZipConstants.ZIP64_MINVAL32; import java.io.IOException; import java.nio.ByteBuffer; @@ -40,7 +64,7 @@ public class ZipCentralEndHeader extends ZipBaseHeader public byte[] comment; - public ZipCentralEndHeader(ByteBuffer buffer, long absOffset) throws IOException + public ZipCentralEndHeader(ByteBuffer buffer, long absOffset) { super(absOffset, buffer.getInt()); long headerStart = buffer.position() - 4L; diff --git a/src/org/infinity/util/io/zip/ZipCentralHeader.java b/src/org/infinity/util/io/zip/ZipCentralHeader.java index 7df3aa3d2..0f49b6784 100644 --- a/src/org/infinity/util/io/zip/ZipCentralHeader.java +++ b/src/org/infinity/util/io/zip/ZipCentralHeader.java @@ -4,7 +4,10 @@ package org.infinity.util.io.zip; -import static org.infinity.util.io.zip.ZipConstants.*; +import static org.infinity.util.io.zip.ZipConstants.CENSIG; +import static org.infinity.util.io.zip.ZipConstants.LOCEXT; +import static org.infinity.util.io.zip.ZipConstants.LOCHDR; +import static org.infinity.util.io.zip.ZipConstants.LOCNAM; import java.io.IOException; import java.nio.ByteBuffer; @@ -39,7 +42,7 @@ public class ZipCentralHeader extends ZipLocalHeader // Cached local header private ZipLocalHeader localHeader; - public ZipCentralHeader(ByteBuffer buffer, long absOffset) throws IOException + public ZipCentralHeader(ByteBuffer buffer, long absOffset) { super(absOffset, buffer.getInt() & 0xffffffffL); long headerStart = buffer.position() - 4L; @@ -98,6 +101,20 @@ public long getDataOffset(SeekableByteChannel ch) throws IOException } } + @Override + public int hashCode() + { + int hash = super.hashCode(); + hash = 31 * hash + versionCreated; + hash = 31 * hash + idxDisk; + hash = 31 * hash + attribInternal; + hash = 31 * hash + Long.hashCode(attribExternal); + hash = 31 * hash + Long.hashCode(ofsLocalHeader); + hash = 31 * hash + ((comment == null) ? 0 : comment.hashCode()); + hash = 31 * hash + ((localHeader == null) ? 0 : localHeader.hashCode()); + return hash; + } + @Override public boolean equals(Object o) { @@ -123,7 +140,7 @@ public int compareTo(ZipBaseHeader o) } else { return 0; } - } else if (o instanceof ZipBaseHeader) { + } else if (o != null) { return super.compareTo(o); } else { throw new NullPointerException(); diff --git a/src/org/infinity/util/io/zip/ZipLocalHeader.java b/src/org/infinity/util/io/zip/ZipLocalHeader.java index 726a79d84..3602586b6 100644 --- a/src/org/infinity/util/io/zip/ZipLocalHeader.java +++ b/src/org/infinity/util/io/zip/ZipLocalHeader.java @@ -4,9 +4,8 @@ package org.infinity.util.io.zip; -import static org.infinity.util.io.zip.ZipConstants.*; +import static org.infinity.util.io.zip.ZipConstants.LOCSIG; -import java.io.IOException; import java.nio.ByteBuffer; /** @@ -47,12 +46,12 @@ public class ZipLocalHeader extends ZipBaseHeader /** Optional extra data. (Is never {@code null}) */ public byte[] extra; - protected ZipLocalHeader(long offset, long signature) throws IOException + protected ZipLocalHeader(long offset, long signature) { super(offset, signature); } - public ZipLocalHeader(ByteBuffer buffer, long absOffset) throws IOException + public ZipLocalHeader(ByteBuffer buffer, long absOffset) { super(absOffset, buffer.getInt()); long headerStart = buffer.position() - 4L; diff --git a/src/org/infinity/util/io/zip/ZipNode.java b/src/org/infinity/util/io/zip/ZipNode.java index 28695888f..5b2287287 100644 --- a/src/org/infinity/util/io/zip/ZipNode.java +++ b/src/org/infinity/util/io/zip/ZipNode.java @@ -205,6 +205,17 @@ public String toString() return new String(getName()); } + @Override + public int hashCode() + { + int hash = 7; + hash = 31 * hash + ((children == null) ? 0 : children.hashCode()); + hash = 31 * hash + ((header == null) ? 0 : header.hashCode()); + hash = 31 * hash + ((endHeader == null) ? 0 : endHeader.hashCode()); + hash = 31 * hash + ((name == null) ? 0 : name.hashCode()); + return hash; + } + @Override public boolean equals(Object o) { diff --git a/src/org/infinity/util/tuples/Couple.java b/src/org/infinity/util/tuples/Couple.java new file mode 100644 index 000000000..2432b2281 --- /dev/null +++ b/src/org/infinity/util/tuples/Couple.java @@ -0,0 +1,153 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.util.tuples; + +import java.util.Collection; +import java.util.Iterator; + +/** + * A tuple class that can store two elements. + */ +public class Couple extends Tuple implements TupleValue0, TupleValue1 +{ + private static final int SIZE = 2; + + private A value0; + private B value1; + + /** + * Creates a new tuple instance with the specified elements. + * @param the tuple element type. + * @param value0 The first element to store in the tuple. + * @param value1 The second element to store in the tuple. + * @return A new tuple instance. + */ + public static Couple with(A value0, B value1) + { + return new Couple<>(value0, value1); + } + + /** + * Creates a new tuple from the array. The array must contain at least 2 elements. + * @param the tuple element type. + * @param arr The array to be used as source for the tuple. + * @return A new tuple instance. + */ + public static Couple fromArray(T[] arr) + { + if (arr == null) { + throw new IllegalArgumentException("Array cannot be null"); + } + if (arr.length < SIZE) { + throw new IllegalArgumentException(String.format("Array must contain at least %d elements", SIZE)); + } + return new Couple<>(arr[0], arr[1]); + } + + /** + * Creates a new tuple from the collection. The collection must contain at least 2 elements. + * @param the tuple element type. + * @param col the collection to be used as source for the tuple. + * @return a new tuple instance. + */ + public static Couple fromCollection(Collection col) + { + if (col == null) { + throw new IllegalArgumentException("Collection cannot be null"); + } + if (col.size() < SIZE) { + throw new IllegalArgumentException(String.format("Collection must contain at least %d elements", SIZE)); + } + Iterator iter = col.iterator(); + T el0 = iter.next(); + T el1 = iter.next(); + return new Couple<>(el0, el1); + } + + /** + * Creates a new tuple from the {@code Iterable} object. + * @param the tuple element type. + * @param iterator the {@code Iterable} object to be used as source for the tuple. + * @return a new tuple instance. + */ + public static Couple fromIterable(Iterable iterator) + { + return fromIterable(iterator, 0); + } + + /** + * Creates a new tuple from the {@code Iterable} object, starting the specified index. + * @param the tuple element type. + * @param iterator the {@code Iterable} object to be used as source for the tuple. + * @param index start index in {@code Iterable} object. + * @return A new tuple instance. + */ + public static Couple fromIterable(Iterable iterator, int index) + { + if (iterator == null) { + throw new IllegalArgumentException("Iterator cannot be null"); + } + + Iterator iter = iterator.iterator(); + for (int i = 0; i < index; i++) { + if (iter.hasNext()) { + iter.next(); + } + i++; + } + + T el0 = iter.hasNext() ? iter.next() : null; + T el1 = iter.hasNext() ? iter.next() : null; + return new Couple<>(el0, el1); + } + + /** + * Constructs a new Couple instance and initializes it with the specified arguments. + * @param value0 the first value of the Couple. + * @param value1 the second value of the Couple. + */ + public Couple(A value0, B value1) + { + super(value0, value1); + this.value0 = value0; + this.value1 = value1; + } + + @Override + public int size() + { + return SIZE; + } + + @Override + public A getValue0() + { + return value0; + } + + @Override + public A setValue0(A newValue) + { + A retVal = value0; + setValue(0, newValue); + value0 = newValue; + return retVal; + } + + @Override + public B getValue1() + { + return value1; + } + + @Override + public B setValue1(B newValue) + { + B retVal = value1; + setValue(1, newValue); + value1 = newValue; + return retVal; + } +} diff --git a/src/org/infinity/util/tuples/Monuple.java b/src/org/infinity/util/tuples/Monuple.java new file mode 100644 index 000000000..a1da228aa --- /dev/null +++ b/src/org/infinity/util/tuples/Monuple.java @@ -0,0 +1,136 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.util.tuples; + +import java.util.Collection; +import java.util.Iterator; + +/** + * A tuple class that can store one element. + */ +public class Monuple extends Tuple implements TupleValue0 +{ + private static final int SIZE = 1; + + private A value0; + + /** + * Creates a new tuple instance with the specified element. + * @param the tuple element type. + * @param value0 The element to store in the tuple. + * @return A new tuple instance. + */ + public static Monuple with(A value0) + { + return new Monuple<>(value0); + } + + /** + * Creates a new tuple from the array. The array must contain at least 1 element. + * @param the tuple element type. + * @param arr the array to be used as source for the tuple. + * @return a new tuple instance. + */ + public static Monuple fromArray(T[] arr) + { + if (arr == null) { + throw new IllegalArgumentException("Array cannot be null"); + } + if (arr.length < SIZE) { + throw new IllegalArgumentException("Array must contain at least 1 element"); + } + return new Monuple<>(arr[0]); + } + + /** + * Creates a new tuple from the collection. The collection must contain at least 1 element. + * @param the tuple element type. + * @param col the collection to be used as source for the tuple. + * @return a new tuple instance. + */ + public static Monuple fromCollection(Collection col) + { + if (col == null) { + throw new IllegalArgumentException("Collection cannot be null"); + } + if (col.size() < SIZE) { + throw new IllegalArgumentException("Collection must contain at least 1 element"); + } + Iterator iter = col.iterator(); + return new Monuple<>(iter.next()); + } + + /** + * Creates a new tuple from the {@code Iterable} object. + * @param the tuple element type. + * @param iterator the {@code Iterable} object to be used as source for the tuple. + * @return a new tuple instance. + */ + public static Monuple fromIterable(Iterable iterator) + { + return fromIterable(iterator, 0); + } + + /** + * Creates a new tuple from the {@code Iterable} object, starting the specified index. + * @param the tuple element type. + * @param iterator the {@code Iterable} object to be used as source for the tuple. + * @param index start index in {@code Iterable} object. + * @return A new tuple instance. + */ + public static Monuple fromIterable(Iterable iterator, int index) + { + if (iterator == null) { + throw new IllegalArgumentException("Iterator cannot be null"); + } + + Iterator iter = iterator.iterator(); + for (int i = 0; i < index; i++) { + if (iter.hasNext()) { + iter.next(); + } + i++; + } + + T el0; + if (iter.hasNext()) { + el0 = iter.next(); + } else { + el0 = null; + } + return new Monuple<>(el0); + } + + /** + * Constructs a new Monuple instance and initializes it with the specified arguments. + * @param value0 the value of the Monuple. + */ + public Monuple(A value0) + { + super(value0); + this.value0 = value0; + } + + @Override + public int size() + { + return SIZE; + } + + @Override + public A getValue0() + { + return value0; + } + + @Override + public A setValue0(A newValue) + { + A retVal = value0; + setValue(0, newValue); + value0 = newValue; + return retVal; + } +} diff --git a/src/org/infinity/util/tuples/Quadruple.java b/src/org/infinity/util/tuples/Quadruple.java new file mode 100644 index 000000000..6cd0f825a --- /dev/null +++ b/src/org/infinity/util/tuples/Quadruple.java @@ -0,0 +1,196 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.util.tuples; + +import java.util.Collection; +import java.util.Iterator; + +/** + * A tuple class that can store four elements. + */ +public class Quadruple extends Tuple + implements TupleValue0, TupleValue1, TupleValue2, TupleValue3 +{ + private static final int SIZE = 4; + + private A value0; + private B value1; + private C value2; + private D value3; + + /** + * Creates a new tuple instance with the specified elements. + * @param the tuple element type. + * @param value0 The first element to store in the tuple. + * @param value2 The second element to store in the tuple. + * @param value3 The third element to store in the tuple. + * @param value4 The fourth element to store in the tuple. + * @return A new tuple instance. + */ + public static Quadruple with(A value0, B value1, C value2, D value3) + { + return new Quadruple<>(value0, value1, value2, value3); + } + + /** + * Creates a new tuple from the array. The array must contain at least 4 elements. + * @param the tuple element type. + * @param arr The array to be used as source for the tuple. + * @return A new tuple instance. + */ + public static Quadruple fromArray(T[] arr) + { + if (arr == null) { + throw new IllegalArgumentException("Array cannot be null"); + } + if (arr.length < SIZE) { + throw new IllegalArgumentException(String.format("Array must contain at least %d elements", SIZE)); + } + return new Quadruple<>(arr[0], arr[1], arr[2], arr[3]); + } + + /** + * Creates a new tuple from the collection. The collection must contain at least 4 elements. + * @param the tuple element type. + * @param col the collection to be used as source for the tuple. + * @return a new tuple instance. + */ + public static Quadruple fromCollection(Collection col) + { + if (col == null) { + throw new IllegalArgumentException("Collection cannot be null"); + } + if (col.size() < SIZE) { + throw new IllegalArgumentException(String.format("Collection must contain at least %d elements", SIZE)); + } + Iterator iter = col.iterator(); + T el0 = iter.next(); + T el1 = iter.next(); + T el2 = iter.next(); + T el3 = iter.next(); + return new Quadruple<>(el0, el1, el2, el3); + } + + /** + * Creates a new tuple from the {@code Iterable} object. + * @param the tuple element type. + * @param iterator the {@code Iterable} object to be used as source for the tuple. + * @return a new tuple instance. + */ + public static Quadruple fromIterable(Iterable iterator) + { + return fromIterable(iterator, 0); + } + + /** + * Creates a new tuple from the {@code Iterable} object, starting the specified index. + * @param the tuple element type. + * @param iterator the {@code Iterable} object to be used as source for the tuple. + * @param index start index in {@code Iterable} object. + * @return A new tuple instance. + */ + public static Quadruple fromIterable(Iterable iterator, int index) + { + if (iterator == null) { + throw new IllegalArgumentException("Iterator cannot be null"); + } + + Iterator iter = iterator.iterator(); + for (int i = 0; i < index; i++) { + if (iter.hasNext()) { + iter.next(); + } + i++; + } + + T el0 = iter.hasNext() ? iter.next() : null; + T el1 = iter.hasNext() ? iter.next() : null; + T el2 = iter.hasNext() ? iter.next() : null; + T el3 = iter.hasNext() ? iter.next() : null; + return new Quadruple<>(el0, el1, el2, el3); + } + + /** + * Constructs a new Triple instance and initializes it with the specified arguments. + * @param value0 the first value of the Quadruple. + * @param value1 the second value of the Quadruple. + * @param value2 the third value of the Quadruple. + * @param value3 the fourth value of the Quadruple. + */ + public Quadruple(A value0, B value1, C value2, D value3) + { + super(value0, value1, value2); + this.value0 = value0; + this.value1 = value1; + this.value2 = value2; + this.value3 = value3; + } + + @Override + public int size() + { + return SIZE; + } + + @Override + public A getValue0() + { + return value0; + } + + @Override + public A setValue0(A newValue) + { + A retVal = value0; + setValue(0, newValue); + value0 = newValue; + return retVal; + } + + @Override + public B getValue1() + { + return value1; + } + + @Override + public B setValue1(B newValue) + { + B retVal = value1; + setValue(1, newValue); + value1 = newValue; + return retVal; + } + + @Override + public C getValue2() + { + return value2; + } + + @Override + public C setValue2(C newValue) + { + C retVal = value2; + setValue(2, newValue); + value2 = newValue; + return retVal; + } + + @Override + public D getValue3() + { + return value3; + } + + @Override + public D setValue3(D newValue) + { + D retVal = value3; + setValue(3, newValue); + value3 = newValue; + return retVal; + } +} diff --git a/src/org/infinity/util/tuples/Quintuple.java b/src/org/infinity/util/tuples/Quintuple.java new file mode 100644 index 000000000..b3b862303 --- /dev/null +++ b/src/org/infinity/util/tuples/Quintuple.java @@ -0,0 +1,216 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.util.tuples; + +import java.util.Collection; +import java.util.Iterator; + +/** + * A tuple class that can store five elements. + */ +public class Quintuple extends Tuple + implements TupleValue0, TupleValue1, TupleValue2, TupleValue3, TupleValue4 +{ + private static final int SIZE = 5; + + private A value0; + private B value1; + private C value2; + private D value3; + private E value4; + + /** + * Creates a new tuple instance with the specified elements. + * @param the tuple element type. + * @param value0 The first element to store in the tuple. + * @param value2 The second element to store in the tuple. + * @param value3 The third element to store in the tuple. + * @param value4 The fourth element to store in the tuple. + * @return A new tuple instance. + */ + public static Quintuple with(A value0, B value1, C value2, D value3, E value4) + { + return new Quintuple<>(value0, value1, value2, value3, value4); + } + + /** + * Creates a new tuple from the array. The array must contain at least 5 elements. + * @param the tuple element type. + * @param arr The array to be used as source for the tuple. + * @return A new tuple instance. + */ + public static Quintuple fromArray(T[] arr) + { + if (arr == null) { + throw new IllegalArgumentException("Array cannot be null"); + } + if (arr.length < SIZE) { + throw new IllegalArgumentException(String.format("Array must contain at least %d elements", SIZE)); + } + return new Quintuple<>(arr[0], arr[1], arr[2], arr[3], arr[4]); + } + + /** + * Creates a new tuple from the collection. The collection must contain at least 5 elements. + * @param the tuple element type. + * @param col the collection to be used as source for the tuple. + * @return a new tuple instance. + */ + public static Quintuple fromCollection(Collection col) + { + if (col == null) { + throw new IllegalArgumentException("Collection cannot be null"); + } + if (col.size() < SIZE) { + throw new IllegalArgumentException(String.format("Collection must contain at least %d elements", SIZE)); + } + Iterator iter = col.iterator(); + T el0 = iter.next(); + T el1 = iter.next(); + T el2 = iter.next(); + T el3 = iter.next(); + T el4 = iter.next(); + return new Quintuple<>(el0, el1, el2, el3, el4); + } + + /** + * Creates a new tuple from the {@code Iterable} object. + * @param the tuple element type. + * @param iterator the {@code Iterable} object to be used as source for the tuple. + * @return a new tuple instance. + */ + public static Quintuple fromIterable(Iterable iterator) + { + return fromIterable(iterator, 0); + } + + /** + * Creates a new tuple from the {@code Iterable} object, starting the specified index. + * @param the tuple element type. + * @param iterator the {@code Iterable} object to be used as source for the tuple. + * @param index start index in {@code Iterable} object. + * @return A new tuple instance. + */ + public static Quintuple fromIterable(Iterable iterator, int index) + { + if (iterator == null) { + throw new IllegalArgumentException("Iterator cannot be null"); + } + + Iterator iter = iterator.iterator(); + for (int i = 0; i < index; i++) { + if (iter.hasNext()) { + iter.next(); + } + i++; + } + + T el0 = iter.hasNext() ? iter.next() : null; + T el1 = iter.hasNext() ? iter.next() : null; + T el2 = iter.hasNext() ? iter.next() : null; + T el3 = iter.hasNext() ? iter.next() : null; + T el4 = iter.hasNext() ? iter.next() : null; + return new Quintuple<>(el0, el1, el2, el3, el4); + } + + /** + * Constructs a new Triple instance and initializes it with the specified arguments. + * @param value0 the first value of the Quintuple. + * @param value1 the second value of the Quintuple. + * @param value2 the third value of the Quintuple. + * @param value3 the fourth value of the Quintuple. + * @param value4 the fifth value of the Quintuple. + */ + public Quintuple(A value0, B value1, C value2, D value3, E value4) + { + super(value0, value1, value2); + this.value0 = value0; + this.value1 = value1; + this.value2 = value2; + this.value3 = value3; + this.value4 = value4; + } + + @Override + public int size() + { + return SIZE; + } + + @Override + public A getValue0() + { + return value0; + } + + @Override + public A setValue0(A newValue) + { + A retVal = value0; + setValue(0, newValue); + value0 = newValue; + return retVal; + } + + @Override + public B getValue1() + { + return value1; + } + + @Override + public B setValue1(B newValue) + { + B retVal = value1; + setValue(1, newValue); + value1 = newValue; + return retVal; + } + + @Override + public C getValue2() + { + return value2; + } + + @Override + public C setValue2(C newValue) + { + C retVal = value2; + setValue(2, newValue); + value2 = newValue; + return retVal; + } + + @Override + public D getValue3() + { + return value3; + } + + @Override + public D setValue3(D newValue) + { + D retVal = value3; + setValue(3, newValue); + value3 = newValue; + return retVal; + } + + @Override + public E getValue4() + { + return value4; + } + + @Override + public E setValue4(E newValue) + { + E retVal = value4; + setValue(4, newValue); + value4 = newValue; + return retVal; + } +} diff --git a/src/org/infinity/util/tuples/Sextuple.java b/src/org/infinity/util/tuples/Sextuple.java new file mode 100644 index 000000000..43a0c5ac3 --- /dev/null +++ b/src/org/infinity/util/tuples/Sextuple.java @@ -0,0 +1,236 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.util.tuples; + +import java.util.Collection; +import java.util.Iterator; + +/** + * A tuple class that can store six elements. + */ +public class Sextuple extends Tuple + implements TupleValue0, TupleValue1, TupleValue2, TupleValue3, TupleValue4, TupleValue5 +{ + private static final int SIZE = 6; + + private A value0; + private B value1; + private C value2; + private D value3; + private E value4; + private F value5; + + /** + * Creates a new tuple instance with the specified elements. + * @param the tuple element type. + * @param value0 The first element to store in the tuple. + * @param value2 The second element to store in the tuple. + * @param value3 The third element to store in the tuple. + * @param value4 The fourth element to store in the tuple. + * @return A new tuple instance. + */ + public static Sextuple with(A value0, B value1, C value2, D value3, E value4, F value5) + { + return new Sextuple<>(value0, value1, value2, value3, value4, value5); + } + + /** + * Creates a new tuple from the array. The array must contain at least 6 elements. + * @param the tuple element type. + * @param arr The array to be used as source for the tuple. + * @return A new tuple instance. + */ + public static Sextuple fromArray(T[] arr) + { + if (arr == null) { + throw new IllegalArgumentException("Array cannot be null"); + } + if (arr.length < SIZE) { + throw new IllegalArgumentException(String.format("Array must contain at least %d elements", SIZE)); + } + return new Sextuple<>(arr[0], arr[1], arr[2], arr[3], arr[4], arr[5]); + } + + /** + * Creates a new tuple from the collection. The collection must contain at least 6 elements. + * @param the tuple element type. + * @param col the collection to be used as source for the tuple. + * @return a new tuple instance. + */ + public static Sextuple fromCollection(Collection col) + { + if (col == null) { + throw new IllegalArgumentException("Collection cannot be null"); + } + if (col.size() < SIZE) { + throw new IllegalArgumentException(String.format("Collection must contain at least %d elements", SIZE)); + } + Iterator iter = col.iterator(); + T el0 = iter.next(); + T el1 = iter.next(); + T el2 = iter.next(); + T el3 = iter.next(); + T el4 = iter.next(); + T el5 = iter.next(); + return new Sextuple<>(el0, el1, el2, el3, el4, el5); + } + + /** + * Creates a new tuple from the {@code Iterable} object. + * @param the tuple element type. + * @param iterator the {@code Iterable} object to be used as source for the tuple. + * @return a new tuple instance. + */ + public static Sextuple fromIterable(Iterable iterator) + { + return fromIterable(iterator, 0); + } + + /** + * Creates a new tuple from the {@code Iterable} object, starting the specified index. + * @param the tuple element type. + * @param iterator the {@code Iterable} object to be used as source for the tuple. + * @param index start index in {@code Iterable} object. + * @return A new tuple instance. + */ + public static Sextuple fromIterable(Iterable iterator, int index) + { + if (iterator == null) { + throw new IllegalArgumentException("Iterator cannot be null"); + } + + Iterator iter = iterator.iterator(); + for (int i = 0; i < index; i++) { + if (iter.hasNext()) { + iter.next(); + } + i++; + } + + T el0 = iter.hasNext() ? iter.next() : null; + T el1 = iter.hasNext() ? iter.next() : null; + T el2 = iter.hasNext() ? iter.next() : null; + T el3 = iter.hasNext() ? iter.next() : null; + T el4 = iter.hasNext() ? iter.next() : null; + T el5 = iter.hasNext() ? iter.next() : null; + return new Sextuple<>(el0, el1, el2, el3, el4, el5); + } + + /** + * Constructs a new Triple instance and initializes it with the specified arguments. + * @param value0 the first value of the Sextuple. + * @param value1 the second value of the Sextuple. + * @param value2 the third value of the Sextuple. + * @param value3 the fourth value of the Sextuple. + * @param value4 the fifth value of the Sextuple. + * @param value5 the sixth value of the Sextuple. + */ + public Sextuple(A value0, B value1, C value2, D value3, E value4, F value5) + { + super(value0, value1, value2); + this.value0 = value0; + this.value1 = value1; + this.value2 = value2; + this.value3 = value3; + this.value4 = value4; + this.value5 = value5; + } + + @Override + public int size() + { + return SIZE; + } + + @Override + public A getValue0() + { + return value0; + } + + @Override + public A setValue0(A newValue) + { + A retVal = value0; + setValue(0, newValue); + value0 = newValue; + return retVal; + } + + @Override + public B getValue1() + { + return value1; + } + + @Override + public B setValue1(B newValue) + { + B retVal = value1; + setValue(1, newValue); + value1 = newValue; + return retVal; + } + + @Override + public C getValue2() + { + return value2; + } + + @Override + public C setValue2(C newValue) + { + C retVal = value2; + setValue(2, newValue); + value2 = newValue; + return retVal; + } + + @Override + public D getValue3() + { + return value3; + } + + @Override + public D setValue3(D newValue) + { + D retVal = value3; + setValue(3, newValue); + value3 = newValue; + return retVal; + } + + @Override + public E getValue4() + { + return value4; + } + + @Override + public E setValue4(E newValue) + { + E retVal = value4; + setValue(4, newValue); + value4 = newValue; + return retVal; + } + + @Override + public F getValue5() + { + return value5; + } + + @Override + public F setValue5(F newValue) + { + F retVal = value5; + setValue(5, newValue); + value5 = newValue; + return retVal; + } +} diff --git a/src/org/infinity/util/tuples/Triple.java b/src/org/infinity/util/tuples/Triple.java new file mode 100644 index 000000000..9739086de --- /dev/null +++ b/src/org/infinity/util/tuples/Triple.java @@ -0,0 +1,174 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.util.tuples; + +import java.util.Collection; +import java.util.Iterator; + +/** + * A tuple class that can store three elements. + */ +public class Triple extends Tuple implements TupleValue0, TupleValue1, TupleValue2 +{ + private static final int SIZE = 3; + + private A value0; + private B value1; + private C value2; + + /** + * Creates a new tuple instance with the specified elements. + * @param the tuple element type. + * @param value0 The first element to store in the tuple. + * @param value2 The second element to store in the tuple. + * @param value3 The third element to store in the tuple. + * @return A new tuple instance. + */ + public static Triple with(A value0, B value1, C value2) + { + return new Triple<>(value0, value1, value2); + } + + /** + * Creates a new tuple from the array. The array must contain at least 3 elements. + * @param the tuple element type. + * @param arr The array to be used as source for the tuple. + * @return A new tuple instance. + */ + public static Triple fromArray(T[] arr) + { + if (arr == null) { + throw new IllegalArgumentException("Array cannot be null"); + } + if (arr.length < SIZE) { + throw new IllegalArgumentException(String.format("Array must contain at least %d elements", SIZE)); + } + return new Triple<>(arr[0], arr[1], arr[2]); + } + + /** + * Creates a new tuple from the collection. The collection must contain at least 3 elements. + * @param the tuple element type. + * @param col the collection to be used as source for the tuple. + * @return a new tuple instance. + */ + public static Triple fromCollection(Collection col) + { + if (col == null) { + throw new IllegalArgumentException("Collection cannot be null"); + } + if (col.size() < SIZE) { + throw new IllegalArgumentException(String.format("Collection must contain at least %d elements", SIZE)); + } + Iterator iter = col.iterator(); + T el0 = iter.next(); + T el1 = iter.next(); + T el2 = iter.next(); + return new Triple<>(el0, el1, el2); + } + + /** + * Creates a new tuple from the {@code Iterable} object. + * @param the tuple element type. + * @param iterator the {@code Iterable} object to be used as source for the tuple. + * @return a new tuple instance. + */ + public static Triple fromIterable(Iterable iterator) + { + return fromIterable(iterator, 0); + } + + /** + * Creates a new tuple from the {@code Iterable} object, starting the specified index. + * @param the tuple element type. + * @param iterator the {@code Iterable} object to be used as source for the tuple. + * @param index start index in {@code Iterable} object. + * @return A new tuple instance. + */ + public static Triple fromIterable(Iterable iterator, int index) + { + if (iterator == null) { + throw new IllegalArgumentException("Iterator cannot be null"); + } + + Iterator iter = iterator.iterator(); + for (int i = 0; i < index; i++) { + if (iter.hasNext()) { + iter.next(); + } + i++; + } + + T el0 = iter.hasNext() ? iter.next() : null; + T el1 = iter.hasNext() ? iter.next() : null; + T el2 = iter.hasNext() ? iter.next() : null; + return new Triple<>(el0, el1, el2); + } + + /** + * Constructs a new Triple instance and initializes it with the specified arguments. + * @param value0 the first value of the Triple. + * @param value1 the second value of the Triple. + * @param value2 the third value of the Triple. + */ + public Triple(A value0, B value1, C value2) + { + super(value0, value1, value2); + this.value0 = value0; + this.value1 = value1; + this.value2 = value2; + } + + @Override + public int size() + { + return SIZE; + } + + @Override + public A getValue0() + { + return value0; + } + + @Override + public A setValue0(A newValue) + { + A retVal = value0; + setValue(0, newValue); + value0 = newValue; + return retVal; + } + + @Override + public B getValue1() + { + return value1; + } + + @Override + public B setValue1(B newValue) + { + B retVal = value1; + setValue(1, newValue); + value1 = newValue; + return retVal; + } + + @Override + public C getValue2() + { + return value2; + } + + @Override + public C setValue2(C newValue) + { + C retVal = value2; + setValue(2, newValue); + value2 = newValue; + return retVal; + } +} diff --git a/src/org/infinity/util/tuples/Tuple.java b/src/org/infinity/util/tuples/Tuple.java new file mode 100644 index 000000000..6ced051c4 --- /dev/null +++ b/src/org/infinity/util/tuples/Tuple.java @@ -0,0 +1,262 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.util.tuples; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.function.Function; + +public abstract class Tuple implements Iterable, Comparable +{ + public static final Function, String> FMT_DEFAULT = (list) -> list.toString(); + + private final Object[] values; + private final List valueList; + + private Function, String> formatter; + + protected Tuple(Object... values) + { + this.values = values; + this.valueList = Arrays.asList(this.values); + } + + /** + * Returns the number of elements that can be stored in the tuple. + * @return size of the tuple. + */ + public abstract int size(); + + /** + * Returns the value at the specified element position. + * @param pos the element position in the tuple. + * @return the value. + */ + public Object getValue(int pos) + { + if (pos < 0 || pos >= size()) { + throw new IllegalArgumentException(String.format("Invalid position: %d. Valid range: [0, %d]", pos, size() - 1)); + } + return values[pos]; + } + + /** + * Assigns a new value to specified element position. + * @param pos the element position in the tuple. + * @param value the new value. + * @return the previous value. + */ + public Object setValue(int pos, Object value) + { + if (pos < 0 || pos >= size()) { + throw new IllegalArgumentException(String.format("Invalid position: %d. Valid range: [0, %d]", pos, size() - 1)); + } + Object retVal = values[pos]; + values[pos] = pos; + valueList.set(pos, value); + return retVal; + } + + /** + * Returns an iterator over the elements in this tuple in proper sequence. + */ + @Override + public Iterator iterator() + { + return this.valueList.iterator(); + } + + /** + * Returns a functional interface used to generate the return value of the {@link #toString()} method. + *

    It takes a list of {@code Objects} which represent the values assigned to the tuple and returns a + * textual representation of the values. + * @return Function to generate the return value of the {@link #toString()} method. + */ + public Function, String> getFormatter() + { + return (formatter != null) ? formatter : FMT_DEFAULT; + } + + /** + * Assigns a new functional interface responsible for generating the return value of the {@link #toString()} method. + *

    It takes a list of {@code Objects} which represent the values assigned to the tuple and returns a + * textual representation of the values. + * @param formatter the {@code Function} to generate the return value of the {@link #toString()} method. + * Specify {@code null} to revert to the default function. + */ + public void setFormatter(Function, String> formatter) + { + this.formatter = formatter; + } + + @Override + public String toString() + { + return getFormatter().apply(valueList); + } + + /** + * Returns {@code true} if this tuple contains the specified element. + * More formally, returns {@code true} if and only if this tuple contains at least one element {@code e} + * such that {@code (o==null ? e==null : o.equals(e))}. + * @param o element whose presence in this tuple is to be tested. + * @return {@code true} if this list contains the specified element. + */ + public boolean contains(Object o) + { + return indexOf(o) >= 0; + } + + /** + * Returns {@code true} if this tuple contains all of the elements of the specified collection. + * @param collection collection to be checked for containment in this tuple. + * @return {@code true} if this tuple contains all of the elements of the specified collection. + */ + public boolean containsAll(Collection collection) + { + for (final Object o : collection) { + if (!contains(o)) { + return false; + } + } + return true; + } + + /** + * Returns {@code true} if this tuple contains all of the elements of the specified array. + * @param values array to be checked for containment in this tuple. + * @return {@code true} if this tuple contains all of the elements of the specified array. + */ + public boolean containsAll(Object... values) + { + if (values == null) { + throw new IllegalArgumentException("Specified argument cannot be null"); + } + for (final Object o : values) { + if (!contains(o)) { + return false; + } + } + return true; + } + + /** + * Returns the index of the first occurrence of the specified element in this tuple, or -1 if this tuple + * does not contain the element. + * More formally, returns the lowest index {@code i} such that {@code (o==null ? get(i)==null : o.equals(get(i)))}, + * or -1 if there is no such index. + * @param o element to search for. + * @return the index of the first occurrence of the specified element in this tuple, + * or -1 if this tuple does not contain the element. + */ + public int indexOf(Object o) + { + int retVal = 0; + for (final Object v : valueList) { + if (v == null) { + if (o == null) { + return retVal; + } + } else { + if (v.equals(o)) { + return retVal; + } + } + retVal++; + } + return -1; + } + + /** + * Returns the index of the last occurrence of the specified element in this tuple, or -1 if this tuple + * does not contain the element. + * More formally, returns the highest index {@code i} such that {@code (o==null ? get(i)==null : o.equals(get(i)))}, + * or -1 if there is no such index. + * @param o element to search for. + * @return the index of the last occurrence of the specified element in this tuple, + * or -1 if this tuple does not contain the element. + */ + public int lastIndexOf(Object o) + { + for (int i = size() - 1; i >= 0; i--) { + final Object v = valueList.get(i); + if (v == null) { + if (o == null) { + return i; + } + } else { + if (v.equals(o)) { + return i; + } + } + } + return -1; + } + + /** + * Returns an unmodifiable list of the elements stored in this tuple. + * @return unmodifiable list with the elements of this tuple. + */ + public List toList() + { + return Collections.unmodifiableList(new ArrayList<>(valueList)); + } + + /** + * Returns an array with the elements stored in this tuple. + * @return array with the elements of this tuple. + */ + public Object[] toArray() + { + return values.clone(); + } + + @Override + public boolean equals(Object o) + { + if (this == o) { + return true; + } + if (o == null) { + return false; + } + if (getClass() != o.getClass()) { + return false; + } + Tuple other = (Tuple)o; + return valueList.equals(other.valueList); + } + + @Override + public int hashCode() + { + return valueList.hashCode(); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Override + public int compareTo(Tuple o) + { + int len = values.length; + Object[] values2 = o.values; + int len2 = values2.length; + + for (int i = 0; i < len && i < len2; i++) { + Comparable el = (Comparable)values[i]; + Comparable el2 = (Comparable)values2[i]; + + int cmp = el.compareTo(el2); + if (cmp != 0) { + return cmp; + } + } + + return (len - len2); + } +} diff --git a/src/org/infinity/util/tuples/TupleValue0.java b/src/org/infinity/util/tuples/TupleValue0.java new file mode 100644 index 000000000..f16ece6cc --- /dev/null +++ b/src/org/infinity/util/tuples/TupleValue0.java @@ -0,0 +1,24 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.util.tuples; + +/** + * Allows read/write access to the first tuple element. + */ +public interface TupleValue0 +{ + /** + * Returns the first element of the tuple. + * @return first element of the tuple. + */ + public T getValue0(); + + /** + * Assigns a new value to the first element of the tuple. + * @param newValue the new value to assign. + * @return the previously assigned value of the first tuple element. + */ + public T setValue0(T newValue); +} diff --git a/src/org/infinity/util/tuples/TupleValue1.java b/src/org/infinity/util/tuples/TupleValue1.java new file mode 100644 index 000000000..fc2a6b329 --- /dev/null +++ b/src/org/infinity/util/tuples/TupleValue1.java @@ -0,0 +1,24 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.util.tuples; + +/** + * Allows read/write access to the second tuple element. + */ +public interface TupleValue1 +{ + /** + * Returns the second element of the tuple. + * @return second element of the tuple. + */ + public T getValue1(); + + /** + * Assigns a new value to the second element of the tuple. + * @param newValue the new value to assign. + * @return the previously assigned value of the second tuple element. + */ + public T setValue1(T newValue); +} diff --git a/src/org/infinity/util/tuples/TupleValue2.java b/src/org/infinity/util/tuples/TupleValue2.java new file mode 100644 index 000000000..8e4f91f78 --- /dev/null +++ b/src/org/infinity/util/tuples/TupleValue2.java @@ -0,0 +1,24 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.util.tuples; + +/** + * Allows read/write access to the third tuple element. + */ +public interface TupleValue2 +{ + /** + * Returns the third element of the tuple. + * @return third element of the tuple. + */ + public T getValue2(); + + /** + * Assigns a new value to the third element of the tuple. + * @param newValue the new value to assign. + * @return the previously assigned value of the third tuple element. + */ + public T setValue2(T newValue); +} diff --git a/src/org/infinity/util/tuples/TupleValue3.java b/src/org/infinity/util/tuples/TupleValue3.java new file mode 100644 index 000000000..239cacbff --- /dev/null +++ b/src/org/infinity/util/tuples/TupleValue3.java @@ -0,0 +1,24 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.util.tuples; + +/** + * Allows read/write access to the fourth tuple element. + */ +public interface TupleValue3 +{ + /** + * Returns the fourth element of the tuple. + * @return fourth element of the tuple. + */ + public T getValue3(); + + /** + * Assigns a new value to the fourth element of the tuple. + * @param newValue the new value to assign. + * @return the previously assigned value of the fourth tuple element. + */ + public T setValue3(T newValue); +} diff --git a/src/org/infinity/util/tuples/TupleValue4.java b/src/org/infinity/util/tuples/TupleValue4.java new file mode 100644 index 000000000..10b8fa6a7 --- /dev/null +++ b/src/org/infinity/util/tuples/TupleValue4.java @@ -0,0 +1,24 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.util.tuples; + +/** + * Allows read/write access to the fifth tuple element. + */ +public interface TupleValue4 +{ + /** + * Returns the fifth element of the tuple. + * @return fifth element of the tuple. + */ + public T getValue4(); + + /** + * Assigns a new value to the fifth element of the tuple. + * @param newValue the new value to assign. + * @return the previously assigned value of the fifth tuple element. + */ + public T setValue4(T newValue); +} diff --git a/src/org/infinity/util/tuples/TupleValue5.java b/src/org/infinity/util/tuples/TupleValue5.java new file mode 100644 index 000000000..4daa9acc9 --- /dev/null +++ b/src/org/infinity/util/tuples/TupleValue5.java @@ -0,0 +1,24 @@ +// Near Infinity - An Infinity Engine Browser and Editor +// Copyright (C) 2001 - 2021 Jon Olav Hauglid +// See LICENSE.txt for license information + +package org.infinity.util.tuples; + +/** + * Allows read/write access to the sixth tuple element. + */ +public interface TupleValue5 +{ + /** + * Returns the sixth element of the tuple. + * @return sixth element of the tuple. + */ + public T getValue5(); + + /** + * Assigns a new value to the sixth element of the tuple. + * @param newValue the new value to assign. + * @return the previously assigned value of the sixth tuple element. + */ + public T setValue5(T newValue); +}