// 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.chu;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JComponent;
import org.infinity.datatype.SectionCount;
import org.infinity.datatype.SectionOffset;
import org.infinity.datatype.TextString;
import org.infinity.datatype.Unknown;
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.HasViewerTabs;
import org.infinity.resource.Resource;
import org.infinity.resource.key.ResourceEntry;
import org.infinity.util.Pair;
public final class ChuResource extends AbstractStruct implements Resource, HasViewerTabs //, HasAddRemovable
{
// CHU-specific field labels
public static final String CHU_NUM_PANELS = "# panels";
public static final String CHU_OFFSET_CONTROLS = "Controls offset";
public static final String CHU_OFFSET_PANELS = "Panels offset";
private List<Pair<Integer>> listControls;
private int ofsPanels, numPanels, sizePanels, ofsControls, numControls;
private Viewer detailViewer;
private StructHexViewer hexViewer;
public ChuResource(ResourceEntry entry) throws Exception
{
super(entry);
}
// --------------------- Begin Interface Writeable ---------------------
@Override
public void write(OutputStream os) throws IOException
{
super.write(os);
for (int i = 0; i < getFieldCount(); i++) {
Object o = getField(i);
if (o instanceof Window) {
((Window)o).writeControlsTable(os);
}
}
for (int i = 0; i < getFieldCount(); i++) {
Object o = getField(i);
if (o instanceof Window) {
((Window)o).writeControls(os);
}
}
}
// --------------------- End Interface Writeable ---------------------
// --------------------- 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:
{
if (detailViewer == null) {
detailViewer = new Viewer(this);
}
return detailViewer;
}
case 1:
{
if (hexViewer == null) {
BasicColorMap colorMap = new BasicColorMap(this, false);
colorMap.setColoredEntry(BasicColorMap.Coloring.BLUE, Window.class);
colorMap.setColoredEntry(BasicColorMap.Coloring.GREEN, Control.class);
hexViewer = new StructHexViewer(this, colorMap);
}
return hexViewer;
}
}
return null;
}
@Override
public boolean viewerTabAddedBefore(int index)
{
return (index == 0);
}
// --------------------- End Interface HasViewerTabs ---------------------
// Write 'size' number of zeros to the output stream
void writeGap(OutputStream os, int startOfs, int endOfs) throws IOException
{
while (startOfs < endOfs) {
os.write(0);
startOfs++;
}
}
/** Returns the starting offset of available panels. */
public int getPanelsOffset()
{
return ofsPanels;
}
/** Returns the number of available panels. */
public int getPanelCount()
{
return numPanels;
}
/** Returns the starting offset of the control table. */
public int getControlsOffset()
{
return ofsControls;
}
/** Returns the number of available controls. */
public int getControlCount()
{
return numControls;
}
/** Returns the absolute starting offset of the control at the given index. */
public int getControlOffset(int index)
{
if (index >= 0 && index < listControls.size()) {
return listControls.get(index).getFirst();
} else {
return 0;
}
}
/** Returns the size of the control at the given index. */
public int getControlSize(int index)
{
if (index >= 0 && index < listControls.size()) {
return listControls.get(index).getSecond();
} else {
return 0;
}
}
/** Returns the panel size in bytes. */
public int getPanelSize()
{
return sizePanels;
}
/** Returns the given panel. */
public Window getPanel(int index)
{
if (index >= 0 && index < getPanelCount()) {
return (Window)getAttribute(Window.CHU_WINDOW_PANEL + " " + index);
} else {
return null;
}
}
@Override
public int read(ByteBuffer buffer, int offset) throws Exception
{
initData(buffer, offset);
addField(new TextString(buffer, offset, 4, COMMON_SIGNATURE));
addField(new TextString(buffer, offset + 4, 4, COMMON_VERSION));
addField(new SectionCount(buffer, offset + 8, 4, CHU_NUM_PANELS, Window.class));
addField(new SectionOffset(buffer, offset + 12, CHU_OFFSET_CONTROLS, Control.class));
addField(new SectionOffset(buffer, offset + 16, CHU_OFFSET_PANELS, Window.class));
offset += 20;
// handling optional gap between header and panels section
int endoffset = Math.min(getPanelsOffset(), getControlsOffset());
if (offset < endoffset) {
addField(new Unknown(buffer, offset, endoffset - offset, COMMON_UNUSED));
}
offset = endoffset;
for (int i = 0; i < getPanelCount(); i++) {
Window window = new Window(this, buffer, offset, i);
offset = window.getEndOffset();
endoffset = Math.max(endoffset, window.readControls(buffer));
addField(window);
}
// handling optional gap between panels section and controls section
if (offset < ofsControls) {
addField(new Unknown(buffer, offset, ofsControls - offset, COMMON_UNUSED));
}
return Math.max(offset, 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();
}
}
// initialize data required to reconstruct the original resource on save
private void initData(ByteBuffer buffer, int offset)
{
// loading header data
numPanels = buffer.getInt(offset + 8);
ofsControls = buffer.getInt(offset + 12);
ofsPanels = buffer.getInt(offset + 16);
if (numPanels > 0) {
sizePanels = Math.abs(ofsControls - ofsPanels) / numPanels;
} else {
sizePanels = 28;
}
if (sizePanels >= 36) sizePanels = 36; else if (sizePanels >= 28) sizePanels = 28;
// loading controls data
numControls = 0;
int curOfs = ofsPanels;
int skip = (sizePanels == 36) ? 8 : 0;
for (int i = 0; i < numPanels; i++) {
int numCtrl = buffer.getShort(curOfs + skip + 14);
int startIdx = buffer.getShort(curOfs + skip + 24);
numControls = Math.max(numControls, startIdx + numCtrl);
curOfs += sizePanels;
}
// Adding offset/size pairs for each control
curOfs = ofsControls;
if (listControls == null) {
listControls = new ArrayList<Pair<Integer>>();
}
int ofs = 0, len = 0;
for (int i = 0; i < numControls; i++, curOfs += 8) {
ofs = buffer.getInt(curOfs);
len = buffer.getInt(curOfs + 4);
listControls.add(new Pair<Integer>(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>(Integer.valueOf(ofs), Integer.valueOf(0)));
}
}