// 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.dlg; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; import javax.swing.JComponent; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.infinity.NearInfinity; import org.infinity.datatype.DecNumber; import org.infinity.datatype.Flag; import org.infinity.datatype.SectionCount; import org.infinity.datatype.SectionOffset; import org.infinity.datatype.TextString; import org.infinity.gui.StructViewer; import org.infinity.gui.WindowBlocker; import org.infinity.resource.AbstractStruct; import org.infinity.resource.AddRemovable; import org.infinity.resource.HasAddRemovable; import org.infinity.resource.HasViewerTabs; import org.infinity.resource.Resource; import org.infinity.resource.StructEntry; import org.infinity.resource.key.ResourceEntry; public final class DlgResource extends AbstractStruct implements Resource, HasAddRemovable, HasViewerTabs, ChangeListener { // DLG-specific field labels public static final String DLG_OFFSET_STATES = "States offset"; public static final String DLG_OFFSET_RESPONSES = "Responses offset"; public static final String DLG_OFFSET_STATE_TRIGGERS = "State triggers offset"; public static final String DLG_OFFSET_RESPONSE_TRIGGERS = "Response triggers offset"; public static final String DLG_OFFSET_ACTIONS = "Actions offset"; public static final String DLG_NUM_STATES = "# states"; public static final String DLG_NUM_RESPONSES = "# responses"; public static final String DLG_NUM_STATE_TRIGGERS = "# state triggers"; public static final String DLG_NUM_RESPONSE_TRIGGERS = "# response triggers"; public static final String DLG_NUM_ACTIONS = "# actions"; public static final String DLG_THREAT_RESPONSE = "Threat response"; private static final String TAB_TREE = "Tree"; public static final String s_NonInt[] = {"Pausing dialogue", "Turn hostile", "Escape area", "Ignore attack"}; private SectionCount countState, countTrans, countStaTri, countTranTri, countAction; private SectionOffset offsetState, offsetTrans, offsetStaTri, offsetTranTri, offsetAction; private Viewer detailViewer; private TreeViewer treeViewer; public DlgResource(ResourceEntry entry) throws Exception { super(entry); } // --------------------- Begin Interface HasAddRemovable --------------------- @Override public AddRemovable[] getAddRemovables() throws Exception { return new AddRemovable[]{new State(), new Transition(), new StateTrigger(), new ResponseTrigger(), new Action()}; } @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 TAB_TREE; } return null; } @Override public JComponent getViewerTab(int index) { switch (index) { case 0: if (detailViewer == null) detailViewer = new Viewer(this); return detailViewer; case 1: if (treeViewer == null) treeViewer = new TreeViewer(this); return treeViewer; } return null; } @Override public boolean viewerTabAddedBefore(int index) { return (index == 0); } // --------------------- End Interface HasViewerTabs --------------------- // --------------------- Begin Interface Writeable --------------------- @Override public void write(OutputStream os) throws IOException { offsetState.setValue(0x30); if (getFieldCount() >= 13 && getField(12).getName().equalsIgnoreCase(DLG_THREAT_RESPONSE)) offsetState.setValue(0x34); offsetTrans.setValue(offsetState.getValue() + 0x10 * countState.getValue()); offsetStaTri.setValue(offsetTrans.getValue() + 0x20 * countTrans.getValue()); offsetTranTri.setValue(offsetStaTri.getValue() + 0x8 * countStaTri.getValue()); offsetAction.setValue(offsetTranTri.getValue() + 0x8 * countTranTri.getValue()); int stringoff = offsetAction.getValue() + 0x8 * countAction.getValue(); for (int i = 0; i < getFieldCount(); i++) { Object o = getField(i); if (o instanceof AbstractCode) { stringoff += ((AbstractCode)o).updateOffset(stringoff); } } super.write(os); for (int i = 0; i < getFieldCount(); i++) { Object o = getField(i); if (o instanceof AbstractCode) { ((AbstractCode)o).writeString(os); } } } // --------------------- End Interface Writeable --------------------- // --------------------- Begin Interface ChangeListener --------------------- @Override public void stateChanged(ChangeEvent event) { if (getViewer() != null) { if (getViewer().isTabSelected(getViewer().getTabIndex(TAB_TREE))) { initTreeView(); } } } // --------------------- End Interface ChangeListener --------------------- @Override protected void viewerInitialized(StructViewer viewer) { if (viewer.isTabSelected(getViewer().getTabIndex(TAB_TREE))) { initTreeView(); } viewer.addTabChangeListener(this); } @Override protected void datatypeAdded(AddRemovable datatype) { updateReferences(datatype, true); } @Override protected void datatypeRemoved(AddRemovable datatype) { updateReferences(datatype, false); } @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 (!version.toString().equalsIgnoreCase("V1.0")) { clearFields(); throw new Exception("Unsupported version: " + version); } countState = new SectionCount(buffer, offset + 8, 4, DLG_NUM_STATES, State.class); addField(countState); offsetState = new SectionOffset(buffer, offset + 12, DLG_OFFSET_STATES, State.class); addField(offsetState); countTrans = new SectionCount(buffer, offset + 16, 4, DLG_NUM_RESPONSES, Transition.class); addField(countTrans); offsetTrans = new SectionOffset(buffer, offset + 20, DLG_OFFSET_RESPONSES, Transition.class); addField(offsetTrans); offsetStaTri = new SectionOffset(buffer, offset + 24, DLG_OFFSET_STATE_TRIGGERS, StateTrigger.class); addField(offsetStaTri); countStaTri = new SectionCount(buffer, offset + 28, 4, DLG_NUM_STATE_TRIGGERS, StateTrigger.class); addField(countStaTri); offsetTranTri = new SectionOffset(buffer, offset + 32, DLG_OFFSET_RESPONSE_TRIGGERS, ResponseTrigger.class); addField(offsetTranTri); countTranTri = new SectionCount(buffer, offset + 36, 4, DLG_NUM_RESPONSE_TRIGGERS, ResponseTrigger.class); addField(countTranTri); offsetAction = new SectionOffset(buffer, offset + 40, DLG_OFFSET_ACTIONS, org.infinity.resource.dlg.Action.class); addField(offsetAction); countAction = new SectionCount(buffer, offset + 44, 4, DLG_NUM_ACTIONS, org.infinity.resource.dlg.Action.class); addField(countAction); if (offsetState.getValue() > 0x30) { addField(new Flag(buffer, offset + 48, 4, DLG_THREAT_RESPONSE, s_NonInt)); } offset = offsetState.getValue(); for (int i = 0; i < countState.getValue(); i++) { State state = new State(this, buffer, offset, i); offset = state.getEndOffset(); addField(state); } offset = offsetTrans.getValue(); for (int i = 0; i < countTrans.getValue(); i++) { Transition transition = new Transition(this, buffer, offset, i); offset = transition.getEndOffset(); addField(transition); } int textSize = 0; offset = offsetStaTri.getValue(); for (int i = 0; i < countStaTri.getValue(); i++) { StateTrigger statri = new StateTrigger(buffer, offset, i); offset += statri.getSize(); textSize += statri.getTextLength(); addField(statri); } offset = offsetTranTri.getValue(); for (int i = 0; i < countTranTri.getValue(); i++) { ResponseTrigger trantri = new ResponseTrigger(buffer, offset, i); offset += trantri.getSize(); textSize += trantri.getTextLength(); addField(trantri); } offset = offsetAction.getValue(); for (int i = 0; i < countAction.getValue(); i++) { Action action = new Action(buffer, offset, i); offset += action.getSize(); textSize += action.getTextLength(); addField(action); } return offset + textSize; } // sorry for this (visibility) public void showStateWithStructEntry(StructEntry entry) { if (detailViewer == null) { getViewerTab(0); } detailViewer.showStateWithStructEntry(entry); } private void initTreeView() { WindowBlocker.blockWindow(NearInfinity.getInstance(), true); try { treeViewer.init(); } finally { WindowBlocker.blockWindow(NearInfinity.getInstance(), false); } } // Updates trigger/action references in states and responses 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 idxTrigger = (trigger.getOffset() - ofsTriggers) / trigger.getSize(); // adjusting state trigger references while (numStates > 0) { State state = (State)getAttribute(ofsStates, false); if (state != null) { DecNumber dec = (DecNumber)state.getAttribute(State.DLG_STATE_TRIGGER_INDEX); if (dec.getValue() == idxTrigger) { if (added) { dec.incValue(1); } else { dec.setValue(-1); } } else if (dec.getValue() > idxTrigger) { if (added) { dec.incValue(1); } else { dec.incValue(-1); } } ofsStates += state.getSize(); } numStates--; } } 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 idxTrigger = (trigger.getOffset() - ofsTriggers) / trigger.getSize(); // adjusting response trigger references while (numTrans > 0) { Transition trans = (Transition)getAttribute(ofsTrans, false); if (trans != null) { Flag flags = (Flag)trans.getAttribute(Transition.DLG_TRANS_FLAGS); if (flags.isFlagSet(1)) { DecNumber dec = (DecNumber)trans.getAttribute(Transition.DLG_TRANS_TRIGGER_INDEX); if (dec.getValue() == idxTrigger) { if (added) { dec.incValue(1); } else { flags.setValue(flags.getValue() & ~2); dec.setValue(0); } } else if (dec.getValue() > idxTrigger) { if (added) { dec.incValue(1); } else { dec.incValue(-1); } } } ofsTrans += trans.getSize(); } numTrans--; } } 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 idxAction = (action.getOffset() - ofsActions) / action.getSize(); // adjusting action references while (numTrans > 0) { Transition trans = (Transition)getAttribute(ofsTrans, false); if (trans != null) { Flag flags = (Flag)trans.getAttribute(Transition.DLG_TRANS_FLAGS); if (flags.isFlagSet(2)) { DecNumber dec = (DecNumber)trans.getAttribute(Transition.DLG_TRANS_ACTION_INDEX); if (dec.getValue() == idxAction) { if (added) { dec.incValue(1); } else { flags.setValue(flags.getValue() & ~4); dec.setValue(0); } } else if (dec.getValue() > idxAction) { if (added) { dec.incValue(1); } else { dec.incValue(-1); } } } ofsTrans += trans.getSize(); } numTrans--; } } } }