context: #region Copyright & License Information
  /*
   * Copyright 2007-2011 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.Generic;
  using System.Drawing;
  using System.IO;
  using System.Linq;
  using System.Security.Cryptography;
  using System.Text;
  using OpenRA.FileFormats;
  using OpenRA.Network;
  using OpenRA.Traits;
  
  namespace OpenRA
  {
  	public class MapOptions
  	{
  		public bool? Cheats;
  		public bool? Crates;
  		public bool? Fog;
  		public bool? Shroud;
  		public bool? AllyBuildRadius;
  		public bool? FragileAlliances;
  		public int? StartingCash;
  		public bool ConfigurableStartingUnits = true;
  		public string[] Difficulties = { };
  
  		public void UpdateServerSettings(Session.Global settings)
  		{
  			if (Cheats.HasValue)
  				settings.AllowCheats = Cheats.Value;
  			if (Crates.HasValue)
  				settings.Crates = Crates.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;
  		}
  	}
  
  	public class Map
  	{
  		[FieldLoader.Ignore] public IFolder Container;
  		public string Path { get; private set; }
  
  		// Yaml map data
  		public string Uid { get; private set; }
  		public int MapFormat;
  		public bool Selectable = true;
  		public bool UseAsShellmap;
  		public string RequiresMod;
  
  		public string Title;
  		public string Type = "Conquest";
  		public string Description;
  		public string Author;
  		public string Tileset;
  		public bool AllowStartUnitConfig = true;
  
  		[FieldLoader.LoadUsing("LoadOptions")]
  		public MapOptions Options;
  
  		static object LoadOptions(MiniYaml y)
  		{
  			var options = new MapOptions();
  			if (y.NodesDict.ContainsKey("Options"))
  				FieldLoader.Load(options, y.NodesDict["Options"]);
  
  			return options;
  		}
  
  		[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> Rules = new List<MiniYamlNode>();
  		[FieldLoader.Ignore] public List<MiniYamlNode> Sequences = new List<MiniYamlNode>();
  		[FieldLoader.Ignore] public List<MiniYamlNode> VoxelSequences = new List<MiniYamlNode>();
  		[FieldLoader.Ignore] public List<MiniYamlNode> Weapons = new List<MiniYamlNode>();
  		[FieldLoader.Ignore] public List<MiniYamlNode> Voices = new List<MiniYamlNode>();
  		[FieldLoader.Ignore] public List<MiniYamlNode> Notifications = new List<MiniYamlNode>();
  		[FieldLoader.Ignore] public List<MiniYamlNode> Translations = new List<MiniYamlNode>();
  
  		// Binary map data
  		[FieldLoader.Ignore] public byte TileFormat = 1;
  		public int2 MapSize;
  
  		[FieldLoader.Ignore] public Lazy<TileReference<ushort, byte>[,]> MapTiles;
  		[FieldLoader.Ignore] public Lazy<TileReference<byte, byte>[,]> MapResources;
  		[FieldLoader.Ignore] public string[,] CustomTerrain;
  
  		public static Map FromTileset(TileSet tileset)
  		{
  			var tile = tileset.Templates.First();
  			var tileRef = new TileReference<ushort, byte> { Type = tile.Key, Index = (byte)0 };
  
  			Map map = new Map()
  			{
  				Title = "Name your map here",
  				Description = "Describe your map here",
  				Author = "Your name here",
  				MapSize = new int2(1, 1),
  				Tileset = tileset.Id,
  				Options = new MapOptions(),
  				MapResources = Lazy.New(() => new TileReference<byte, byte>[1, 1]),
  				MapTiles = Lazy.New(() => new TileReference<ushort, byte>[1, 1] { { tileRef } }),
  				Actors = Lazy.New(() => new Dictionary<string, ActorReference>()),
  				Smudges = Lazy.New(() => new List<SmudgeReference>())
  			};
  
  			return map;
  		}
  
  		void AssertExists(string filename)
  		{
  			using (var s = Container.GetContent(filename))
  				if (s == null)
  					throw new InvalidOperationException("Required file {0} not present in this map".F(filename));
  		}
  
  		// Stub constructor that doesn't produce a valid map, but is
  		// sufficient for loading a mod to the content-install panel
  		public Map() { }
  
  		// The standard constructor for most purposes
  		public Map(string path) : this(path, null) { }
  
  		// Support upgrading format 5 maps to a more
  		// recent version by defining upgradeForMod.
  		public Map(string path, string upgradeForMod)
  		{
  			Path = path;
  			Container = FileSystem.OpenPackage(path, null, int.MaxValue);
  
  			AssertExists("map.yaml");
  			AssertExists("map.bin");
  
  			var yaml = new MiniYaml(null, MiniYaml.FromStream(Container.GetContent("map.yaml")));
  			FieldLoader.Load(this, yaml);
  
  			// Support for formats 1-3 dropped 2011-02-11.
  			// Use release-20110207 to convert older maps to format 4
  			// Use release-20110511 to convert older maps to format 5
  			if (MapFormat < 5)
  				throw new InvalidDataException("Map format {0} is not supported.\n File: {1}".F(MapFormat, path));
  
  			// Format 5 -> 6 enforces the use of RequiresMod
  			if (MapFormat == 5)
  			{
  				if (upgradeForMod == null)
  					throw new InvalidDataException("Map format {0} is not supported, but can be upgraded.\n File: {1}".F(MapFormat, path));
  
  				Console.WriteLine("Upgrading {0} from Format 5 to Format 6", path);
  
  				// TODO: This isn't very nice, but there is no other consistent way
  				// of finding the mod early during the engine initialization.
  				RequiresMod = upgradeForMod;
  			}
  
  			// Load players
  			foreach (var kv in yaml.NodesDict["Players"].NodesDict)
  			{
  				var player = new PlayerReference(kv.Value);
  				Players.Add(player.Name, player);
  			}
  
  			Actors = Lazy.New(() =>
  			{
  				var ret = new Dictionary<string, ActorReference>();
  				foreach (var kv in yaml.NodesDict["Actors"].NodesDict)
  					ret.Add(kv.Key, new ActorReference(kv.Value.Value, kv.Value.NodesDict));
  				return ret;
  			});
  
  			// Smudges
  			Smudges = Lazy.New(() =>
  			{
  				var ret = new List<SmudgeReference>();
  				foreach (var kv in yaml.NodesDict["Smudges"].NodesDict)
  				{
  					var vals = kv.Key.Split(' ');
  					var loc = vals[1].Split(',');
  					ret.Add(new SmudgeReference(vals[0], new int2(int.Parse(loc[0]), int.Parse(loc[1])), int.Parse(vals[2])));
  				}
  
  				return ret;
  			});
  
  			Rules = MiniYaml.NodesOrEmpty(yaml, "Rules");
  			Sequences = MiniYaml.NodesOrEmpty(yaml, "Sequences");
  			VoxelSequences = MiniYaml.NodesOrEmpty(yaml, "VoxelSequences");
  
gt: 			Weapons = MiniYaml.NodesOrEmpty(yaml, "Weapons");