private bool LoadTMXSections(BinaryReader reader, ref TMXMapDef map) { while (true) { int section = reader.ReadByte(); if (section == RetroBlit_TMX_SECTION_TILE_LAYER) { if (!LoadTMXTileLayerDef(reader, ref map)) { return(false); } } else if (section == RetroBlit_TMX_SECTION_OBJECTGROUP) { if (!LoadTMXObjectGroupDef(reader, ref map)) { return(false); } } else if (section == RetroBlit_TSX_PROPERTIES) { if (!LoadTSXProperties(reader, ref map)) { return(false); } } else if (section == RetroBlit_TMX_SECTION_END) { break; } } return(true); }
private bool LoadTSXProperties(BinaryReader reader, ref TMXMapDef map) { int tsxLoops = 0; var allProps = new List <Dictionary <int, TMXProperties> >(); int prevTsxIndex = -1; while (true) { var tsxIndex = reader.ReadInt32(); if (tsxIndex == -1) { // All done break; } if (tsxIndex != prevTsxIndex + 1) { Debug.LogError("TMX binary files had non-consequitive TSX property sets, TMX import is likely corrupt, please try reimporting " + map.realPathName); return(false); } var propsSet = new Dictionary <int, TMXProperties>(); while (true) { var tid = reader.ReadInt32(); if (tid == -1) { // All done this tsx break; } var props = new TMXProperties(); LoadProperties(reader, props); propsSet.Add(tid, props); } allProps.Add(propsSet); prevTsxIndex = tsxIndex; // Break out if we loop for too long, TSX data could be corrupt tsxLoops++; if (tsxLoops > 256) { Debug.Log("TSX properties data is invalid, please try to reimport " + map.realPathName); return(false); } } map.allTileProperties = allProps; return(true); }
private bool LoadTMXTileLayerDef(BinaryReader reader, ref TMXMapDef map) { TMXLayerDef layerDef = new TMXLayerDef(); string name = reader.ReadString(); int layerWidth = reader.ReadInt32(); int layerHeight = reader.ReadInt32(); int layerOffsetX = reader.ReadInt32(); int layerOffsetY = reader.ReadInt32(); bool layerVisible = reader.ReadBoolean(); byte layerAlpha = reader.ReadByte(); int chunkCount = 0; // Load properties if available bool propsAvailable = reader.ReadBoolean(); if (propsAvailable) { var props = new TMXProperties(); LoadProperties(reader, props); layerDef.SetProperties(props); } if (map.infinite) { chunkCount = reader.ReadInt32(); } layerDef.chunkCount = chunkCount; layerDef.SetSize(new Vector2i(layerWidth, layerHeight)); layerDef.SetOffset(new Vector2i(layerOffsetX, layerOffsetY)); layerDef.SetVisible(layerVisible); layerDef.SetAlpha(layerAlpha); map.layers[name] = layerDef; return(true); }
private Dictionary <ulong, ChunkDef> GetLayerIndexTable(TMXMapDef tmx, int layerName) { if (tmx == null) { return(null); } var fileName = tmx.realPathName + "layer_" + layerName.ToString("x") + "_index"; var cached = tmx.mLayerIndexLRU.Get(fileName); if (cached != null) { return(cached); } var indexTableFile = Resources.Load <TextAsset>(fileName); if (indexTableFile == null) { Debug.Log("TMX could not find layer index table"); return(null); } try { var reader = new BinaryReader(new MemoryStream(indexTableFile.bytes)); int byteSize = 0; int chunkCount = reader.ReadInt32(); byteSize += 4; var table = new Dictionary <ulong, ChunkDef>(); // Return empty table if there are no chunks if (chunkCount == 0) { return(table); } for (int i = 0; i < chunkCount; i++) { var chunkDef = new ChunkDef(); ulong offset = reader.ReadUInt64(); byteSize += 8; chunkDef.segmentIndex = reader.ReadUInt16(); byteSize += 2; chunkDef.segmentOffset = reader.ReadUInt16(); byteSize += 2; chunkDef.compressedLength = reader.ReadUInt16(); byteSize += 2; table[offset] = chunkDef; } tmx.mLayerIndexLRU.Add(fileName, table, byteSize); return(table); } catch (IOException e) { Debug.Log("Failed to load layer index from file " + fileName + ", " + e.ToString()); return(null); } }
/// <summary> /// Load a map definition from a parsed binary TMX file /// </summary> /// <param name="fileName">Filename</param> /// <returns>Map definition</returns> public TMXMap LoadTMX(string fileName) { var map = new TMXMapDef(fileName); if (fileName == null) { return(null); } map.realPathName = Path.GetDirectoryName(fileName) + "/" + Path.GetFileNameWithoutExtension(fileName) + ".tmx.rb/"; map.realPathName = map.realPathName.Replace('\\', '/'); string infoFileName = map.realPathName + "info"; var tmxFile = Resources.Load <TextAsset>(infoFileName); if (tmxFile == null) { Debug.Log("Can't find TMX map at " + fileName + ". If TMX file exists then please try re-importing your TMX file."); return(null); } try { var reader = new BinaryReader(new MemoryStream(tmxFile.bytes)); var magicNum = reader.ReadUInt16(); var version = reader.ReadUInt16(); if (magicNum != RetroBlit_TMX_MAGIC) { Debug.Log(fileName + " is not a TMX file"); Debug.Log("Magic: " + magicNum + " expected " + RetroBlit_TMX_MAGIC); return(null); } if (version > RetroBlit_TMX_VERSION) { Debug.Log(fileName + " is of a newer version than this version of RetroBlit supports, try reimporting your TMX file into Unity."); return(null); } byte type = reader.ReadByte(); if (type != RetroBlit_TMX_TYPE_MAP) { Debug.Log(fileName + " is a RetroBlit TMX file but it is of the wrong type."); return(null); } int mapWidth = reader.ReadInt32(); int mapHeight = reader.ReadInt32(); byte r = reader.ReadByte(); byte g = reader.ReadByte(); byte b = reader.ReadByte(); byte a = reader.ReadByte(); bool infinite = reader.ReadBoolean(); map.SetSize(new Vector2i(mapWidth, mapHeight)); map.SetBackgroundColor(new Color32(r, g, b, a)); map.SetInfinite(infinite); int chunkWidth = reader.ReadInt32(); int chunkHeight = reader.ReadInt32(); map.chunkSize = new Vector2i(chunkWidth, chunkHeight); // Load properties if available bool propsAvailable = reader.ReadBoolean(); if (propsAvailable) { var props = new TMXProperties(); LoadProperties(reader, props); map.SetProperties(props); } if (!LoadTMXSections(reader, ref map)) { Debug.Log("Failed to load TMX sections from " + fileName); return(null); } } catch (IOException e) { Debug.Log("Failed to load TMX from file " + fileName + ", " + e.ToString()); return(null); } return(map); }
private bool LoadTMXObjectGroupDef(BinaryReader reader, ref TMXMapDef map) { var objectGroup = new TMXObjectGroupDef(); var name = reader.ReadString(); var r = reader.ReadByte(); var g = reader.ReadByte(); var b = reader.ReadByte(); var a = reader.ReadByte(); var alpha = reader.ReadByte(); var visible = reader.ReadBoolean(); var offsetX = reader.ReadInt32(); var offsetY = reader.ReadInt32(); objectGroup.SetName(name); objectGroup.SetColor(new Color32(r, g, b, a)); objectGroup.SetAlpha(alpha); objectGroup.SetVisible(visible); objectGroup.SetOffset(new Vector2i(offsetX, offsetY)); // Load properties if available bool propsAvailable = reader.ReadBoolean(); if (propsAvailable) { var props = new TMXProperties(); LoadProperties(reader, props); objectGroup.SetProperties(props); } // Now load objects var objects = new List <TMXObject>(); var objectCount = reader.ReadInt32(); for (int i = 0; i < objectCount; i++) { var objName = reader.ReadString(); var objType = reader.ReadString(); var rectX = reader.ReadInt32(); var rectY = reader.ReadInt32(); var rectWidth = reader.ReadInt32(); var rectHeight = reader.ReadInt32(); var rotation = reader.ReadSingle(); var objVisible = reader.ReadBoolean(); var shape = reader.ReadInt32(); var points = new List <Vector2i>(); var pointsCount = reader.ReadInt32(); for (int j = 0; j < pointsCount; j++) { var pointX = reader.ReadInt32(); var pointY = reader.ReadInt32(); points.Add(new Vector2i(pointX, pointY)); } var tmxObject = new TMXObjectDef(); tmxObject.SetName(objName); tmxObject.SetType(objType); tmxObject.SetShape((TMXObject.Shape)shape); tmxObject.SetRect(new Rect2i(rectX, rectY, rectWidth, rectHeight)); tmxObject.SetRotation(rotation); tmxObject.SetVisible(objVisible); tmxObject.SetPoints(points); // Load properties if available propsAvailable = reader.ReadBoolean(); if (propsAvailable) { var props = new TMXProperties(); LoadProperties(reader, props); tmxObject.SetProperties(props); } objects.Add(tmxObject); } objectGroup.SetObjects(objects); map.objectGroups[name] = objectGroup; return(true); }
/// <summary> /// Load a single layer chunk /// </summary> /// <param name="tmx">Map definition</param> /// <param name="tmxSourceLayer">Name of source layer</param> /// <param name="destinationLayer">RetroBlit destination layer</param> /// <param name="chunkOffset">Chunk offset</param> /// <param name="destPos">Destination position</param> /// <param name="packedSpriteLookup">Lookup table for translating TMX tile indexes to packed sprites</param> /// <returns>True if successful</returns> public bool LoadTMXLayerChunk(TMXMap tmx, string tmxSourceLayer, int destinationLayer, Vector2i chunkOffset, Vector2i destPos, PackedSpriteID[] packedSpriteLookup) { TMXMapDef map = null; if (!(tmx is RetroBlitInternal.RetroBlitTilemap.TMXMapDef)) { Debug.LogError("Can't load TMX layer, invalid map object!"); return(false); } map = (TMXMapDef)tmx; if (map == null || map.realPathName == null || map.realPathName.Length == 0 || map.layers == null) { Debug.LogError("Can't load TMX layer, invalid map, or map not open yet!"); return(false); } if (!map.infinite) { Debug.LogError("TMX map is not infinite, use LoadTMXLayer() instead"); return(false); } if (!map.layers.ContainsKey(tmxSourceLayer)) { Debug.LogError("Layer " + tmxSourceLayer + " not found"); return(false); } int chunkWidth = map.chunkSize.x; int chunkHeight = map.chunkSize.y; var tmxLayer = (TMXLayerDef)map.layers[tmxSourceLayer]; ulong part1 = (ulong)chunkOffset.x; ulong part2 = (ulong)chunkOffset.y; ulong offset = ((part1 << 32) & 0xFFFFFFFF00000000) | (part2 & 0xFFFFFFFF); int layerNameHash = mWorkStr.Set(tmxSourceLayer).ToLowerInvariant().GetHashCode(); var tupleKey = new RetroBlitTuple <int, ulong>(layerNameHash, offset); var decompressed = map.mChunkLRU.Get(tupleKey); if (decompressed == null) { var chunkTable = GetLayerIndexTable(map, layerNameHash); if (chunkTable == null) { Debug.LogError("TMX could not load chunk index table for layer " + tmxSourceLayer); return(false); } // If the chunk can't be found then fail silently and wipe the chunk area. This will also // release the chunk geometry on next draw because it will not have any vertices if (!chunkTable.ContainsKey(offset)) { for (int y = destPos.y; y < destPos.y + chunkHeight; y++) { for (int x = destPos.x; x < destPos.x + chunkWidth; x++) { mRetroBlitAPI.Tilemap.SpriteSet(destinationLayer, x, y, RB.SPRITE_EMPTY, Color.white, 0); Tile[] tilesArr; int tileIndex; if (GetTileRef(destinationLayer, x, y, out tilesArr, out tileIndex, true)) { tilesArr[tileIndex].data = null; } } } return(true); } var chunkDef = chunkTable[offset]; var chunkFileName = map.realPathName + "layer_" + layerNameHash.ToString("x") + "_seg_" + chunkDef.segmentIndex; var chunkFile = Resources.Load <TextAsset>(chunkFileName); if (chunkFile == null) { Debug.LogError("Can't find TMX file when loading TMX layer!"); return(false); } var chunkBytes = chunkFile.bytes; decompressed = RetroBlitDeflate.Decompress(chunkBytes, chunkDef.segmentOffset, chunkDef.compressedLength); if (decompressed == null || decompressed.Length <= 0) { Debug.LogError("Could not decompress tile data for layer " + tmxSourceLayer); return(false); } map.mChunkLRU.Add(tupleKey, decompressed, decompressed.Length); } var tileDataReader = new BinaryReader(new MemoryStream(decompressed)); if (tileDataReader == null) { Debug.LogError("Could not read tile data for layer " + tmxSourceLayer); return(false); } Color32 color = Color.white; int sx = 0; int sy = 0; int dx = destPos.x; int dy = destPos.y; while (tileDataReader.PeekChar() >= 0) { // Skip tsxIndex, don't need it for now tileDataReader.ReadByte(); byte flags = tileDataReader.ReadByte(); int tileId = tileDataReader.ReadInt32(); if (packedSpriteLookup != null) { if (packedSpriteLookup != null) { if (tileId < packedSpriteLookup.Length && tileId >= 0) { tileId = packedSpriteLookup[tileId].id; flags |= RetroBlitInternal.RetroBlitTilemap.SPRITEPACK; } } } SpriteSet(destinationLayer, dx, dy, tileId, color, flags); dx++; sx++; if (sx >= chunkWidth) { sx = 0; dx = destPos.x; sy++; dy++; } if (sy >= chunkHeight) { break; } } return(true); }
/// <summary> /// Load a layer definition from an map definition /// </summary> /// <param name="tmx">Map definition</param> /// <param name="tmxSourceLayer">Name of the layer to load</param> /// <param name="destinationLayer">Destination RetroBlit layer</param> /// <param name="sourceRect">Source rectangle</param> /// <param name="destPos">Destination position</param> /// <param name="packedSpriteLookup">Lookup table for translating TMX tile indexes to packed sprites</param> /// <returns>True if successful</returns> public bool LoadTMXLayer(TMXMap tmx, string tmxSourceLayer, int destinationLayer, Rect2i sourceRect, Vector2i destPos, PackedSpriteID[] packedSpriteLookup) { TMXMapDef map = null; if (!(tmx is RetroBlitInternal.RetroBlitTilemap.TMXMapDef)) { Debug.LogError("Can't load TMX layer, invalid map object!"); return(false); } map = (TMXMapDef)tmx; if (map == null || map.realPathName == null || map.realPathName.Length == 0 || map.layers == null) { Debug.LogError("Can't load TMX layer, invalid map, or map not open yet!"); return(false); } if (map.infinite) { Debug.LogError("TMX map is infinite, use MapLoadTMXLayerChunk() instead"); return(false); } if (!map.layers.ContainsKey(tmxSourceLayer)) { Debug.LogError("Layer " + tmxSourceLayer + " not found"); return(false); } var tmxLayer = (TMXLayerDef)map.layers[tmxSourceLayer]; var layerNameHash = mWorkStr.Set(tmxSourceLayer).ToLowerInvariant().GetHashCode().ToString("x"); var tmxFileName = map.realPathName + "layer_" + layerNameHash; var tmxFile = Resources.Load <TextAsset>(tmxFileName); if (tmxFile == null) { Debug.LogError("Can't find TMX file when loading TMX layer!"); return(false); } var tmxBytes = tmxFile.bytes; var decompressed = RetroBlitDeflate.Decompress(tmxBytes, 0, tmxBytes.Length); if (decompressed == null || decompressed.Length <= 0) { Debug.LogError("Could not decompress tile data for layer " + tmxSourceLayer); return(false); } var tileDataReader = new BinaryReader(new MemoryStream(decompressed)); if (tileDataReader == null) { Debug.LogError("Could not read tile data for layer " + tmxSourceLayer); return(false); } Color32 color = Color.white; int sx = 0; int sy = 0; int sx0 = sourceRect.x; int sx1 = sourceRect.x + sourceRect.width; int sy0 = sourceRect.y; int sy1 = sourceRect.y + sourceRect.height; int dx = destPos.x; int dy = destPos.y; while (tileDataReader.PeekChar() >= 0) { byte tsxIndex = tileDataReader.ReadByte(); byte flags = tileDataReader.ReadByte(); int tileId = tileDataReader.ReadInt32(); if (packedSpriteLookup != null) { if (tileId < packedSpriteLookup.Length && tileId >= 0) { tileId = packedSpriteLookup[tileId].id; flags |= RetroBlitInternal.RetroBlitTilemap.SPRITEPACK; } } if (sx >= sx0 && sx <= sx1 && sy >= sy0 && sy <= sy1) { SpriteSet(destinationLayer, dx, dy, tileId, color, flags); // Set properties if available if (tsxIndex >= 0 && tsxIndex < map.allTileProperties.Count) { var props = map.allTileProperties[tsxIndex]; if (props != null) { if (props.ContainsKey(tileId)) { DataSet <TMXProperties>(destinationLayer, dx, dy, props[tileId]); } } } dx++; } sx++; if (sx >= tmxLayer.size.x) { sx = 0; dx = destPos.x; sy++; dy++; } if (sy >= tmxLayer.size.y || sy >= sourceRect.y + sourceRect.height) { break; } } return(true); }