/*
* Copyright (c) 2005 (Mike) Maurice Kienenberger (mkienenb@gmail.com)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.gamenet.application.mm8leveleditor.data.mm6;
import java.util.ArrayList;
import java.util.List;
import org.gamenet.application.mm8leveleditor.data.GameVersion;
import org.gamenet.swing.controls.ComparativeTableControl;
import org.gamenet.swing.controls.Vertex3DValueHolder;
import org.gamenet.util.ByteConversions;
public class Sprite implements Vertex3DValueHolder
{
private static final short DECLIST_ID_OFFSET = 0; // two bytes
private static final short AI_ATTRIBUTE_MARKERS_OFFSET = 2; // two bytes
// 0, 0, 0 is center
// short coords from -0x58 (-88) to 0x58 (88) are reachable,
private static final int EAST_WEST_OFFSET = 4; // four signed bytes, neg is west
private static final int NORTH_SOUTH_OFFSET = 8; // four signed bytes, neg is south
private static final int HEIGHT_OFFSET = 12; // four signed bytes
private static final int FACING_OFFSET = 16; // four signed bytes
private static final int EVENT1_OFFSET = 20; // two bytes
private static final int EVENT2_OFFSET = 22; // two bytes
private static final int VARIABLE1_OFFSET = 24; // two bytes
private static final int VARIABLE2_OFFSET = 26; // two bytes -- sound counter?
private static final int SPRITE_OBJECT_DATA_RECORD_LENGTH_MM6 = 28;
private static final int SPECIAL_TRIGGER_OFFSET = 28; // two bytes // mm7 & 8
private static final int PADDING_OFFSET = 30; // two bytes // mm7 & 8
private static final int SPRITE_OBJECT_DATA_RECORD_LENGTH_MM7 = 32;
private static final int NAME_MAX_LENGTH = 32;
// following that is
// xfe570 - 1041776
// object maps
// A = 4 byte record count
// A * 28-byte records
// A * 32-byte sprite resource names
private int gameVersion = GameVersion.MM6;
private long spriteDataOffset = 0;
private long spriteNameOffset = 0;
private byte spriteData[] = null;
private String spriteName = null;
public Sprite(int gameVersion)
{
super();
this.gameVersion = gameVersion;
}
public Sprite(int gameVersion, String spriteName, int x, int y, int z, int eventNumber)
{
this(gameVersion);
if (GameVersion.MM6 == gameVersion)
this.spriteData = new byte[SPRITE_OBJECT_DATA_RECORD_LENGTH_MM6];
else this.spriteData = new byte[SPRITE_OBJECT_DATA_RECORD_LENGTH_MM7];
setSpriteName(spriteName);
setX(x);
setY(y);
setZ(z);
setEventNumber(eventNumber);
}
public int initialize(byte dataSrc[], int offset)
{
this.spriteDataOffset = offset;
if (GameVersion.MM6 == gameVersion)
this.spriteData = new byte[SPRITE_OBJECT_DATA_RECORD_LENGTH_MM6];
else this.spriteData = new byte[SPRITE_OBJECT_DATA_RECORD_LENGTH_MM7];
System.arraycopy(dataSrc, offset, this.spriteData, 0, this.spriteData.length);
offset += this.spriteData.length;
return offset;
}
/**
* @param data
* @param offset
* @param spriteArray
* @return
*/
public static int populateObjects(int gameVersion, byte[] data, int offset, List spriteList)
{
int spriteCount = ByteConversions.getIntegerInByteArrayAtPosition(data, offset);
offset += 4;
for (int spriteIndex = 0; spriteIndex < spriteCount; ++spriteIndex)
{
Sprite sprite = new Sprite(gameVersion);
offset = sprite.initialize(data, offset);
spriteList.add(sprite);
}
for (int spriteIndex = 0; spriteIndex < spriteList.size(); ++spriteIndex)
{
Sprite sprite = (Sprite)spriteList.get(spriteIndex);
sprite.spriteNameOffset = offset;
sprite.spriteName = ByteConversions.getZeroTerminatedStringInByteArrayAtPositionMaxLength(data, offset, NAME_MAX_LENGTH);
offset += NAME_MAX_LENGTH;
}
return offset;
}
/**
* @param newData
* @param offset
* @param spriteArray
* @return offset
*/
public static int updateData(byte[] newData, int offset, List spriteList)
{
ByteConversions.setIntegerInByteArrayAtPosition(spriteList.size(), newData, offset);
offset += 4;
for (int spriteIndex = 0; spriteIndex < spriteList.size(); ++spriteIndex)
{
Sprite sprite = (Sprite)spriteList.get(spriteIndex);
System.arraycopy(sprite.getSpriteData(), 0, newData, offset, sprite.getObjectRecordSize());
offset += sprite.getObjectRecordSize();
}
for (int spriteIndex = 0; spriteIndex < spriteList.size(); ++spriteIndex)
{
Sprite sprite = (Sprite)spriteList.get(spriteIndex);
ByteConversions.setZeroTerminatedStringInByteArrayAtPositionMaxLength(sprite.getSpriteName(), newData, offset, NAME_MAX_LENGTH);
offset += NAME_MAX_LENGTH;
}
return offset;
}
public byte[] getSpriteData()
{
return this.spriteData;
}
public long getSpriteDataOffset()
{
return spriteDataOffset;
}
public String getSpriteName()
{
return this.spriteName;
}
public void setSpriteName(String spriteName)
{
this.spriteName = spriteName;
}
public long getSpriteNameOffset()
{
return spriteNameOffset;
}
public int getX()
{
return ByteConversions.getIntegerInByteArrayAtPosition(getSpriteData(), EAST_WEST_OFFSET);
}
public void setX(int value)
{
ByteConversions.setIntegerInByteArrayAtPosition(value, getSpriteData(), EAST_WEST_OFFSET);
}
public int getY()
{
return ByteConversions.getIntegerInByteArrayAtPosition(getSpriteData(), NORTH_SOUTH_OFFSET);
}
public void setY(int value)
{
ByteConversions.setIntegerInByteArrayAtPosition(value, getSpriteData(), NORTH_SOUTH_OFFSET);
}
public int getZ()
{
return ByteConversions.getIntegerInByteArrayAtPosition(getSpriteData(), HEIGHT_OFFSET);
}
public void setZ(int value)
{
ByteConversions.setIntegerInByteArrayAtPosition(value, getSpriteData(), HEIGHT_OFFSET);
}
public int getEventNumber()
{
return ByteConversions.getShortInByteArrayAtPosition(getSpriteData(), EVENT2_OFFSET);
}
public void setEventNumber(int value)
{
ByteConversions.setShortInByteArrayAtPosition((short)value, getSpriteData(), EVENT2_OFFSET);
}
public static int getRecordSize(int gameVersion)
{
if (GameVersion.MM6 == gameVersion)
return SPRITE_OBJECT_DATA_RECORD_LENGTH_MM6 + NAME_MAX_LENGTH;
else return SPRITE_OBJECT_DATA_RECORD_LENGTH_MM7 + NAME_MAX_LENGTH;
}
public int getObjectRecordSize()
{
if (GameVersion.MM6 == gameVersion)
return SPRITE_OBJECT_DATA_RECORD_LENGTH_MM6;
else return SPRITE_OBJECT_DATA_RECORD_LENGTH_MM7;
}
// Unknown things to decode
public static List getOffsetList(int gameVersion)
{
List offsetList = new ArrayList();
offsetList.add(new ComparativeTableControl.OffsetData(DECLIST_ID_OFFSET, 2, ComparativeTableControl.REPRESENTATION_SHORT_DEC, "DecList ID #"));
offsetList.add(new ComparativeTableControl.OffsetData(AI_ATTRIBUTE_MARKERS_OFFSET, 2, ComparativeTableControl.REPRESENTATION_SHORT_DEC, "attributes"));
offsetList.add(new ComparativeTableControl.OffsetData(EAST_WEST_OFFSET, 4, ComparativeTableControl.REPRESENTATION_INT_DEC, "X"));
offsetList.add(new ComparativeTableControl.OffsetData(NORTH_SOUTH_OFFSET, 4, ComparativeTableControl.REPRESENTATION_INT_DEC, "Y"));
offsetList.add(new ComparativeTableControl.OffsetData(HEIGHT_OFFSET, 4, ComparativeTableControl.REPRESENTATION_INT_DEC, "Z"));
offsetList.add(new ComparativeTableControl.OffsetData(FACING_OFFSET, 4, ComparativeTableControl.REPRESENTATION_INT_DEC, "facing"));
offsetList.add(new ComparativeTableControl.OffsetData(EVENT1_OFFSET, 2, ComparativeTableControl.REPRESENTATION_SHORT_DEC, "Event1 #"));
offsetList.add(new ComparativeTableControl.OffsetData(EVENT2_OFFSET, 2, ComparativeTableControl.REPRESENTATION_SHORT_DEC, "Event2 #"));
offsetList.add(new ComparativeTableControl.OffsetData(VARIABLE1_OFFSET, 2, ComparativeTableControl.REPRESENTATION_SHORT_DEC, "variable1"));
offsetList.add(new ComparativeTableControl.OffsetData(VARIABLE2_OFFSET, 2, ComparativeTableControl.REPRESENTATION_SHORT_DEC, "variable2"));
if (GameVersion.MM6 != gameVersion)
{
offsetList.add(new ComparativeTableControl.OffsetData(SPECIAL_TRIGGER_OFFSET, 2, ComparativeTableControl.REPRESENTATION_SHORT_DEC, "special event trigger"));
offsetList.add(new ComparativeTableControl.OffsetData(PADDING_OFFSET, 2, ComparativeTableControl.REPRESENTATION_SHORT_DEC, "unused padding"));
}
return offsetList;
}
public static ComparativeTableControl.DataSource getComparativeDataSource(final List spriteList)
{
return new ComparativeTableControl.DataSource()
{
public int getRowCount()
{
return spriteList.size();
}
public byte[] getData(int dataRow)
{
Sprite sprite = (Sprite)spriteList.get(dataRow);
return sprite.getSpriteData();
}
public int getAdjustedDataRowIndex(int dataRow)
{
return dataRow;
}
public String getIdentifier(int dataRow)
{
Sprite sprite = (Sprite)spriteList.get(dataRow);
return sprite.getSpriteName();
}
public int getOffset(int dataRow)
{
return 0;
}
};
}
}