package tk.amberide.engine.data.map.codec;
import tk.amberide.Amber;
import tk.amberide.engine.data.map.*;
import tk.amberide.ide.data.res.Tileset;
import tk.amberide.ide.data.res.Tileset.TileSprite;
import tk.amberide.engine.data.io.ByteStream;
import tk.amberide.engine.data.map.exc.InvalidMapException;
import static tk.amberide.engine.data.map.codec.V1.Tag.*;
import tk.amberide.ide.data.res.Resource;
import tk.amberide.engine.data.sparse.SparseMatrix;
import tk.amberide.engine.data.sparse.SparseVector;
import tk.amberide.engine.gl.model.obj.WavefrontObject;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.HashMap;
/**
* @author Tudor
*/
public class V1 extends Codec {
public interface Tag {
short MAP_MAGIC = 0x1337;
int MAP_VERSION = 0x01;
byte TAG_TILESHEET = 0x01;
byte TAG_AUDIO = 0x02;
byte TAG_MODEL = 0x03;
byte MATRIX_SPARSE = 0x01;
byte MATRIX_DENSE = 0x02;
}
@Override
public LevelMap loadMap(DataInputStream buffer) throws IOException {
if (buffer.readShort() != MAP_MAGIC) {
throw new InvalidMapException("magic number != " + MAP_MAGIC);
}
int v;
if ((v = buffer.readInt()) != MAP_VERSION)
throw new InvalidMapException("wrong map version " + v);
LevelMap map = new LevelMap(buffer.readShort(), buffer.readShort());
HashMap<Short, Tileset.TileSprite> sprites = new HashMap<Short, Tileset.TileSprite>();
HashMap<Short, WavefrontObject> models = new HashMap<Short, WavefrontObject>();
int constantsCount = buffer.readShort();
for (int i = 0; i != constantsCount; i++) {
byte tag;
switch (tag = buffer.readByte()) {
case TAG_TILESHEET:
sprites.put(buffer.readShort(), TileSprite.byId(buffer.readUTF()));
break;
case TAG_MODEL:
short id = buffer.readShort();
String name = buffer.readUTF();
for (Resource<WavefrontObject> e : Amber.getResourceManager().getModels()) {
if (e.getName().equals(name)) {
models.put(id, e.get());
}
}
break;
case TAG_AUDIO:
throw new InvalidMapException("tag not implemented yet");
default:
throw new InvalidMapException("invalid tag " + tag + " at index " + i);
}
}
short layersCount = buffer.readShort();
for (int i = 0; i != layersCount; i++) {
String name = buffer.readUTF();
Layer level = new Layer(name, map);
int layerCnt = buffer.readInt();
if (layerCnt == 0) {
map.addLayer(level);
continue;
}
for (int s = 0; s != layerCnt; s++) {
byte matrix = buffer.readByte();
int z = buffer.readInt();
switch (matrix) {
case MATRIX_SPARSE:
int full = buffer.readInt();
for (int f = 0; f != full; f++) {
int x = buffer.readShort();
int y = buffer.readShort();
short id = buffer.readShort();
if (id > 0) {
Tile t = new Tile(sprites.get(id), Direction.values()[buffer.readByte()], Angle.values()[buffer.readByte()], TileType.values()[buffer.readByte()]);
level.setTile(x, y, z, t);
}
}
break;
case MATRIX_DENSE:
for (int x = 0; x != map.getWidth(); x++) {
for (int y = 0; y != map.getLength(); y++) {
short id = buffer.readShort();
if (id > 0) {
Tile t = new Tile(sprites.get(id), Direction.values()[buffer.readByte()], Angle.values()[buffer.readByte()], TileType.values()[buffer.readByte()]);
level.setTile(x, y, z, t);
}
}
}
break;
default:
System.err.println("invalid matrix type " + matrix + " at index " + s);
break;
}
}
int modelCount = buffer.readInt();
for (int m = 0; m != modelCount; m++) {
level.setModel(buffer.readShort(),
buffer.readShort(),
buffer.readShort(),
new TileModel(models.get(buffer.readShort())));
}
map.addLayer(level);
}
return map;
}
@Override
public void compileMap(LevelMap map, DataOutputStream out) throws IOException {
ByteStream mapBuffer = ByteStream.writeStream();
mapBuffer.writeShort(MAP_MAGIC);
mapBuffer.writeInt(MAP_VERSION);
mapBuffer.writeShort(map.getWidth());
mapBuffer.writeShort(map.getLength());
HashMap<Tileset.TileSprite, Integer> recordedTiles = new HashMap<Tileset.TileSprite, Integer>();
HashMap<WavefrontObject, Integer> recordedModels = new HashMap<WavefrontObject, Integer>();
int constantsCount = 0;
ByteStream constantBuffer = ByteStream.writeStream();
ByteStream mapLayerBuffer = ByteStream.writeStream();
mapLayerBuffer.writeShort(map.getLayers().size());
for (Layer layer : map.getLayers()) {
ByteStream layerBuffer = ByteStream.writeStream();
int totFull = 0;
SparseVector.SparseVectorIterator tileIterator = layer.tileMatrix().iterator();
while (tileIterator.hasNext()) {
SparseMatrix<Tile> matrix = (SparseMatrix<Tile>) tileIterator.next();
// This is a two step process. First, we calculate the amount of non-null
// tiles in the current plane.
// We can also use this phase to compile tile IDs
int full = 0;
for (int x = 0; x != map.getWidth(); x++) {
for (int y = 0; y != map.getLength(); y++) {
Tile t = matrix.get(x, y);
if (t != null) {
full++;
Tileset.TileSprite spr = t.getSprite();
// This is not a recorded tile, so we should save it.
if (!recordedTiles.containsKey(spr)) {
constantBuffer.writeByte(TAG_TILESHEET).writeShort(recordedTiles.size() + 1).writeUTF(spr.getId());
recordedTiles.put(spr, recordedTiles.size() + 1);
constantsCount++;
}
}
}
}
if (full == 0) {
// There are no tiles in this plane, it can be ignored
continue;
}
totFull++;
int z = tileIterator.realIndex();
// Now we fetch the total amount of cells on the current plane.
int totalTiles = map.getWidth() * map.getLength();
// To store in a sparse format, we need 3 pieces of information:
// * tile id
// * x coordinate
// * y coordinate
// To store in a solid block, we need only the id, but we need to fill
// the entire block. Assuming tile id, x-coord and y-coord
// are of the same datatype, we can check whether it is worthwhile to save
// in a sparce format with a simple evaluation, below.
boolean sparse = totalTiles > full * 3;
// We need to identify this plane as being sparse or block based.
// We also need to write the plane z-coord, in case a layer was
// skipped, or the format is not in sequential order.
layerBuffer.writeByte(sparse ? MATRIX_SPARSE : MATRIX_DENSE);
layerBuffer.writeInt(z);
if (sparse) {
layerBuffer.writeInt(full);
for (int x = 0; x != map.getWidth(); x++) {
for (int y = 0; y != map.getLength(); y++) {
Tile t = layer.getTile(x, y, z);
int id = t != null ? recordedTiles.get(t.getSprite()) : 0;
if (id > 0) {
layerBuffer.writeShort(x)
.writeShort(y)
.writeShort(id);
layerBuffer.writeByte(t.getDirection().ordinal());
layerBuffer.writeByte(t.getAngle().ordinal());
layerBuffer.writeByte(t.getType().ordinal());
}
}
}
} else {
// Iterate over every row and save each tile id
// sequentially.
for (int x = 0; x != map.getWidth(); x++) {
for (int y = 0; y != map.getLength(); y++) {
Tile t = layer.getTile(x, y, z);
int id = t != null ? recordedTiles.get(t.getSprite()) : 0;
layerBuffer.writeShort(id);
if (id > 0) {
layerBuffer.writeByte(t.getDirection().ordinal());
layerBuffer.writeByte(t.getAngle().ordinal());
layerBuffer.writeByte(t.getType().ordinal());
}
}
}
}
}
int nz = 0;
for (SparseMatrix<TileModel> matrix : layer.modelMatrix()) {
for (TileModel u : matrix) {
if (u != null) {
nz++;
}
}
}
layerBuffer.writeInt(nz);
SparseVector.SparseVectorIterator modelIterator = layer.modelMatrix().iterator();
HashMap<WavefrontObject, String> modelIds = new HashMap<WavefrontObject, String>();
for (Resource<WavefrontObject> models : Amber.getResourceManager().getModels()) {
modelIds.put(models.get(), models.getName());
}
while (modelIterator.hasNext()) {
SparseMatrix.SparseMatrixIterator matrixIterator = ((SparseMatrix<WavefrontObject>) modelIterator.next()).iterator();
while (matrixIterator.hasNext()) {
TileModel tile = (TileModel) matrixIterator.next();
if (tile != null) {
WavefrontObject obj = tile.getModel();
if (!recordedModels.containsKey(obj)) {
if (modelIds.containsKey(obj)) {
constantBuffer.writeByte(TAG_MODEL).writeShort(recordedModels.size() + 1).writeUTF(modelIds.get(obj));
recordedModels.put(obj, recordedModels.size() + 1);
constantsCount++;
}
}
layerBuffer.writeShort(matrixIterator.realX())
.writeShort(matrixIterator.realY())
.writeShort(modelIterator.realIndex()) // z
.writeShort(recordedModels.get(obj));
}
}
}
mapLayerBuffer.writeUTF(layer.getName());
mapLayerBuffer.writeInt(totFull);
System.out.println(totFull);
mapLayerBuffer.writeBytes(layerBuffer.getBuffer());
}
mapBuffer.writeShort(constantsCount);
mapBuffer.writeBytes(constantBuffer.getBuffer());
mapBuffer.writeBytes(mapLayerBuffer.getBuffer());
out.write(mapBuffer.getBuffer());
}
}