context: #region Copyright & License Information
  /*
   * Copyright 2007-2015 The OpenRA Developers (see AUTHORS)
   * This file is part of OpenRA, which is free software. It is made
   * available to you under the terms of the GNU General Public License
   * as published by the Free Software Foundation. For more information,
   * see COPYING.
   */
  #endregion
  
  using System;
  using System.Collections;
  using System.Collections.Generic;
  using System.Drawing;
  using System.IO;
  using System.Linq;
  using System.Security.Cryptography;
  using System.Text;
  using OpenRA.FileSystem;
  using OpenRA.Graphics;
  using OpenRA.Network;
  using OpenRA.Support;
  using OpenRA.Traits;
  
  namespace OpenRA
  {
  	struct BinaryDataHeader
  	{
  		public readonly byte Format;
  		public readonly uint TilesOffset;
  		public readonly uint HeightsOffset;
  		public readonly uint ResourcesOffset;
  
  		public BinaryDataHeader(Stream s, int2 expectedSize)
  		{
  			Format = s.ReadUInt8();
  			var width = s.ReadUInt16();
  			var height = s.ReadUInt16();
  			if (width != expectedSize.X || height != expectedSize.Y)
  				throw new InvalidDataException("Invalid tile data");
  
  			if (Format == 1)
  			{
  				TilesOffset = 5;
  				HeightsOffset = 0;
  				ResourcesOffset = (uint)(3 * width * height + 5);
  			}
  			else if (Format == 2)
  			{
  				TilesOffset = s.ReadUInt32();
  				HeightsOffset = s.ReadUInt32();
  				ResourcesOffset = s.ReadUInt32();
  			}
  			else
  				throw new InvalidDataException("Unknown binary map format '{0}'".F(Format));
  		}
  	}
  
  	public class MapOptions
  	{
  		public bool? Cheats;
  		public bool? Crates;
  		public bool? Creeps;
  		public bool? Fog;
  		public bool? Shroud;
  		public bool? AllyBuildRadius;
  		public bool? FragileAlliances;
  		public int? StartingCash;
  		public string TechLevel;
  		public bool ConfigurableStartingUnits = true;
  		public string[] Difficulties = { };
  		public bool? ShortGame;
  
  		public void UpdateServerSettings(Session.Global settings)
  		{
  			if (Cheats.HasValue)
  				settings.AllowCheats = Cheats.Value;
  			if (Crates.HasValue)
  				settings.Crates = Crates.Value;
  			if (Creeps.HasValue)
  				settings.Creeps = Creeps.Value;
  			if (Fog.HasValue)
  				settings.Fog = Fog.Value;
  			if (Shroud.HasValue)
  				settings.Shroud = Shroud.Value;
  			if (AllyBuildRadius.HasValue)
  				settings.AllyBuildRadius = AllyBuildRadius.Value;
  			if (StartingCash.HasValue)
  				settings.StartingCash = StartingCash.Value;
  			if (FragileAlliances.HasValue)
  				settings.FragileAlliances = FragileAlliances.Value;
  			if (ShortGame.HasValue)
  				settings.ShortGame = ShortGame.Value;
  		}
  	}
  
  	public class MapVideos
  	{
  		public string BackgroundInfo;
  		public string Briefing;
  		public string GameStart;
  		public string GameWon;
  		public string GameLost;
  	}
  
  	[Flags]
  	public enum MapVisibility
  	{
  		Lobby = 1,
  		Shellmap = 2,
  		MissionSelector = 4
  	}
  
  	public interface IMap
  	{
  		TileShape TileShape { get; }
  
  		int2 MapSize { get; set; }
  		bool Contains(CPos cell);
  		CPos CellContaining(WPos pos);
  		WVec OffsetOfSubCell(SubCell subCell);
  		IEnumerable<CPos> FindTilesInCircle(CPos center, int maxRange);
  		WPos CenterOfCell(CPos cell);
  	}
  
  	public class Map : IMap
  	{
  		public const int MaxTilesInCircleRange = 50;
  		public readonly TileShape TileShape;
  		TileShape IMap.TileShape
  		{
  			get { return TileShape; }
  		}
  
  		[FieldLoader.Ignore] public readonly WVec[] SubCellOffsets;
  		public readonly SubCell DefaultSubCell;
  		public readonly SubCell LastSubCell;
  		[FieldLoader.Ignore] public IFolder Container;
  		public string Path { get; private set; }
  
  		// Yaml map data
  		public string Uid { get; private set; }
  		public int MapFormat;
  		public MapVisibility Visibility = MapVisibility.Lobby;
  		public string RequiresMod;
  
  		public string Title;
  		public string Type = "Conquest";
  		public string Description;
  		public string Author;
  		public string Tileset;
  		public bool AllowStartUnitConfig = true;
  		public Bitmap CustomPreview;
  		public bool InvalidCustomRules { get; private set; }
  
  		public WVec OffsetOfSubCell(SubCell subCell) { return SubCellOffsets[(int)subCell]; }
  
  		[FieldLoader.LoadUsing("LoadOptions")] public MapOptions Options;
  
  		static object LoadOptions(MiniYaml y)
  		{
  			var options = new MapOptions();
  			var nodesDict = y.ToDictionary();
  			if (nodesDict.ContainsKey("Options"))
  				FieldLoader.Load(options, nodesDict["Options"]);
  
  			return options;
  		}
  
  		[FieldLoader.LoadUsing("LoadVideos")] public MapVideos Videos;
  
  		static object LoadVideos(MiniYaml y)
  		{
  			var videos = new MapVideos();
  			var nodesDict = y.ToDictionary();
  			if (nodesDict.ContainsKey("Videos"))
  				FieldLoader.Load(videos, nodesDict["Videos"]);
  
  			return videos;
  		}
  
  		[FieldLoader.Ignore] public Lazy<Dictionary<string, ActorReference>> Actors;
  
  		public int PlayerCount { get { return Players.Count(p => p.Value.Playable); } }
  
  		public Rectangle Bounds;
  
  		// Yaml map data
  		[FieldLoader.Ignore] public Dictionary<string, PlayerReference> Players = new Dictionary<string, PlayerReference>();
  		[FieldLoader.Ignore] public Lazy<List<SmudgeReference>> Smudges;
  
  		[FieldLoader.Ignore] public List<MiniYamlNode> RuleDefinitions = new List<MiniYamlNode>();
  		[FieldLoader.Ignore] public List<MiniYamlNode> SequenceDefinitions = new List<MiniYamlNode>();
  		[FieldLoader.Ignore] public List<MiniYamlNode> VoxelSequenceDefinitions = new List<MiniYamlNode>();
  		[FieldLoader.Ignore] public List<MiniYamlNode> WeaponDefinitions = new List<MiniYamlNode>();
  		[FieldLoader.Ignore] public List<MiniYamlNode> VoiceDefinitions = new List<MiniYamlNode>();
  		[FieldLoader.Ignore] public List<MiniYamlNode> NotificationDefinitions = new List<MiniYamlNode>();
  		[FieldLoader.Ignore] public List<MiniYamlNode> TranslationDefinitions = new List<MiniYamlNode>();
  
  		// Binary map data
  		[FieldLoader.Ignore] public byte TileFormat = 2;
  
  		public int2 MapSize;
  
  		int2 IMap.MapSize
  		{
  			get { return MapSize; }
  			set { MapSize = value; }
  		}
  
  		[FieldLoader.Ignore] public Lazy<CellLayer<TerrainTile>> MapTiles;
  		[FieldLoader.Ignore] public Lazy<CellLayer<ResourceTile>> MapResources;
  		[FieldLoader.Ignore] public Lazy<CellLayer<byte>> MapHeight;
  
  		[FieldLoader.Ignore] public CellLayer<byte> CustomTerrain;
  
  		[FieldLoader.Ignore] Lazy<TileSet> cachedTileSet;
  		[FieldLoader.Ignore] Lazy<Ruleset> rules;
  		public Ruleset Rules { get { return rules != null ? rules.Value : null; } }
  		public SequenceProvider SequenceProvider { get { return Rules.Sequences[Tileset]; } }
  
  		[FieldLoader.Ignore] public CellRegion Cells;
  
  		public static Map FromTileset(TileSet tileset)
  		{
  			var size = new Size(1, 1);
  			var tileShape = Game.ModData.Manifest.TileShape;
  			var tileRef = new TerrainTile(tileset.Templates.First().Key, (byte)0);
  
  			var makeMapTiles = Exts.Lazy(() =>
  			{
  				var ret = new CellLayer<TerrainTile>(tileShape, size);
  				ret.Clear(tileRef);
  				return ret;
  			});
  
  			var makeMapHeight = Exts.Lazy(() =>
  			{
  				var ret = new CellLayer<byte>(tileShape, size);
  				ret.Clear(0);
  				return ret;
  			});
  
  			var map = new Map()
  			{
  				Title = "Name your map here",
  				Description = "Describe your map here",
  				Author = "Your name here",
  				MapSize = new int2(size),
  				Tileset = tileset.Id,
  				Videos = new MapVideos(),
  				Options = new MapOptions(),
  				MapResources = Exts.Lazy(() => new CellLayer<ResourceTile>(tileShape, size)),
  				MapTiles = makeMapTiles,
  				MapHeight = makeMapHeight,
  				Actors = Exts.Lazy(() => new Dictionary<string, ActorReference>()),
  				Smudges = Exts.Lazy(() => new List<SmudgeReference>())
  			};
  
  			map.PostInit();
  
  			return map;
  		}
  
  		void AssertExists(string filename)
  		{
  			using (var s = Container.GetContent(filename))
  
gt: 				if (s == null)