private static void ParseTowns(Map map, FileLoader loader, FileLoaderNode otbNode)
        {
            var nodeTown = otbNode.Child;

            while (nodeTown != null)
            {
                PropertyReader props;
                if (!loader.GetProps(nodeTown, out props))
                    throw new Exception("Could not read town data.");

                uint townid = props.ReadUInt32();
                string townName = props.GetString();
                ushort townTempleX = props.ReadUInt16();
                ushort townTempleY = props.ReadUInt16();
                byte townTempleZ = props.ReadByte();

                var town = new Town(townid, townName, new Position(townTempleX, townTempleY, townTempleZ));
                map.AddTown(town);

                nodeTown = nodeTown.Next;
            }
        }
 public void Save(Map map, string fileName)
 {
     throw new NotImplementedException();
 }
        private static void ParseTileArea(Map map, FileLoader loader, FileLoaderNode otbNode)
        {
            PropertyReader props;
            if (!loader.GetProps(otbNode, out props))
                throw new Exception("Invalid map node.");

            int baseX = props.ReadUInt16();
            int baseY = props.ReadUInt16();
            int baseZ = props.ReadByte();

            var nodeTile = otbNode.Child;

            while (nodeTile != null)
            {
                if (nodeTile.Type == (long)OtbMapNodeType.Tile ||
                    nodeTile.Type == (long)OtbMapNodeType.HouseTile)
                {
                    loader.GetProps(nodeTile, out props);

                    int tileX = baseX + props.ReadByte();
                    int tileY = baseY + props.ReadByte();
                    int tileZ = baseZ;

                    bool isHouseTile = false;
                    //House* house = NULL;
                    //Tile tile = null;
                    //Item groundItem = null;
                    //TileFlags tileflags = TileFlags.None;

                    //var tile = new Tile(tileX, tileY, tileZ);

                    // TODO: houses
                    if (nodeTile.Type == (long)OtbMapNodeType.HouseTile)
                    {
                        uint houseId = props.ReadUInt32();
                    }

                    while (props.PeekChar() != -1)
                    {
                        byte attribute = props.ReadByte();
                        switch ((OtbmAttribute)attribute)
                        {
                            case OtbmAttribute.TileFlags:
                                {
                                    var flags = /*(TileFlags)*/props.ReadUInt32();
                                    //if ((flags & TileFlags.ProtectionZone) == TileFlags.ProtectionZone)
                                    //    tileflags |= TileFlags.ProtectionZone;
                                    //else if ((flags & TileFlags.NoPvpZone) == TileFlags.NoPvpZone)
                                    //    tileflags |= TileFlags.NoPvpZone;
                                    //else if ((flags & TileFlags.PvpZone) == TileFlags.PvpZone)
                                    //    tileflags |= TileFlags.PvpZone;

                                    //if ((flags & TileFlags.NoLogout) == TileFlags.NoLogout)
                                    //    tileflags |= TileFlags.NoLogout;

                                    //if ((flags & TileFlags.Refresh) == TileFlags.Refresh)
                                    //    tileflags |= TileFlags.Refresh;

                                    break;
                                }
                            case OtbmAttribute.Item:
                                {
                                    ushort itemId = props.ReadUInt16();
                                    //Item item = Item.Create(itemId);

                                    //if (item == null)
                                    //{
                                    //    Log.Error("Failed to create item.");
                                    //    return false;
                                    //}

                                    //if (tile != null)
                                    //{
                                    //    tile.InternalAddThing(item);
                                    //    item.StartDecaying();
                                    //}
                                    //else if (item.IsGround)
                                    //    groundItem = item;
                                    //else
                                    //{
                                    //    tile = CreateTile(groundItem, item, tileX, tileY, tileZ);
                                    //    tile.InternalAddThing(item);
                                    //    item.StartDecaying();
                                    //}

                                    break;
                                }
                            default:
                                throw new Exception(string.Format("{0} Unknown tile attribute.", new Position(tileX, tileY, tileZ)));
                        }
                    }

                    var nodeItem = nodeTile.Child;

                    while (nodeItem != null)
                    {
                        if (nodeItem.Type == (long)OtbMapNodeType.Item)
                        {
                            loader.GetProps(nodeItem, out props);
                            ushort itemId = props.ReadUInt16();

                            //// TODO: subclass item, different deserializations
                            //// for different types
                            //Item item = Item.Create(itemId);

                            //if (tile != null)
                            //{
                            //    tile.InternalAddThing(item);
                            //    item.StartDecaying();
                            //}
                            //else if (item.IsGround)
                            //    groundItem = item;
                            //else
                            //{
                            //    // !tile
                            //    tile = CreateTile(groundItem, item, tileX, tileY, tileZ);
                            //    tile.InternalAddThing(item);
                            //    item.StartDecaying();
                            //}
                        }
                        else
                        {
                            throw new Exception(string.Format("{0} Unknown node type.", new Position(tileX, tileY, tileZ)));
                        }

                        nodeItem = nodeItem.Next;
                    }

                    //if (tile == null)
                    //    tile = CreateTile(groundItem, null, tileX, tileY, tileZ);

                    //tile.SetFlag(tileflags);
                    //SetTile(tileX, tileY, tileZ, tile);
                }

                nodeTile = nodeTile.Next;
            }
        }
        public void Load(Map map, string fileName)
        {
            if (!File.Exists(fileName))
                throw new Exception(string.Format("File not found {0}.", fileName));

            var loader = new FileLoader();
            loader.OpenFile(fileName);
            var node = loader.GetRootNode();

            PropertyReader props;

            if (!loader.GetProps(node, out props))
                throw new Exception("Could not read root property.");

            props.ReadByte(); // junk?

            var version = props.ReadUInt32();
            var width = props.ReadUInt16();
            var height = props.ReadUInt16();

            var majorVersionItems = props.ReadUInt32();
            var minorVersionItems = props.ReadUInt32();

            if (version <= 0)
            {
                //In otbm version 1 the count variable after splashes/fluidcontainers and stackables
                //are saved as attributes instead, this solves alot of problems with items
                //that is changed (stackable/charges/fluidcontainer/splash) during an update.
                throw new Exception("This map needs to be upgraded by using the latest map editor version to be able to load correctly.");
            }

            if (version > 2)
                throw new Exception("Unknown OTBM version detected, please update your server.");

            if (majorVersionItems < 3)
                throw new Exception("This map needs to be upgraded by using the latest map editor version to be able to load correctly.");

            //if (MajorVersionItems > ItemInfo.MajorVersion)
            //{
            //    Log.Error("The map was saved with a different items.otb version, an upgraded items.otb is required.");
            //    return false;
            //}

            if (minorVersionItems < (uint)ClientVersion.ClientVersion810)
                throw new Exception("This map needs to be updated.");

            //if (MinorVersionItems > ItemInfo.MinorVersion)
            //    Log.Warn("This map needs an updated items.otb.");
            //if (MinorVersionItems == (uint)ClientVersion.ClientVersion854Bad)
            //    Log.Warn("This map needs uses an incorrect version of items.otb.");

            Logger.Info(string.Format("Map size: {0}x{1}", width, height));

            node = node.Child;

            if ((OtbMapNodeType)node.Type != OtbMapNodeType.MapData)
                throw new Exception("Could not read data node.");

            if (!loader.GetProps(node, out props))
                throw new Exception("Could not read map data attributes.");

            while (props.PeekChar() != -1)
            {
                byte attribute = props.ReadByte();
                switch ((OtbmAttribute)attribute)
                {
                    case OtbmAttribute.Description:
                        var description = props.GetString();
                        Logger.Info(string.Format("Map Description: {0}", description));
                        break;
                    case OtbmAttribute.ExtSpawnFile:
                        var spawnFile = props.GetString();
                        break;
                    case OtbmAttribute.ExtHouseFile:
                        var houseFile = props.GetString();
                        break;
                    default:
                        throw new Exception("Unknown header node.");
                }
            }

            var nodeMapData = node.Child;

            while (nodeMapData != null)
            {
                switch ((OtbMapNodeType)nodeMapData.Type)
                {
                    case OtbMapNodeType.TileArea:
                        ParseTileArea(map, loader, nodeMapData);
                        break;
                    case OtbMapNodeType.Towns:
                        ParseTowns(map, loader, nodeMapData);
                        break;
                }
                nodeMapData = nodeMapData.Next;
            }
        }