diff --git a/src/org/apollo/fs/decoder/GameObjectDecoder.java b/src/org/apollo/fs/decoder/GameObjectDecoder.java index bff8fc82..13f95f9a 100644 --- a/src/org/apollo/fs/decoder/GameObjectDecoder.java +++ b/src/org/apollo/fs/decoder/GameObjectDecoder.java @@ -3,28 +3,55 @@ package org.apollo.fs.decoder; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; -import java.util.Collection; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import org.apollo.fs.IndexedFileSystem; -import org.apollo.fs.archive.Archive; +import org.apollo.fs.decoder.MapFileDecoder.MapDefinition; import org.apollo.game.model.Position; +import org.apollo.game.model.World; +import org.apollo.game.model.area.Sector; +import org.apollo.game.model.area.SectorRepository; +import org.apollo.game.model.area.collision.CollisionMatrix; import org.apollo.game.model.entity.GameObject; import org.apollo.util.BufferUtil; import org.apollo.util.CompressionUtil; +import com.google.common.collect.Iterables; + /** - * Decodes map object data from the {@code map_index.dat} file into {@link GameObject}s. + * Parses static object definitions, which include map tiles and landscapes. * - * @author Chris Fletcher + * @author Ryley */ public final class GameObjectDecoder { + /** + * A bit flag which denotes that a specified Position is blocked. + */ + private static final int FLAG_BLOCKED = 1; + + /** + * A bit flag which denotes that a specified Position is a bridge. + */ + private static final int FLAG_BRIDGE = 1; + + /** + * The sector repository. + */ + private static final SectorRepository sectors = World.getWorld().getSectorRepository(); + /** * The {@link IndexedFileSystem}. */ private final IndexedFileSystem fs; + /** + * A {@link List} of decoded game objects. + */ + private final List objects = new ArrayList<>(); + /** * Creates the decoder. * @@ -41,80 +68,87 @@ public final class GameObjectDecoder { * @throws IOException If an I/O error occurs. */ public GameObject[] decode() throws IOException { - Archive versionList = Archive.decode(fs.getFile(0, 5)); - ByteBuffer buffer = versionList.getEntry("map_index").getBuffer(); + Map definitions = MapFileDecoder.parse(fs); - int indices = buffer.remaining() / 7; - int[] areas = new int[indices]; - int[] landscapes = new int[indices]; + for (Entry entry : definitions.entrySet()) { + MapDefinition def = entry.getValue(); - for (int i = 0; i < indices; i++) { - areas[i] = buffer.getShort() & 0xFFFF; + int hash = def.getHash(); + int x = (hash >> 8 & 0xFF) * 64; + int y = (hash & 0xFF) * 64; - @SuppressWarnings("unused") - int mapFile = buffer.getShort() & 0xFFFF; + ByteBuffer gameObjectData = fs.getFile(4, def.getObjectFile()); + ByteBuffer gameObjectBuffer = ByteBuffer.wrap(CompressionUtil.degzip(gameObjectData)); + parseGameObject(gameObjectBuffer, x, y); - landscapes[i] = buffer.getShort() & 0xFFFF; - - @SuppressWarnings("unused") - boolean members = (buffer.get() & 0xFF) == 1; + ByteBuffer terrainData = fs.getFile(4, def.getTerrainFile()); + ByteBuffer terrainBuffer = ByteBuffer.wrap(CompressionUtil.degzip(terrainData)); + parseTerrain(terrainBuffer, x, y); } - List objects = new ArrayList<>(); - - for (int i = 0; i < indices; i++) { - ByteBuffer compressed = fs.getFile(4, landscapes[i]); - ByteBuffer uncompressed = ByteBuffer.wrap(CompressionUtil.degzip(compressed)); - - Collection areaObjects = parseArea(areas[i], uncompressed); - objects.addAll(areaObjects); - } - - return objects.toArray(new GameObject[objects.size()]); + return Iterables.toArray(objects, GameObject.class); } - /** - * Parses a single area from the specified buffer. - * - * @param area The identifier of that area. - * @param buffer The buffer which holds the area's data. - * @return A collection of all parsed objects. - */ - private static Collection parseArea(int area, ByteBuffer buffer) { - List objects = new ArrayList<>(); + private void parseGameObject(ByteBuffer buffer, int x, int y) { + for (int deltaId, id = -1; (deltaId = BufferUtil.readSmart(buffer)) != 0;) { + id += deltaId; - int x = (area >> 8 & 0xFF) * 64; - int y = (area & 0xFF) * 64; + for (int deltaPos, hash = 0; (deltaPos = BufferUtil.readSmart(buffer)) != 0;) { + hash += deltaPos - 1; - int id = -1; - int idOffset; + int localX = hash >> 6 & 0x3F; + int localY = hash & 0x3F; + int height = hash >> 12 & 0x3; - while ((idOffset = BufferUtil.readSmart(buffer)) != 0) { - id += idOffset; + int attributes = buffer.get() & 0xFF; + int type = attributes >> 2; + int orientation = attributes & 0x3; + Position position = new Position(x + localX, y + localY, height); - int position = 0; - int positionOffset; + gameObjectDecoded(id, orientation, type, position); + } + } + } - while ((positionOffset = BufferUtil.readSmart(buffer)) != 0) { - position += positionOffset - 1; + private void gameObjectDecoded(int id, int orientation, int type, Position position) { - int localX = position >> 6 & 0x3F; - int localY = position & 0x3F; - int height = position >> 12; + } - int info = buffer.get() & 0xFF; - int type = info >> 2; - int rotation = info & 3; - if (type >= 0 && type <= 3 || type >= 9 && type <= 21) { - Position pos = new Position(x + localX, y + localY, height); + private void parseTerrain(ByteBuffer buffer, int x, int y) { + for (int height = 0; height < 4; height++) { + for (int localX = 0; localX < 64; localX++) { + for (int localY = 0; localY < 64; localY++) { + Position position = new Position(x + localX, y + localY, height); - GameObject object = new GameObject(id, pos, type, rotation); - objects.add(object); + int flags = 0; + for (;;) { + int attributeId = buffer.get() & 0xFF; + if (attributeId == 0) { + terrainDecoded(flags, position); + break; + } else if (attributeId == 1) { + buffer.get(); + terrainDecoded(flags, position); + break; + } else if (attributeId <= 49) { + buffer.get(); + } else if (attributeId <= 81) { + flags = attributeId - 49; + } + } } } } + } - return objects; + private void terrainDecoded(int flags, Position position) { + Sector sector = sectors.fromPosition(position); + + if ((flags & FLAG_BLOCKED) != 0) { + } + + if((flags & FLAG_BRIDGE) != 0) { + } } } \ No newline at end of file diff --git a/src/org/apollo/fs/decoder/MapFileDecoder.java b/src/org/apollo/fs/decoder/MapFileDecoder.java new file mode 100644 index 00000000..28a2498f --- /dev/null +++ b/src/org/apollo/fs/decoder/MapFileDecoder.java @@ -0,0 +1,118 @@ +package org.apollo.fs.decoder; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.Map; + +import org.apollo.fs.IndexedFileSystem; +import org.apollo.fs.archive.Archive; +import org.apollo.fs.archive.ArchiveEntry; + +/** + * Parses {@link MapDefinition map definitions} from the {@link IndexedFileSystem}. + * + * @author Ryley + */ +public final class MapFileDecoder { + + /** + * Parses {@link MapDefinition}s from the specified {@link IndexedFileSystem}. + * + * @param fs The file system. + * @return A {@link Map} of parsed map definitions. + * @throws IOException If some I/O error occurs. + */ + protected static Map parse(IndexedFileSystem fs) throws IOException { + Archive archive = Archive.decode(fs.getFile(0, 5)); + ArchiveEntry entry = archive.getEntry("map_index"); + ByteBuffer buffer = entry.getBuffer(); + Map defs = new HashMap<>(); + + int count = buffer.capacity() / 7; + for (int i = 0; i < count; i++) { + int hash = buffer.getShort() & 0xFFFF; + int terrainFile = buffer.getShort() & 0xFFFF; + int objectFile = buffer.getShort() & 0xFFFF; + boolean preload = buffer.get() == 1; + + defs.put(hash, new MapDefinition(hash, terrainFile, objectFile, preload)); + } + + return defs; + } + + /** + * Represents a single map definition. + * + * @author Ryley + */ + public static final class MapDefinition { + + /** + * The hash of the region coordinates. + */ + private final int hash; + + /** + * The terrain file id. + */ + private final int terrainFile; + + /** + * The object file id. + */ + private final int objectFile; + + /** + * Whether or not this map is preloaded. + */ + private final boolean preload; + + /** + * Constructs a new {@link MapDefinition} with the specified hash, terrain file id, object file id and preload + * state. + * + * @param hash The hash of the region coordinates. + * @param terrainFile The terrain file id. + * @param objectFile The object file id. + * @param preload Whether or not this map is preloaded. + */ + public MapDefinition(int hash, int terrainFile, int objectFile, boolean preload) { + this.hash = hash; + this.terrainFile = terrainFile; + this.objectFile = objectFile; + this.preload = preload; + } + + /** + * Returns the coordinate hash. + */ + public int getHash() { + return hash; + } + + /** + * Returns the terrain file id. + */ + public int getTerrainFile() { + return terrainFile; + } + + /** + * Returns the object file id. + */ + public int getObjectFile() { + return objectFile; + } + + /** + * Returns whether or not this map is preloaded. + */ + public boolean isPreload() { + return preload; + } + + } + +} \ No newline at end of file