From ed08314ec091d1ebcd832b44168fac9553c1929f Mon Sep 17 00:00:00 2001 From: Chris Forbes Date: Thu, 8 Apr 2010 21:07:50 +1200 Subject: [PATCH] clean up some messy GC behavior & needlessly longwinded code. slight perf cost on map save. --- OpenRA.FileFormats/Exts.cs | 18 ++ OpenRA.FileFormats/Map/Map.cs | 472 +++++++++++++++--------------- OpenRA.FileFormats/Map/MapStub.cs | 111 ++++--- 3 files changed, 303 insertions(+), 298 deletions(-) diff --git a/OpenRA.FileFormats/Exts.cs b/OpenRA.FileFormats/Exts.cs index 3484b7773e..914341dcd7 100644 --- a/OpenRA.FileFormats/Exts.cs +++ b/OpenRA.FileFormats/Exts.cs @@ -22,6 +22,7 @@ using System; using System.Collections.Generic; using System.Reflection; using System.Linq; +using System.IO; namespace OpenRA { @@ -42,5 +43,22 @@ namespace OpenRA { return a.GetTypes().Select(t => t.Namespace).Distinct().Where(n => n != null); } + + public static string ReadAllText(this Stream s) + { + using (s) + using (var sr = new StreamReader(s)) + return sr.ReadToEnd(); + } + + public static byte[] ReadAllBytes(this Stream s) + { + using (s) + { + var data = new byte[s.Length - s.Position]; + s.Read(data, 0, data.Length); + return data; + } + } } } diff --git a/OpenRA.FileFormats/Map/Map.cs b/OpenRA.FileFormats/Map/Map.cs index ca6e223ff8..95aca52b5d 100644 --- a/OpenRA.FileFormats/Map/Map.cs +++ b/OpenRA.FileFormats/Map/Map.cs @@ -23,247 +23,237 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; -using System.Security.Cryptography; - -namespace OpenRA.FileFormats -{ - public class Map - { - public IFolder Package; - public string Uid; - - // Yaml map data - public int MapFormat = 1; - public string Title; - public string Description; - public string Author; - public int PlayerCount; - public string Tileset; - - public Dictionary Actors = new Dictionary(); - public List Smudges = new List(); - public Dictionary Waypoints = new Dictionary(); - public Dictionary Rules = new Dictionary(); - - // Binary map data - public byte TileFormat = 1; - public int2 MapSize; - - public int2 TopLeft; - public int2 BottomRight; - - public TileReference[ , ] MapTiles; - public TileReference[ , ] MapResources; - - - // Temporary compat hacks - public int XOffset {get {return TopLeft.X;}} - public int YOffset {get {return TopLeft.Y;}} - public int Width {get {return BottomRight.X-TopLeft.X;}} - public int Height {get {return BottomRight.Y-TopLeft.Y;}} - public string Theater {get {return Tileset;}} - public IEnumerable SpawnPoints {get {return Waypoints.Select(kv => kv.Value);}} - +using System.Security.Cryptography; + +namespace OpenRA.FileFormats +{ + public class Map + { + public IFolder Package; + public string Uid; + + // Yaml map data + public int MapFormat = 1; + public string Title; + public string Description; + public string Author; + public int PlayerCount; + public string Tileset; + + public Dictionary Actors = new Dictionary(); + public List Smudges = new List(); + public Dictionary Waypoints = new Dictionary(); + public Dictionary Rules = new Dictionary(); + + // Binary map data + public byte TileFormat = 1; + public int2 MapSize; + + public int2 TopLeft; + public int2 BottomRight; + + public TileReference[,] MapTiles; + public TileReference[,] MapResources; + + + // Temporary compat hacks + public int XOffset { get { return TopLeft.X; } } + public int YOffset { get { return TopLeft.Y; } } + public int Width { get { return BottomRight.X - TopLeft.X; } } + public int Height { get { return BottomRight.Y - TopLeft.Y; } } + public string Theater { get { return Tileset; } } + public IEnumerable SpawnPoints { get { return Waypoints.Select(kv => kv.Value); } } + static List SimpleFields = new List() { "MapFormat", "Title", "Description", "Author", "PlayerCount", "Tileset", "MapSize", "TopLeft", "BottomRight" - }; - - public Map() {} - - public Map(IFolder package) - { - Package = package; - var yaml = MiniYaml.FromStream(Package.GetContent("map.yaml")); - - // 'Simple' metadata - FieldLoader.LoadFields(this,yaml,SimpleFields); - - // Waypoints - foreach (var wp in yaml["Waypoints"].Nodes) - { - string[] loc = wp.Value.Value.Split(','); - Waypoints.Add(wp.Key, new int2(int.Parse(loc[0]),int.Parse(loc[1]))); - } - - // Actors - foreach (var kv in yaml["Actors"].Nodes) - { - string[] vals = kv.Value.Value.Split(' '); - string[] loc = vals[2].Split(','); - var a = new ActorReference(vals[0], new int2(int.Parse(loc[0]),int.Parse(loc[1])) ,vals[2]); - Actors.Add(kv.Key,a); - } - - // Smudges - foreach (var kv in yaml["Smudges"].Nodes) - { - string[] vals = kv.Key.Split(' '); - string[] loc = vals[1].Split(','); - Smudges.Add(new SmudgeReference(vals[0], new int2(int.Parse(loc[0]),int.Parse(loc[1])) ,int.Parse(vals[2]))); - } - - // Rules - Rules = yaml["Rules"].Nodes; - - LoadUid(); - LoadBinaryData(); - } - - public void Save(string filepath) - { - Dictionary root = new Dictionary(); - var d = new Dictionary(); - foreach (var field in SimpleFields) - { - FieldInfo f = this.GetType().GetField(field); - if (f.GetValue(this) == null) continue; - root.Add(field,new MiniYaml(FieldSaver.FormatValue(this,f),null)); - } - - root.Add("Actors",MiniYaml.FromDictionary(Actors)); - root.Add("Waypoints",MiniYaml.FromDictionary(Waypoints)); - root.Add("Smudges",MiniYaml.FromList(Smudges)); - root.Add("Rules",new MiniYaml(null,Rules)); - SaveBinaryData(Path.Combine(filepath,"map.bin")); - root.WriteToFile(Path.Combine(filepath,"map.yaml")); - SaveUid(Path.Combine(filepath,"map.uid")); - } - - static byte ReadByte( Stream s ) - { - int ret = s.ReadByte(); - if( ret == -1 ) - throw new NotImplementedException(); - return (byte)ret; - } - - static ushort ReadWord(Stream s) - { - ushort ret = ReadByte(s); - ret |= (ushort)(ReadByte(s) << 8); - - return ret; - } - - public void LoadBinaryData() - { - Stream dataStream = Package.GetContent("map.bin"); - - // Load header info - byte version = ReadByte(dataStream); - var width = ReadWord(dataStream); - var height = ReadWord(dataStream); - - if (width != MapSize.X || height != MapSize.Y) - throw new InvalidDataException("Invalid tile data"); - - MapTiles = new TileReference[ MapSize.X, MapSize.Y ]; - MapResources = new TileReference[ MapSize.X, MapSize.Y ]; - - // Load tile data - for( int i = 0 ; i < MapSize.X ; i++ ) - for( int j = 0 ; j < MapSize.Y ; j++ ) - { - ushort tile = ReadWord(dataStream); - byte index = ReadByte(dataStream); - byte image = (index == byte.MaxValue) ? (byte)( i % 4 + ( j % 4 ) * 4 ) : index; - MapTiles[i,j] = new TileReference(tile,index, image); - } - - // Load resource data - for( int i = 0 ; i < MapSize.X ; i++ ) - for( int j = 0 ; j < MapSize.Y ; j++ ) - MapResources[i,j] = new TileReference(ReadByte(dataStream),ReadByte(dataStream)); - } - - public void SaveBinaryData(string filepath) - { - FileStream dataStream = new FileStream(filepath+".tmp", FileMode.Create, FileAccess.Write); - BinaryWriter writer = new BinaryWriter( dataStream ); - writer.BaseStream.Seek(0, SeekOrigin.Begin); - - // File header consists of a version byte, followed by 2 ushorts for width and height - writer.Write(TileFormat); - writer.Write((ushort)MapSize.X); - writer.Write((ushort)MapSize.Y); - - // Tile data - for( int i = 0 ; i < MapSize.X ; i++ ) - for( int j = 0 ; j < MapSize.Y ; j++ ) - { - writer.Write( MapTiles[i,j].type ); - writer.Write( MapTiles[i,j].index ); - } - - // Resource data - for( int i = 0 ; i < MapSize.X ; i++ ) - for( int j = 0 ; j < MapSize.Y ; j++ ) - { - writer.Write( MapResources[i,j].type ); - writer.Write( MapResources[i,j].index ); - } - - writer.Flush(); - writer.Close(); - File.Delete(filepath); - File.Move(filepath+".tmp",filepath); - } - - public void LoadUid() - { - StreamReader uidStream = new StreamReader(Package.GetContent("map.uid")); - Uid = uidStream.ReadLine(); - uidStream.Close(); - } - - public void SaveUid(string filename) - { - // UID is calculated by taking an SHA1 of the yaml and binary data - // Read the relevant data into a buffer - var yamlStream = Package.GetContent("map.yaml"); - var binaryStream = Package.GetContent("map.bin"); - var data = new byte[yamlStream.Length+binaryStream.Length]; - - yamlStream.Read(data,0,(int)yamlStream.Length); - binaryStream.Read(data,(int)yamlStream.Length,(int)binaryStream.Length); - - // Take the SHA1 - using (var csp = SHA1.Create()) - Uid = new string(csp.ComputeHash(data).SelectMany(a => a.ToString("x2")).ToArray()); - - // Save to file - StreamWriter file = new System.IO.StreamWriter(filename); - file.WriteLine(Uid); - file.Close(); - } - - public bool IsInMap(int2 xy) - { - return IsInMap(xy.X,xy.Y); - } - - public bool IsInMap(int x, int y) - { - return (x >= TopLeft.X && y >= TopLeft.Y && x < BottomRight.X && y < BottomRight.Y); - } - - public void DebugContents() - { - foreach (var field in SimpleFields) - Console.WriteLine("Loaded {0}: {1}", field, this.GetType().GetField(field).GetValue(this)); - - Console.WriteLine("Loaded Waypoints:"); - foreach (var wp in Waypoints) - Console.WriteLine("\t{0} => {1}",wp.Key,wp.Value); - - Console.WriteLine("Loaded Actors:"); - foreach (var wp in Actors) - Console.WriteLine("\t{0} => {1} {2} {3}",wp.Key,wp.Value.Name, wp.Value.Owner,wp.Value.Location); - - Console.WriteLine("Loaded Smudges:"); - foreach (var s in Smudges) - Console.WriteLine("\t{0} {1} {2}",s.Type,s.Location,s.Depth); - - } - } + }; + + public Map() { } + + public Map(IFolder package) + { + Package = package; + var yaml = MiniYaml.FromStream(Package.GetContent("map.yaml")); + + // 'Simple' metadata + FieldLoader.LoadFields(this, yaml, SimpleFields); + + // Waypoints + foreach (var wp in yaml["Waypoints"].Nodes) + { + string[] loc = wp.Value.Value.Split(','); + Waypoints.Add(wp.Key, new int2(int.Parse(loc[0]), int.Parse(loc[1]))); + } + + // Actors + foreach (var kv in yaml["Actors"].Nodes) + { + string[] vals = kv.Value.Value.Split(' '); + string[] loc = vals[2].Split(','); + var a = new ActorReference(vals[0], new int2(int.Parse(loc[0]), int.Parse(loc[1])), vals[2]); + Actors.Add(kv.Key, a); + } + + // Smudges + foreach (var kv in yaml["Smudges"].Nodes) + { + string[] vals = kv.Key.Split(' '); + string[] loc = vals[1].Split(','); + Smudges.Add(new SmudgeReference(vals[0], new int2(int.Parse(loc[0]), int.Parse(loc[1])), int.Parse(vals[2]))); + } + + // Rules + Rules = yaml["Rules"].Nodes; + + LoadUid(); + LoadBinaryData(); + } + + public void Save(string filepath) + { + Dictionary root = new Dictionary(); + var d = new Dictionary(); + foreach (var field in SimpleFields) + { + FieldInfo f = this.GetType().GetField(field); + if (f.GetValue(this) == null) continue; + root.Add(field, new MiniYaml(FieldSaver.FormatValue(this, f), null)); + } + + root.Add("Actors", MiniYaml.FromDictionary(Actors)); + root.Add("Waypoints", MiniYaml.FromDictionary(Waypoints)); + root.Add("Smudges", MiniYaml.FromList(Smudges)); + root.Add("Rules", new MiniYaml(null, Rules)); + SaveBinaryData(Path.Combine(filepath, "map.bin")); + root.WriteToFile(Path.Combine(filepath, "map.yaml")); + SaveUid(Path.Combine(filepath, "map.uid")); + } + + static byte ReadByte(Stream s) + { + int ret = s.ReadByte(); + if (ret == -1) + throw new NotImplementedException(); + return (byte)ret; + } + + static ushort ReadWord(Stream s) + { + ushort ret = ReadByte(s); + ret |= (ushort)(ReadByte(s) << 8); + + return ret; + } + + public void LoadBinaryData() + { + using (var dataStream = Package.GetContent("map.bin")) + { + // Load header info + byte version = ReadByte(dataStream); + var width = ReadWord(dataStream); + var height = ReadWord(dataStream); + + if (width != MapSize.X || height != MapSize.Y) + throw new InvalidDataException("Invalid tile data"); + + MapTiles = new TileReference[MapSize.X, MapSize.Y]; + MapResources = new TileReference[MapSize.X, MapSize.Y]; + + // Load tile data + for (int i = 0; i < MapSize.X; i++) + for (int j = 0; j < MapSize.Y; j++) + { + ushort tile = ReadWord(dataStream); + byte index = ReadByte(dataStream); + byte image = (index == byte.MaxValue) ? (byte)(i % 4 + (j % 4) * 4) : index; + MapTiles[i, j] = new TileReference(tile, index, image); + } + + // Load resource data + for (int i = 0; i < MapSize.X; i++) + for (int j = 0; j < MapSize.Y; j++) + MapResources[i, j] = new TileReference(ReadByte(dataStream), ReadByte(dataStream)); + } + } + + public void SaveBinaryData(string filepath) + { + using (var dataStream = File.Create(filepath + ".tmp")) + using (var writer = new BinaryWriter(dataStream)) + { + writer.BaseStream.Seek(0, SeekOrigin.Begin); + + // File header consists of a version byte, followed by 2 ushorts for width and height + writer.Write(TileFormat); + writer.Write((ushort)MapSize.X); + writer.Write((ushort)MapSize.Y); + + // Tile data + for (int i = 0; i < MapSize.X; i++) + for (int j = 0; j < MapSize.Y; j++) + { + writer.Write(MapTiles[i, j].type); + writer.Write(MapTiles[i, j].index); + } + + // Resource data + for (int i = 0; i < MapSize.X; i++) + for (int j = 0; j < MapSize.Y; j++) + { + writer.Write(MapResources[i, j].type); + writer.Write(MapResources[i, j].index); + } + } + File.Delete(filepath); + File.Move(filepath + ".tmp", filepath); + } + + public void LoadUid() + { + Uid = Package.GetContent("map.uid").ReadAllText(); + } + + public void SaveUid(string filename) + { + // UID is calculated by taking an SHA1 of the yaml and binary data + // Read the relevant data into a buffer + var data = Exts.ReadAllBytes(Package.GetContent("map.yaml")) + .Concat(Exts.ReadAllBytes(Package.GetContent("map.bin"))).ToArray(); + + // Take the SHA1 + using (var csp = SHA1.Create()) + Uid = new string(csp.ComputeHash(data).SelectMany(a => a.ToString("x2")).ToArray()); + + File.WriteAllText(filename, Uid); + } + + public bool IsInMap(int2 xy) + { + return IsInMap(xy.X, xy.Y); + } + + public bool IsInMap(int x, int y) + { + return (x >= TopLeft.X && y >= TopLeft.Y && x < BottomRight.X && y < BottomRight.Y); + } + + public void DebugContents() + { + foreach (var field in SimpleFields) + Console.WriteLine("Loaded {0}: {1}", field, this.GetType().GetField(field).GetValue(this)); + + Console.WriteLine("Loaded Waypoints:"); + foreach (var wp in Waypoints) + Console.WriteLine("\t{0} => {1}", wp.Key, wp.Value); + + Console.WriteLine("Loaded Actors:"); + foreach (var wp in Actors) + Console.WriteLine("\t{0} => {1} {2} {3}", wp.Key, wp.Value.Name, wp.Value.Owner, wp.Value.Location); + + Console.WriteLine("Loaded Smudges:"); + foreach (var s in Smudges) + Console.WriteLine("\t{0} {1} {2}", s.Type, s.Location, s.Depth); + } + } } diff --git a/OpenRA.FileFormats/Map/MapStub.cs b/OpenRA.FileFormats/Map/MapStub.cs index 89149727f9..6b2fcc9286 100644 --- a/OpenRA.FileFormats/Map/MapStub.cs +++ b/OpenRA.FileFormats/Map/MapStub.cs @@ -16,63 +16,60 @@ * You should have received a copy of the GNU General Public License * along with OpenRA. If not, see . */ -#endregion - -using System.Collections.Generic; -using System.Drawing; -using System.IO; -using System; - -namespace OpenRA.FileFormats -{ - public class MapStub - { - public IFolder Package; - - // Yaml map data - public string Uid; - public string Title; - public string Description; - public string Author; - public int PlayerCount; - public string Tileset; - - public int2 TopLeft; - public int2 BottomRight; - public int Width {get {return BottomRight.X - TopLeft.X;}} - public int Height {get {return BottomRight.Y - TopLeft.Y;}} - public Lazy Preview; - +#endregion + +using System; +using System.Collections.Generic; +using System.Drawing; + +namespace OpenRA.FileFormats +{ + public class MapStub + { + public IFolder Package; + + // Yaml map data + public string Uid; + public string Title; + public string Description; + public string Author; + public int PlayerCount; + public string Tileset; + + public int2 TopLeft; + public int2 BottomRight; + public int Width { get { return BottomRight.X - TopLeft.X; } } + public int Height { get { return BottomRight.Y - TopLeft.Y; } } + public Lazy Preview; + static List Fields = new List() { "Title", "Description", "Author", "PlayerCount", "Tileset", "TopLeft", "BottomRight" - }; - - public MapStub() {} - - public MapStub(IFolder package) - { - Package = package; - var yaml = MiniYaml.FromStream(Package.GetContent("map.yaml")); - FieldLoader.LoadFields(this,yaml,Fields); - - Preview = Lazy.New( - () => {return new Bitmap(Package.GetContent("preview.png"));} - ); - - StreamReader uidStream = new StreamReader(Package.GetContent("map.uid")); - Uid = uidStream.ReadLine(); - uidStream.Close(); - } - - public Rectangle PreviewBounds(Rectangle container) - { - float scale = Math.Min(container.Width*1.0f/Width,container.Height*1.0f/Height); - - var size = Math.Max(Width, Height); - var dw = (int)(scale*(size - Width)) / 2; - var dh = (int)(scale*(size - Height)) / 2; - - return new Rectangle(container.X + dw, container.Y + dh, (int)(Width*scale), (int)(Height*scale)); - } - } + }; + + public MapStub() { } + + public MapStub(IFolder package) + { + Package = package; + var yaml = MiniYaml.FromStream(Package.GetContent("map.yaml")); + FieldLoader.LoadFields(this, yaml, Fields); + + Preview = Lazy.New( + () => { return new Bitmap(Package.GetContent("preview.png")); } + ); + + Uid = Package.GetContent("map.uid").ReadAllText(); + } + + public Rectangle PreviewBounds(Rectangle container) + { + float scale = Math.Min(container.Width * 1.0f / Width, container.Height * 1.0f / Height); + + var size = Math.Max(Width, Height); + var dw = (int)(scale * (size - Width)) / 2; + var dh = (int)(scale * (size - Height)) / 2; + + return new Rectangle(container.X + dw, container.Y + dh, (int)(Width * scale), (int)(Height * scale)); + } + } }