// 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.sto;
import java.nio.ByteBuffer;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JScrollPane;
import org.infinity.datatype.Bitmap;
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.StringRef;
import org.infinity.datatype.TextString;
import org.infinity.datatype.Unknown;
import org.infinity.datatype.UnsignDecNumber;
import org.infinity.gui.StructViewer;
import org.infinity.gui.hexview.BasicColorMap;
import org.infinity.gui.hexview.StructHexViewer;
import org.infinity.resource.AbstractStruct;
import org.infinity.resource.AddRemovable;
import org.infinity.resource.HasAddRemovable;
import org.infinity.resource.HasViewerTabs;
import org.infinity.resource.Profile;
import org.infinity.resource.Resource;
import org.infinity.resource.StructEntry;
import org.infinity.resource.key.ResourceEntry;
import org.infinity.search.SearchOptions;
public final class StoResource extends AbstractStruct implements Resource, HasAddRemovable, HasViewerTabs
{
// STO-specific field labels
public static final String STO_TYPE = "Type";
public static final String STO_NAME = "Name";
public static final String STO_FLAGS = "Flags";
public static final String STO_MARKUP_SELL = "Sell markup";
public static final String STO_MARKUP_BUY = "Buy markup";
public static final String STO_DEPRECIATION_RATE = "Depreciation rate";
public static final String STO_STEALING_DIFFICULTY = "Stealing difficulty";
public static final String STO_STORAGE_CAPACITY = "Storage capacity";
public static final String STO_OFFSET_ITEMS_PURCHASED = "Items purchased offset";
public static final String STO_NUM_ITEMS_PURCHASED = "# items purchased";
public static final String STO_OFFSET_ITEMS_FOR_SALE = "Items for sale offset";
public static final String STO_NUM_ITEMS_FOR_SALE = "# items for sale";
public static final String STO_LORE = "Lore";
public static final String STO_COST_TO_IDENTIFY = "Cost to identify";
public static final String STO_RUMORS_DRINKS = "Rumors (drinks)";
public static final String STO_OFFSET_DRINKS = "Drinks for sale offset";
public static final String STO_NUM_DRINKS = "# drinks for sale";
public static final String STO_RUMORS_DONATIONS = "Rumors (donations)";
public static final String STO_ROOMS_AVAILABLE = "Available rooms";
public static final String STO_PRICE_ROOM_PEASANT = "Price peasant room";
public static final String STO_PRICE_ROOM_MERCHANT = "Price merchant room";
public static final String STO_PRICE_ROOM_NOBLE = "Price noble room";
public static final String STO_PRICE_ROOM_ROYAL = "Price royal room";
public static final String STO_OFFSET_CURES = "Cures for sale offset";
public static final String STO_NUM_CURES = "# cures for sale";
// private static final String[] s_type = {"Store", "Tavern", "Inn", "Temple"};
public static final String[] s_type9 = {"Store", "Tavern", "Inn", "Temple", "Container"};
public static final String[] s_type_bg2 = {"Store", "Tavern", "Inn", "Temple", "", "Container"};
// 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", "Can buy cures",
"Can buy drinks", "", "", "Tavern quality 1", "Tavern quality 2",
"", "Fence", "EE: Ignore reputation", "Ex: Toggle recharge",
"EE: Can sell critical"};
public static final String[] s_rooms = {"No rooms available", "Peasant", "Merchant", "Noble", "Royal"};
private StructHexViewer hexViewer;
public static String getSearchString(ByteBuffer buffer)
{
return new StringRef(buffer, 12, "").toString().trim();
}
public StoResource(ResourceEntry entry) throws Exception
{
super(entry);
}
// --------------------- Begin Interface HasAddRemovable ---------------------
@Override
public AddRemovable[] getAddRemovables() throws Exception
{
TextString version = (TextString)getAttribute(COMMON_VERSION);
if (version.toString().equals("V1.1"))
return new AddRemovable[]{new Purchases(), new ItemSale11(), new Drink(), new Cure()};
else
return new AddRemovable[]{new Purchases(), new ItemSale(), new Drink(), new Cure()};
}
@Override
public AddRemovable confirmAddEntry(AddRemovable entry) throws Exception
{
return entry;
}
@Override
public boolean confirmRemoveEntry(AddRemovable entry) throws Exception
{
return true;
}
// --------------------- End Interface HasAddRemovable ---------------------
// --------------------- Begin Interface HasViewerTabs ---------------------
@Override
public int getViewerTabCount()
{
return 2;
}
@Override
public String getViewerTabName(int index)
{
switch (index) {
case 0:
return StructViewer.TAB_VIEW;
case 1:
return StructViewer.TAB_RAW;
}
return null;
}
@Override
public JComponent getViewerTab(int index)
{
switch (index) {
case 0:
{
JScrollPane scroll = new JScrollPane(new Viewer(this));
scroll.setBorder(BorderFactory.createEmptyBorder());
return scroll;
}
case 1:
{
if (hexViewer == null) {
hexViewer = new StructHexViewer(this, new BasicColorMap(this, true));
}
return hexViewer;
}
}
return null;
}
@Override
public boolean viewerTabAddedBefore(int index)
{
return (index == 0);
}
// --------------------- End Interface HasViewerTabs ---------------------
@Override
public int read(ByteBuffer buffer, int offset) throws Exception
{
addField(new TextString(buffer, offset, 4, COMMON_SIGNATURE));
TextString version = new TextString(buffer, offset + 4, 4, COMMON_VERSION);
addField(version);
if (Profile.getEngine() == Profile.Engine.BG2 || Profile.isEnhancedEdition()) {
addField(new Bitmap(buffer, offset + 8, 4, STO_TYPE, s_type_bg2));
addField(new StringRef(buffer, offset + 12, STO_NAME));
} else {
addField(new Bitmap(buffer, offset + 8, 4, STO_TYPE, s_type9));
addField(new StringRef(buffer, offset + 12, STO_NAME));
}
addField(new Flag(buffer, offset + 16, 4, STO_FLAGS, s_flag_bg2));
addField(new DecNumber(buffer, offset + 20, 4, STO_MARKUP_SELL));
addField(new DecNumber(buffer, offset + 24, 4, STO_MARKUP_BUY));
addField(new DecNumber(buffer, offset + 28, 4, STO_DEPRECIATION_RATE));
// addField(new Unknown(buffer, offset + 30, 2));
addField(new DecNumber(buffer, offset + 32, 2, STO_STEALING_DIFFICULTY));
if (version.toString().equalsIgnoreCase("V9.0")) {
addField(new Unknown(buffer, offset + 34, 2));
} else {
addField(new UnsignDecNumber(buffer, offset + 34, 2, STO_STORAGE_CAPACITY));
}
addField(new Unknown(buffer, offset + 36, 8));
SectionOffset offset_purchased = new SectionOffset(buffer, offset + 44, STO_OFFSET_ITEMS_PURCHASED,
Purchases.class);
addField(offset_purchased);
SectionCount count_purchased = new SectionCount(buffer, offset + 48, 4, STO_NUM_ITEMS_PURCHASED,
Purchases.class);
addField(count_purchased);
SectionOffset offset_sale;
SectionCount count_sale;
if (version.toString().equals("V1.0") || version.toString().equals("V9.0")) {
offset_sale = new SectionOffset(buffer, offset + 52, STO_OFFSET_ITEMS_FOR_SALE,
ItemSale.class);
addField(offset_sale);
count_sale = new SectionCount(buffer, offset + 56, 4, STO_NUM_ITEMS_FOR_SALE,
ItemSale.class);
addField(count_sale);
}
else if (version.toString().equals("V1.1")) {
offset_sale = new SectionOffset(buffer, offset + 52, STO_OFFSET_ITEMS_FOR_SALE,
ItemSale11.class);
addField(offset_sale);
count_sale = new SectionCount(buffer, offset + 56, 4, STO_NUM_ITEMS_FOR_SALE,
ItemSale11.class);
addField(count_sale);
}
else {
clearFields();
throw new Exception("Unsupported version: " + version);
}
addField(new DecNumber(buffer, offset + 60, 4, STO_LORE));
addField(new DecNumber(buffer, offset + 64, 4, STO_COST_TO_IDENTIFY));
addField(new ResourceRef(buffer, offset + 68, STO_RUMORS_DRINKS, "DLG"));
SectionOffset offset_drinks = new SectionOffset(buffer, offset + 76, STO_OFFSET_DRINKS,
Drink.class);
addField(offset_drinks);
SectionCount count_drinks = new SectionCount(buffer, offset + 80, 4, STO_NUM_DRINKS,
Drink.class);
addField(count_drinks);
addField(new ResourceRef(buffer, offset + 84, STO_RUMORS_DONATIONS, "DLG"));
addField(new Flag(buffer, offset + 92, 4, STO_ROOMS_AVAILABLE, s_rooms));
addField(new DecNumber(buffer, offset + 96, 4, STO_PRICE_ROOM_PEASANT));
addField(new DecNumber(buffer, offset + 100, 4, STO_PRICE_ROOM_MERCHANT));
addField(new DecNumber(buffer, offset + 104, 4, STO_PRICE_ROOM_NOBLE));
addField(new DecNumber(buffer, offset + 108, 4, STO_PRICE_ROOM_ROYAL));
SectionOffset offset_cures = new SectionOffset(buffer, offset + 112, STO_OFFSET_CURES,
Cure.class);
addField(offset_cures);
SectionCount count_cures = new SectionCount(buffer, offset + 116, 4, STO_NUM_CURES,
Cure.class);
addField(count_cures);
addField(new Unknown(buffer, offset + 120, 36));
if (version.toString().equals("V9.0")) {
addField(new UnsignDecNumber(buffer, offset + 156, 2, STO_STORAGE_CAPACITY));
addField(new Unknown(buffer, offset + 158, 82));
}
offset = offset_drinks.getValue();
for (int i = 0; i < count_drinks.getValue(); i++) {
Drink drink = new Drink(this, buffer, offset, i);
offset = drink.getEndOffset();
addField(drink);
}
offset = offset_sale.getValue();
if (version.toString().equals("V1.0") || version.toString().equals("V9.0")) {
for (int i = 0; i < count_sale.getValue(); i++) {
ItemSale sale = new ItemSale(this, buffer, offset, i);
offset = sale.getEndOffset();
addField(sale);
}
}
else if (version.toString().equals("V1.1")) {
for (int i = 0; i < count_sale.getValue(); i++) {
ItemSale11 sale = new ItemSale11(this, buffer, offset, i);
offset = sale.getEndOffset();
addField(sale);
}
}
offset = offset_cures.getValue();
for (int i = 0; i < count_cures.getValue(); i++) {
Cure cure = new Cure(this, buffer, offset, i);
offset = cure.getEndOffset();
addField(cure);
}
offset = offset_purchased.getValue();
for (int i = 0; i < count_purchased.getValue(); i++) {
Purchases pur = new Purchases(buffer, offset, i);
offset += pur.getSize();
addField(pur);
}
int endoffset = offset;
for (int i = 0; i < getFieldCount(); i++) {
StructEntry entry = getField(i);
if (entry.getOffset() + entry.getSize() > endoffset)
endoffset = entry.getOffset() + entry.getSize();
}
return endoffset;
}
@Override
protected void viewerInitialized(StructViewer viewer)
{
viewer.addTabChangeListener(hexViewer);
}
@Override
protected void datatypeAdded(AddRemovable datatype)
{
if (hexViewer != null) {
hexViewer.dataModified();
}
}
@Override
protected void datatypeAddedInChild(AbstractStruct child, AddRemovable datatype)
{
super.datatypeAddedInChild(child, datatype);
if (hexViewer != null) {
hexViewer.dataModified();
}
}
@Override
protected void datatypeRemoved(AddRemovable datatype)
{
if (hexViewer != null) {
hexViewer.dataModified();
}
}
@Override
protected void datatypeRemovedInChild(AbstractStruct child, AddRemovable datatype)
{
super.datatypeRemovedInChild(child, datatype);
if (hexViewer != null) {
hexViewer.dataModified();
}
}
// Called by "Extended Search"
// Checks whether the specified resource entry matches all available search options.
public static boolean matchSearchOptions(ResourceEntry entry, SearchOptions searchOptions)
{
if (entry != null && searchOptions != null) {
try {
StoResource sto = new StoResource(entry);
Bitmap[] purchases;
ResourceRef[] items;
boolean retVal = true;
String key;
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);
if (ofs != null && ofs.getValue() > 0 && cnt != null && cnt.getValue() > 0) {
String itemLabel = SearchOptions.getResourceName(SearchOptions.STO_Item_Item1);
items = new ResourceRef[cnt.getValue()];
for (int i = 0; i < cnt.getValue(); i++) {
String itemStruct = String.format(SearchOptions.getResourceName(SearchOptions.STO_Item), i);
if (Profile.getEngine() == Profile.Engine.PST) {
ItemSale11 item = (ItemSale11)sto.getAttribute(itemStruct, false);
if (item != null) {
items[i] = (ResourceRef)item.getAttribute(itemLabel, false);
} else {
items[i] = null;
}
} else {
ItemSale item = (ItemSale)sto.getAttribute(itemStruct, false);
if (item != null) {
items[i] = (ResourceRef)item.getAttribute(itemLabel, false);
} else {
items[i] = null;
}
}
}
} else {
items = new ResourceRef[0];
}
ofs = (DecNumber)sto.getAttribute(STO_OFFSET_ITEMS_PURCHASED, false);
cnt = (DecNumber)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++) {
String label = String.format(SearchOptions.getResourceName(SearchOptions.STO_Purchased), i);
purchases[i] = (Bitmap)sto.getAttribute(label, false);
}
} else {
purchases = new Bitmap[0];
}
if (retVal) {
key = SearchOptions.STO_Name;
o = searchOptions.getOption(key);
StructEntry struct = sto.getAttribute(SearchOptions.getResourceName(key), false);
retVal &= SearchOptions.Utils.matchString(struct, o, false, false);
}
if (retVal) {
key = SearchOptions.STO_Type;
o = searchOptions.getOption(key);
StructEntry struct = sto.getAttribute(SearchOptions.getResourceName(key), false);
retVal &= SearchOptions.Utils.matchNumber(struct, o);
}
String[] keyList = new String[]{SearchOptions.STO_Purchased1, SearchOptions.STO_Purchased2,
SearchOptions.STO_Purchased3, SearchOptions.STO_Purchased4,
SearchOptions.STO_Purchased5};
for (int idx = 0; idx < keyList.length; idx++) {
if (retVal) {
key = keyList[idx];
o = searchOptions.getOption(key);
boolean found = false;
for (int idx2 = 0; idx2 < purchases.length; idx2++) {
if (purchases[idx2] != null) {
found |= SearchOptions.Utils.matchNumber(purchases[idx2], o);
}
}
retVal &= found || (o == null);
} else {
break;
}
}
keyList = new String[]{SearchOptions.STO_Flags, SearchOptions.STO_RoomsAvailable};
for (int idx = 0; idx < keyList.length; idx++) {
if (retVal) {
key = keyList[idx];
o = searchOptions.getOption(key);
StructEntry struct = sto.getAttribute(SearchOptions.getResourceName(key), false);
retVal &= SearchOptions.Utils.matchFlags(struct, o);
} else {
break;
}
}
keyList = new String[]{SearchOptions.STO_Depreciation, SearchOptions.STO_SellMarkup,
SearchOptions.STO_BuyMarkup, SearchOptions.STO_Stealing,
SearchOptions.STO_Capacity};
for (int idx = 0; idx < keyList.length; idx++) {
if (retVal) {
key = keyList[idx];
o = searchOptions.getOption(key);
StructEntry struct = sto.getAttribute(SearchOptions.getResourceName(key), false);
retVal &= SearchOptions.Utils.matchNumber(struct, o);
} else {
break;
}
}
keyList = new String[]{SearchOptions.STO_Item_Item1, SearchOptions.STO_Item_Item2,
SearchOptions.STO_Item_Item3, SearchOptions.STO_Item_Item4};
for (int idx = 0; idx < keyList.length; idx++) {
if (retVal) {
key = keyList[idx];
o = searchOptions.getOption(key);
boolean found = false;
for (int idx2 = 0; idx2 < items.length; idx2++) {
if (items[idx2] != null) {
found |= SearchOptions.Utils.matchResourceRef(items[idx2], o, false);
}
}
retVal &= found || (o == null);
} else {
break;
}
}
keyList = new String[]{SearchOptions.STO_Custom1, SearchOptions.STO_Custom2,
SearchOptions.STO_Custom3, SearchOptions.STO_Custom4};
for (int idx = 0; idx < keyList.length; idx++) {
if (retVal) {
key = keyList[idx];
o = searchOptions.getOption(key);
retVal &= SearchOptions.Utils.matchCustomFilter(sto, o);
} else {
break;
}
}
return retVal;
} catch (Exception e) {
}
}
return false;
}
}