// 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;
import java.awt.Component;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import javax.swing.JComponent;
import javax.swing.JOptionPane;
import javax.swing.table.AbstractTableModel;
import org.infinity.datatype.Editable;
import org.infinity.datatype.InlineEditable;
import org.infinity.datatype.SectionCount;
import org.infinity.datatype.SectionOffset;
import org.infinity.datatype.Unknown;
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.key.BIFFResourceEntry;
import org.infinity.resource.key.ResourceEntry;
import org.infinity.util.io.ByteBufferOutputStream;
import org.infinity.util.io.FileManager;
import org.infinity.util.io.StreamUtils;
public abstract class AbstractStruct extends AbstractTableModel implements StructEntry, Viewable, Closeable
{
// Commonly used field labels
public static final String COMMON_SIGNATURE = "Signature";
public static final String COMMON_VERSION = "Version";
public static final String COMMON_UNKNOWN = "Unknown";
public static final String COMMON_UNUSED = "Unused";
public static final String COMMON_UNUSED_BYTES = "Unused bytes?";
private List<StructEntry> list;
private AbstractStruct superStruct;
private Map<Class<? extends StructEntry>, SectionCount> countmap;
private Map<Class<? extends StructEntry>, SectionOffset> offsetmap;
private ResourceEntry entry;
private String name;
private StructViewer viewer;
private boolean structChanged;
private int startoffset, endoffset, extraoffset;
private Collection<Component> viewerComponents = null;
private static void adjustEntryOffsets(AbstractStruct superStruct, AbstractStruct modifiedStruct,
AddRemovable datatype, int amount)
{
for (int i = 0; i < superStruct.getFieldCount(); i++) {
StructEntry structEntry = superStruct.getField(i);
if (structEntry.getOffset() > datatype.getOffset() ||
structEntry.getOffset() == datatype.getOffset() && structEntry != datatype &&
structEntry != modifiedStruct) {
structEntry.setOffset(structEntry.getOffset() + amount);
}
if (structEntry instanceof AbstractStruct)
adjustEntryOffsets((AbstractStruct)structEntry, modifiedStruct, datatype, amount);
}
}
private static void adjustSectionOffsets(AbstractStruct superStruct, AddRemovable datatype, int amount)
{
for (int i = 0; i < superStruct.getFieldCount(); i++) {
Object o = superStruct.getField(i);
if (o instanceof SectionOffset) {
SectionOffset sOffset = (SectionOffset)o;
if (sOffset.getValue() + superStruct.getExtraOffset() > datatype.getOffset()) {
sOffset.incValue(amount);
}
else if (sOffset.getValue() + superStruct.getExtraOffset() == datatype.getOffset()) {
if (amount > 0 &&
!(sOffset.getSection() == datatype.getClass() ||
Profile.getEngine() == Profile.Engine.IWD2 &&
superStruct instanceof CreResource)) {
sOffset.incValue(amount);
}
}
}
}
}
protected AbstractStruct()
{
}
protected AbstractStruct(ResourceEntry entry) throws Exception
{
this.entry = entry;
list = new ArrayList<StructEntry>();
name = entry.toString();
ByteBuffer bb = entry.getResourceBuffer();
endoffset = read(bb, 0);
if (this instanceof HasAddRemovable && !list.isEmpty()) {// Is this enough?
Collections.sort(list); // This way we can writeField out in the order in list - sorted by offset
fixHoles((ByteBuffer)bb.position(0));
initAddStructMaps();
}
}
protected AbstractStruct(AbstractStruct superStruct, String name, int startoffset, int listSize)
{
this.superStruct = superStruct;
this.name = name;
this.startoffset = startoffset;
list = new ArrayList<StructEntry>(listSize);
}
protected AbstractStruct(AbstractStruct superStruct, String name, ByteBuffer buffer, int startoffset)
throws Exception
{
this(superStruct, name, buffer, startoffset, 10);
}
protected AbstractStruct(AbstractStruct superStruct, String name, ByteBuffer buffer, int startoffset,
int listSize) throws Exception
{
this(superStruct, name, startoffset, listSize);
endoffset = read(buffer, startoffset);
if (this instanceof HasAddRemovable) {
if (!(this instanceof Actor)) { // Is this enough?
Collections.sort(list); // This way we can writeField out in the order in list - sorted by offset
}
initAddStructMaps();
}
}
// --------------------- Begin Interface Closeable ---------------------
// end - extends AbstractTableModel
// begin - implements Closeable
@Override
public void close() throws Exception
{
if (structChanged && viewer != null && this instanceof Resource && superStruct == null) {
Path outPath;
if (entry instanceof BIFFResourceEntry) {
outPath = FileManager.query(Profile.getRootFolders(), Profile.getOverrideFolderName(), entry.toString());
} else {
outPath = entry.getActualPath();
}
String options[] = {"Save changes", "Discard changes", "Cancel"};
int result = JOptionPane.showOptionDialog(viewer, "Save changes to " + outPath + '?', "Resource changed",
JOptionPane.YES_NO_CANCEL_OPTION,
JOptionPane.WARNING_MESSAGE, null, options, options[0]);
if (result == 0) {
ResourceFactory.saveResource((Resource)this, viewer.getTopLevelAncestor());
} else if (result != 1) {
throw new Exception("Save aborted");
}
}
if (viewer != null) {
viewer.close();
}
}
// --------------------- End Interface Closeable ---------------------
// --------------------- Begin Interface Comparable ---------------------
// begin - implements StructEntry
@Override
public int compareTo(StructEntry o)
{
return getOffset() - o.getOffset();
}
// --------------------- End Interface Comparable ---------------------
// --------------------- Begin Interface StructEntry ---------------------
@Override
public Object clone() throws CloneNotSupportedException
{
AbstractStruct newstruct = (AbstractStruct)super.clone();
newstruct.superStruct = null;
newstruct.list = new ArrayList<StructEntry>(list.size());
newstruct.viewer = null;
for (int i = 0; i < list.size(); i++)
newstruct.list.add((StructEntry)list.get(i).clone());
// for (Iterator i = newstruct.list.iterator(); i.hasNext();) {
// StructEntry sentry = (StructEntry)i.next();
// if (sentry.getOffset() <= 0)
// break;
// sentry.setOffset(sentry.getOffset() - newstruct.getOffset());
// }
newstruct.initAddStructMaps();
return newstruct;
}
@Override
public void copyNameAndOffset(StructEntry structEntry)
{
name = structEntry.getName();
setOffset(structEntry.getOffset());
}
@Override
public String getName()
{
return name;
}
@Override
public int getOffset()
{
return startoffset;
}
@Override
public StructEntry getParent()
{
return getSuperStruct();
}
@Override
public int getSize()
{
return endoffset - startoffset;
}
@Override
public ByteBuffer getDataBuffer()
{
ByteBuffer bb = ByteBuffer.allocate(getSize());
try (ByteBufferOutputStream bbos = new ByteBufferOutputStream(bb)) {
writeFlatList(bbos);
} catch (IOException e) {
e.printStackTrace();
}
bb.position(0);
return bb;
}
@Override
public List<StructEntry> getStructChain()
{
List<StructEntry> list = new Vector<StructEntry>();
StructEntry e = this;
while (e != null) {
list.add(0, e);
e = e.getParent();
if (list.contains(e)) {
// avoid infinite loops
break;
}
}
return list;
}
@Override
public void setOffset(int newoffset)
{
if (extraoffset != 0)
extraoffset += newoffset - startoffset;
int delta = getSize();
startoffset = newoffset;
endoffset = newoffset + delta;
}
@Override
public void setParent(StructEntry parent)
{
if (parent instanceof AbstractStruct) {
setSuperStruct((AbstractStruct)parent);
} else {
setSuperStruct(null);
}
}
// --------------------- End Interface StructEntry ---------------------
// --------------------- Begin Interface TableModel ---------------------
// start - extends AbstractTableModel
@Override
public int getRowCount()
{
return getFieldCount();
}
@Override
public int getColumnCount()
{
if (BrowserMenuBar.getInstance().showOffsets())
return 3;
return 2;
}
@Override
public Object getValueAt(int row, int column)
{
if (getField(row) instanceof StructEntry) {
StructEntry data = getField(row);
switch (column) {
case 0:
return data.getName();
case 1:
return data;
case 2:
return Integer.toHexString(data.getOffset()) + " h";
}
}
return "Unknown datatype";
}
// --------------------- End Interface TableModel ---------------------
// --------------------- Begin Interface Viewable ---------------------
// end - implements Closeable
// begin - implements Viewable
@Override
public JComponent makeViewer(ViewableContainer container)
{
if (viewer == null) {
viewer = new StructViewer(this, viewerComponents);
viewerInitialized(viewer);
}
return viewer;
}
// --------------------- End Interface Viewable ---------------------
// --------------------- Begin Interface Writeable ---------------------
// begin - implements Writeable
@Override
public void write(OutputStream os) throws IOException
{
Collections.sort(getList()); // This way we can writeField out in the order in list - sorted by offset
for (int i = 0, count = getFieldCount(); i < count; i++) {
getField(i).write(os);
}
}
// --------------------- End Interface Writeable ---------------------
@Override
public String getColumnName(int columnIndex)
{
if (columnIndex == 0)
return "Attribute";
if (columnIndex == 1)
return "Value";
return "Offset";
}
@Override
public boolean isCellEditable(int row, int col)
{
if (col == 1) {
Object o = getValueAt(row, col);
if (o instanceof InlineEditable && !(o instanceof Editable))
return true;
}
return false;
}
@Override
public void setValueAt(Object value, int row, int column)
{
Object o = getValueAt(row, column);
if (o instanceof InlineEditable) {
if (!((InlineEditable)o).update(value))
JOptionPane.showMessageDialog(viewer, "Error updating value", "Error", JOptionPane.ERROR_MESSAGE);
else {
fireTableCellUpdated(row, column);
setStructChanged(true);
}
}
}
@Override
public String toString()
{
StringBuffer sb = new StringBuffer(80);
for (int i = 0, count = getFieldCount(); i < count && sb.length() < 80; i++) { // < 80 to speed things up
StructEntry datatype = getField(i);
sb.append(datatype.getName()).append(": ").append(datatype.toString()).append(',');
}
return sb.toString();
}
public int addDatatype(AddRemovable addedEntry)
{
int index = 0;
// Find place to add
if (viewer != null && viewer.getSelectedEntry() != null &&
viewer.getSelectedEntry().getClass() == addedEntry.getClass()) {
index = viewer.getSelectedRow();
} else if (offsetmap.containsKey(addedEntry.getClass())) {
int offset = offsetmap.get(addedEntry.getClass()).getValue() + extraoffset;
int fieldCount = getFieldCount();
while (index < fieldCount && getField(index).getOffset() < offset) {
index++;
}
while (index < fieldCount && addedEntry.getClass() == (getField(index)).getClass()) {
index++;
}
if (index == 0) {
SectionOffset soffset = offsetmap.get(addedEntry.getClass());
if (soffset.getValue() == 0) {
index = fieldCount;
soffset.setValue(getSize());
}
else
throw new IllegalArgumentException(
"addDatatype: No suitable index found - " + getName() + " adding " + addedEntry.getName());
}
}
else {
index = getAddedPosition();
}
return addDatatype(addedEntry, index);
}
public int addDatatype(AddRemovable addedEntry, int index)
{
// Increase count
if (countmap.containsKey(addedEntry.getClass()))
countmap.get(addedEntry.getClass()).incValue(1);
// Set addedEntry offset
if (index > 0 && getField(index - 1).getClass() == addedEntry.getClass()) {
StructEntry prev = getField(index - 1);
addedEntry.setOffset(prev.getOffset() + prev.getSize());
}
else if (offsetmap.containsKey(addedEntry.getClass())) {
addedEntry.setOffset(offsetmap.get(addedEntry.getClass()).getValue() + extraoffset);
}
else if (index == 0 && getFieldCount() > 0) {
StructEntry next = getField(0);
addedEntry.setOffset(next.getOffset());
}
else {
setAddRemovableOffset(addedEntry);
for (int i = 0; i < getFieldCount(); i++) {
StructEntry structEntry = getField(i);
if (structEntry.getOffset() == addedEntry.getOffset()) {
index = i;
break;
}
}
}
if (addedEntry instanceof AbstractStruct) {
AbstractStruct addedStruct = (AbstractStruct)addedEntry;
addedStruct.realignStructOffsets();
addedStruct.superStruct = this;
}
AbstractStruct topStruct = this;
while (topStruct.superStruct != null) {
if (topStruct instanceof Resource) {
topStruct.endoffset += addedEntry.getSize();
adjustSectionOffsets(topStruct, addedEntry, addedEntry.getSize());
}
topStruct = topStruct.superStruct;
}
if (topStruct instanceof Resource)
topStruct.endoffset += addedEntry.getSize();
adjustEntryOffsets(topStruct, this, addedEntry, addedEntry.getSize());
adjustSectionOffsets(topStruct, addedEntry, addedEntry.getSize());
addField(addedEntry, index);
datatypeAdded(addedEntry);
if (superStruct != null)
superStruct.datatypeAddedInChild(this, addedEntry);
setStructChanged(true);
fireTableRowsInserted(index, index);
return index;
}
/**
* Adds the specified entry as a new field to the current structure.
* @param entry The new field to add.
* @return The added field.
*/
public StructEntry addField(StructEntry entry)
{
return addField(entry, getFieldCount());
}
/**
* Inserts the specified entry as a new field at the given position to the current structure.
* @param entry The new field to add.
* @param index The desired position of the new field.
* @return The inserted field.
*/
public StructEntry addField(StructEntry entry, int index)
{
if (entry != null) {
if (index < 0) index = 0; else if (index > list.size()) index = list.size();
entry.setParent(this);
list.add(index, entry);
}
return entry;
}
/** Adds list of entries to the AbstractStruct table after the specified index. */
public void addToList(int startIndex, List<StructEntry> toBeAdded)
{
if (toBeAdded != null) {
startIndex = Math.max(-1, Math.min(list.size() - 1, startIndex));
for (int i = 0; i < toBeAdded.size(); i++) {
addField(toBeAdded.get(i), startIndex+i+1);
}
}
}
/** Adds list of entries to the AbstractStruct table after the specified StructEntry object. */
public void addToList(StructEntry startFromEntry, List<StructEntry> toBeAdded)
{
if (toBeAdded != null) {
int startIndex = list.indexOf(startFromEntry) + 1;
for (int i = 0; i < toBeAdded.size(); i++) {
addField(toBeAdded.get(i), startIndex+i);
}
}
}
/**
* Removes all field entries from the list.
*/
public void clearFields()
{
for (int i = 0; i < list.size(); i++) {
StructEntry e = list.remove(i);
e.setParent(null);
}
}
/**
* Returns the lowest-level structure located at the specified offset.
* @param offset The offset of the structure to find.
* @return The matching structure, or null if not found.
*/
public StructEntry getAttribute(int offset)
{
return getAttribute(this, offset, StructEntry.class, true);
}
/**
* Returns the structure located at the specified offset.
* @param offset The offset of the structure to find.
* @param recursive If true, returns the lowest-level structure at the specified offset.
* If false, returns the first-level structure at the specified offset.
* @return The matching structure, or null if not found.
*/
public StructEntry getAttribute(int offset, boolean recursive)
{
return getAttribute(this, offset, StructEntry.class, recursive);
}
/**
* Returns the lowest-level structure of the given type, located at the specified offset.
* @param offset The offset of the structure to find.
* @param type The type of structure to find.
* @return The matching structure, or null if not found.
*/
public StructEntry getAttribute(int offset, Class<? extends StructEntry> type)
{
return getAttribute(this, offset, type, true);
}
/**
* Returns the structure of the given type, located at the specified offset.
* @param offset The offset of the structure to find.
* @param type The type of structure to find.
* @param recursive If true, returns the lowest-level structure at the specified offset.
* If false, returns the first-level structure at the specified offset.
* @return The matching structure, or null if not found.
*/
public StructEntry getAttribute(int offset, Class<? extends StructEntry> type, boolean recursive)
{
return getAttribute(this, offset, type, recursive);
}
/**
* Returns the lowest-level structure, matching the given field name.
* @param ename The field name of the structure.
* @return The matching structure, or null if not found.
*/
public StructEntry getAttribute(String ename)
{
return getAttribute(this, ename, true);
}
/**
* Returns the lowest-level structure, matching the given field name.
* @param ename The field name of the structure.
* @param recursive If true, returns the lowest-level structure matching the given name.
* If false, returns the first-level structure matching the given name.
* @return The matching structure, or null if not found.
*/
public StructEntry getAttribute(String ename, boolean recursive)
{
return getAttribute(this, ename, recursive);
}
private StructEntry getAttribute(AbstractStruct parent, int offset, Class<? extends StructEntry> type,
boolean recursive)
{
if (parent == null) parent = this;
if (type == null) type = StructEntry.class;
for (int i = 0, count = parent.getFieldCount(); i < count; i++) {
StructEntry structEntry = parent.getField(i);
if (offset >= structEntry.getOffset() &&
offset < structEntry.getOffset() + structEntry.getSize()) {
if (recursive && structEntry instanceof AbstractStruct) {
return getAttribute((AbstractStruct)structEntry, offset, type, recursive);
} else if (type.isInstance(structEntry)) {
return structEntry;
}
}
}
return null;
}
private StructEntry getAttribute(AbstractStruct parent, String name, boolean recursive)
{
if (name != null && !name.isEmpty()) {
if (parent == null) parent = this;
for (int i = 0, count = parent.getFieldCount(); i < count; i++) {
StructEntry structEntry = parent.getField(i);
if (structEntry.getName().equals(name)) {
return structEntry;
} else if (recursive && structEntry instanceof AbstractStruct) {
structEntry = getAttribute((AbstractStruct)structEntry, name, recursive);
if (structEntry != null) {
return structEntry;
}
}
}
}
return null;
}
public int getEndOffset()
{
return endoffset;
}
public int getExtraOffset()
{
return extraoffset;
}
public List<StructEntry> getList()
{
return list;
}
/** Returns the number of fields in the current structure. */
public int getFieldCount()
{
return list.size();
}
/**
* Returns the StructEntry object at the specified index.
* @param index The index of the desired StructEntry object.
* @return The StructEntry object, or null if not available.
*/
public StructEntry getField(int index)
{
try {
return list.get(index);
} catch (IndexOutOfBoundsException e) {
}
return null;
}
public List<StructEntry> getFlatList()
{
List<StructEntry> flatList = new ArrayList<StructEntry>(2 * getFieldCount());
addFlatList(flatList);
Collections.sort(flatList);
return flatList;
}
public int getIndexOf(StructEntry structEntry)
{
return list.indexOf(structEntry);
}
public ResourceEntry getResourceEntry()
{
return entry;
}
public AbstractStruct getSuperStruct()
{
return superStruct;
}
public AbstractStruct getSuperStruct(StructEntry structEntry)
{
for (int i = 0; i < list.size(); i++) {
Object o = list.get(i);
if (o == structEntry)
return this;
if (o instanceof AbstractStruct) {
AbstractStruct result = ((AbstractStruct)o).getSuperStruct(structEntry);
if (result != null)
return result;
}
}
return null;
}
/**
* Returns whether any parent of the current AbstractStruct object is an instance of
* the specified class type.
*/
public boolean isChildOf(Class<? extends AbstractStruct> struct)
{
if (struct != null) {
AbstractStruct parent = getSuperStruct();
while (parent != null) {
if (struct.isInstance(parent)) {
return true;
}
parent = parent.getSuperStruct();
}
}
return false;
}
// end - implements Viewable
public StructViewer getViewer()
{
return viewer;
}
public void realignStructOffsets()
{
int offset = startoffset;
for (int i = 0; i < list.size(); i++) {
StructEntry structEntry = list.get(i);
structEntry.setOffset(offset);
offset += structEntry.getSize();
if (structEntry instanceof AbstractStruct)
((AbstractStruct)structEntry).realignStructOffsets();
}
}
public List<AddRemovable> removeAllRemoveables()
{
List<AddRemovable> removed = new ArrayList<AddRemovable>();
for (int i = 0; i < list.size(); i++) {
StructEntry o = list.get(i);
if (o instanceof AddRemovable) {
removeDatatype((AddRemovable)o, false);
removed.add((AddRemovable)o);
i--;
}
}
return removed;
}
public void removeDatatype(AddRemovable removedEntry, boolean removeRecurse)
{
if (removeRecurse && removedEntry instanceof HasAddRemovable) { // Recusivly removeTableLine substructures first
AbstractStruct removedStruct = (AbstractStruct)removedEntry;
for (int i = 0; i < removedStruct.list.size(); i++) {
Object o = removedStruct.list.get(i);
if (o instanceof AddRemovable) {
removedStruct.removeDatatype((AddRemovable)o, removeRecurse);
i--;
}
}
}
int index = list.indexOf(removedEntry);
list.remove(index);
// decrease count
if (countmap != null && countmap.containsKey(removedEntry.getClass()))
countmap.get(removedEntry.getClass()).incValue(-1);
// decrease offsets
AbstractStruct topStruct = this;
while (topStruct.superStruct != null) {
if (topStruct instanceof Resource) {
topStruct.endoffset -= removedEntry.getSize();
adjustSectionOffsets(topStruct, removedEntry, -removedEntry.getSize());
}
topStruct = topStruct.superStruct;
}
if (topStruct instanceof Resource)
topStruct.endoffset -= removedEntry.getSize();
adjustEntryOffsets(topStruct, this, removedEntry, -removedEntry.getSize());
adjustSectionOffsets(topStruct, removedEntry, -removedEntry.getSize());
datatypeRemoved(removedEntry);
if (superStruct != null)
superStruct.datatypeRemovedInChild(this, removedEntry);
fireTableRowsDeleted(index, index);
setStructChanged(true);
}
/**
* Removes the specified entry from the current structure.
* @param entry The entry to remove.
* @return true if the current structure contained the given entry, false otherwise.
*/
public boolean removeField(StructEntry entry)
{
if (entry != null) {
if (list.remove(entry)) {
entry.setParent(null);
return true;
}
}
return false;
}
/**
* Removes the entry at the specified index.
* @param index The index of the entry to remove.
* @return The removed entry if found, null otherwise.
*/
public StructEntry removeField(int index)
{
if (index >= 0 && index < list.size()) {
StructEntry e = list.remove(index);
if (e != null) {
e.setParent(null);
}
return e;
}
return null;
}
public ByteBuffer removeFromList(StructEntry startFromEntry, int numBytes) throws IOException
{
int startindex = list.indexOf(startFromEntry) + 1;
int endindex = startindex;
int len = 0;
// getting total size
int maxLen = 0;
for (int i = startindex, cnt = list.size(); i < cnt && maxLen < numBytes; i++) {
maxLen += list.get(i).getSize();
}
// filling buffer
ByteBuffer bb = StreamUtils.getByteBuffer(maxLen);
try (ByteBufferOutputStream bbos = new ByteBufferOutputStream(bb)) {
while (len < maxLen) {
StructEntry e = list.get(endindex++);
len += e.getSize();
e.write(bbos);
}
}
// discard entries
for (int i = endindex - 1; i >= startindex; i--) {
list.remove(i);
}
bb.position(0);
return bb;
}
public void setListEntry(int index, StructEntry structEntry)
{
list.set(index, structEntry);
fireTableRowsUpdated(index, index);
}
public boolean hasStructChanged()
{
return structChanged;
}
public void setStructChanged(boolean changed)
{
structChanged = changed;
if (superStruct != null)
superStruct.setStructChanged(changed);
}
public String toMultiLineString()
{
StringBuffer sb = new StringBuffer(30 * list.size());
for (int i = 0; i < list.size(); i++) {
StructEntry datatype = list.get(i);
sb.append(datatype.getName()).append(": ").append(datatype.toString()).append('\n');
}
return sb.toString();
}
public void setExtraComponents(Collection<Component> list)
{
// List of components to be added to the bottom panel of the StructView component
viewerComponents = list;
}
/** Returns the SectionOffset entry linked to the specified StructEntry object if available. */
public SectionOffset getSectionOffset(Class<? extends StructEntry> cls)
{
return offsetmap.get(cls);
}
/** Returns the SectionCount entry linked to the specified StructEntry object if available. */
public SectionCount getSectionCount(Class<? extends StructEntry> cls)
{
return countmap.get(cls);
}
private void addFlatList(List<StructEntry> flatList)
{
for (int i = 0; i < list.size(); i++) {
StructEntry o = list.get(i);
if (o instanceof AbstractStruct)
((AbstractStruct)o).addFlatList(flatList);
else if (o instanceof AbstractCode)
((AbstractCode)o).addFlatList(flatList);
else
flatList.add(o);
}
}
public boolean hasViewTab()
{
return (viewer != null && viewer.hasViewTab());
}
public boolean isViewTabSelected()
{
return (viewer != null && viewer.isViewTabSelected());
}
public void selectViewTab()
{
if (viewer != null) {
viewer.selectViewTab();
}
}
public boolean isEditTabSelected()
{
return (viewer != null && viewer.isEditTabSelected());
}
public void selectEditTab()
{
if (viewer != null) {
viewer.selectEditTab();
}
}
public boolean hasRawTab()
{
return (viewer != null && viewer.hasRawTab());
}
public boolean isRawTabSelected()
{
return (viewer != null && viewer.isRawTabSelected());
}
public void selectRawTab()
{
if (viewer != null) {
viewer.selectRawTab();
}
}
// To be overriden by subclasses
protected void viewerInitialized(StructViewer viewer)
{
}
// To be overriden by subclasses
protected void datatypeAdded(AddRemovable datatype)
{
}
// To be overriden by subclasses
protected void datatypeAddedInChild(AbstractStruct child, AddRemovable datatype)
{
if (superStruct != null)
superStruct.datatypeAddedInChild(child, datatype);
}
// To be overriden by subclasses
protected void datatypeRemoved(AddRemovable datatype)
{
}
// To be overriden by subclasses
protected void datatypeRemovedInChild(AbstractStruct child, AddRemovable datatype)
{
if (superStruct != null)
superStruct.datatypeRemovedInChild(child, datatype);
}
private void fixHoles(ByteBuffer buffer)
{
int offset = startoffset;
List<StructEntry> flatList = getFlatList();
for (int i = 0; i < flatList.size(); i++) {
StructEntry se = flatList.get(i);
int delta = se.getOffset() - offset;
if (se.getSize() > 0 && delta > 0) {
Unknown hole = new Unknown(buffer, offset, delta, COMMON_UNUSED_BYTES);
list.add(hole);
flatList.add(i, hole);
System.out.println("Hole: " + name + " off: " + Integer.toHexString(offset) + "h len: " + delta);
i++;
}
// Using max() as shared data regions may confuse the hole detection algorithm
offset = Math.max(offset, se.getOffset() + se.getSize());
}
if (endoffset < buffer.limit()) { // Does this break anything?
list.add(new Unknown(buffer, endoffset, buffer.limit() - endoffset, COMMON_UNUSED_BYTES));
System.out.println("Hole: " + name + " off: " + Integer.toHexString(offset) + "h len: " +
(buffer.limit() - endoffset));
endoffset = buffer.limit();
}
}
// To be overriden by subclasses
protected int getAddedPosition()
{
return list.size(); // Default: Add at end
}
private void initAddStructMaps()
{
countmap = new HashMap<Class<? extends StructEntry>, SectionCount>();
offsetmap = new HashMap<Class<? extends StructEntry>, SectionOffset>();
for (int i = 0; i < list.size(); i++) {
Object o = list.get(i);
if (o instanceof SectionOffset) {
SectionOffset so = (SectionOffset)o;
if (so.getSection() != null) {
offsetmap.put(so.getSection(), so);
}
}
else if (o instanceof SectionCount)
countmap.put(((SectionCount)o).getSection(), (SectionCount)o);
}
}
// To be overriden by subclasses
protected void setAddRemovableOffset(AddRemovable datatype)
{
}
// end - implements StructEntry
protected void setExtraOffset(int offset)
{
extraoffset = offset;
}
protected void setStartOffset(int offset)
{
startoffset = offset;
}
// end - implements Writeable
protected void writeFlatList(OutputStream os) throws IOException
{
List<StructEntry> flatList = getFlatList();
for (int i = 0; i < flatList.size(); i++)
flatList.get(i).write(os);
}
/** Assign a new list of fields. Clears current list if argument is null. */
protected void setList(List<StructEntry> newList)
{
if (newList != null) {
list = newList;
} else {
list.clear();
}
}
protected void setSuperStruct(AbstractStruct struct)
{
this.superStruct = struct;
}
}