void Start() { town = new Town(); peop = new Peop(); terr = new Terr(); server(); }
/// <summary> /// Decompresses the passed Terr block. /// </summary> /// <param name="terr">Terr block to decompress</param> /// <returns>Decompressed version</returns> public static Terr Decompress(this Terr oldTerr) { Terr newTerr = new Terr(); newTerr.Width = oldTerr.Width; newTerr.Height = oldTerr.Height; foreach (Terrblock block in oldTerr.BlocksHmap1) { decompressBlock(oldTerr, newTerr, block, true); } foreach (Terrblock block in oldTerr.BlocksHmap2) { decompressBlock(oldTerr, newTerr, block, false); } return newTerr; }
/// <summary> /// Does the dirty work /// </summary> /// <param name="reader"></param> private void Parse(BinaryReader prjReader) { using (MemoryStream ms = new MemoryStream()) { BinaryWriter prjBegin = new BinaryWriter(ms); // check that this is a PRJ file string idString = ASCIIEncoding.ASCII.GetString(prjReader.ReadBytes(32)); string header = "Dark Omen Battle file 1.10 "; if (idString != header) throw new IOException("Not a PRJ File"); prjBegin.Write(Encoding.ASCII.GetBytes(header)); // ignore the BASE block prjBegin.Write(prjReader.ReadInt32()); int size = prjReader.ReadInt32(); prjBegin.Write(size); prjBegin.Write(prjReader.ReadBytes(size)); // ignore the WATR block prjBegin.Write(prjReader.ReadInt32()); size = prjReader.ReadInt32(); prjBegin.Write(size); prjBegin.Write(prjReader.ReadBytes(size)); // ignore the FURN block prjBegin.Write(prjReader.ReadInt32()); size = prjReader.ReadInt32(); int fixup = prjReader.ReadInt32(); prjBegin.Write(size); prjBegin.Write(fixup); prjBegin.Write(prjReader.ReadBytes(size + fixup * 4 - 4)); // ignore the INST block prjBegin.Write(prjReader.ReadInt32()); size = prjReader.ReadInt32(); fixup = 8; prjBegin.Write(size); prjBegin.Write(prjReader.ReadBytes(size + fixup)); this.prjBegin = ms.ToArray(); } // Read the TERR Block Terr = new Terr(prjReader); // Read until EOF using (MemoryStream ms = new MemoryStream()) { BinaryWriter prjEnd = new BinaryWriter(ms); try { for (;;) { prjEnd.Write(prjReader.ReadByte()); } } catch (EndOfStreamException) { // Not very smart } this.prjEnd = ms.ToArray(); } }
private static void PrintStatistic(Terr terr) { // Map dimension Console.Error.WriteLine("Dimension: " + terr.Width + "x" + terr.Height); // Min/Max Heightmap Value (1st hmap) int min = int.MaxValue; int max = int.MinValue; foreach (Terrblock block in terr.BlocksHmap1) { min = Math.Min(min, block.Minimum); max = Math.Max(min, block.Minimum); } Console.Error.WriteLine("Min/Max (Hmap1): " + min + "/" + max); // Min/Max Heightmap Value (2nd hmap) min = int.MaxValue; max = int.MinValue; foreach (Terrblock block in terr.BlocksHmap2) { min = Math.Min(min, block.Minimum); max = Math.Max(min, block.Minimum); } Console.Error.WriteLine("Min/Max (Hmap2): " + min + "/" + max); // Block count (Macro and Micro blocks) + Compression ratio Console.Error.WriteLine("Blocks: " + terr.BlocksHmap1.Count * 2 + "/" + terr.Offsets.Count); String ratio = (100 - (float)terr.Offsets.Count / (terr.BlocksHmap1.Count * 2) * 100).ToString("0.00"); Console.Error.WriteLine("Compression: " + ratio + "%"); }
/// <summary> /// Creates a Terr heightmap from a Bitmap /// </summary> /// <param name="oldTerr">Source terr</param> /// <param name="heightmap">Heightmap Bitmap</param> /// <param name="targetHmaps">Flags to determine which Heightmaps shall be changed</param> /// <returns></returns> public static Terr FromBitmap(this Terr oldTerr, Bitmap heightmap, TargetHeightmaps targetHmaps) { if (heightmap.Width != oldTerr.Width || heightmap.Height != oldTerr.Height) { throw new ArgumentException( "Heightmap measures wrong. Expected: " + oldTerr.Width + "x" + oldTerr.Height + " (Image: " + heightmap.Width + "x" + heightmap.Height + ")"); } if ((targetHmaps & (TargetHeightmaps.FirstHeightmap | TargetHeightmaps.SecondHeightmap)) == TargetHeightmaps.None) { throw new ArgumentException("No valid TargetHeightmap specified"); } oldTerr = oldTerr.Decompress(); bool keepFirstHmap = (targetHmaps & TargetHeightmaps.FirstHeightmap) == TargetHeightmaps.None; bool keepSecondHmap = (targetHmaps & TargetHeightmaps.SecondHeightmap) == TargetHeightmaps.None; Terr newTerr = new Terr(); newTerr.Width = oldTerr.Width; newTerr.Height = oldTerr.Height; if (keepFirstHmap) { oldTerr.CopyFirstHmap(newTerr); } // This scary loop iterates row by row over all 8x8 blocks for (int h = 0; h < heightmap.Height; h += 8) { for (int w = 0; w < heightmap.Width; w += 8) { // Single 8x8 Block Terrblock block = new Terrblock(); if (!keepFirstHmap) { newTerr.BlocksHmap1.Add(block); } if (!keepSecondHmap) { newTerr.BlocksHmap2.Add(block); } int minHeight = int.MaxValue; byte[] newOffsets = new byte[64]; for (int y = 0; y < 8; ++y) { if (h + y >= heightmap.Height) { // Image height not multiple of 8 break; } for (int x = 0; x < 8; ++x) { if (w + x >= heightmap.Width) { // Image width not multiple of 8 break; } int height = heightmap.GetPixel(w + x, h + y).R; minHeight = Math.Min(minHeight, height); newOffsets[x + y * 8] = (byte)height; } } // Adjust offsets based on minimum for (int i = 0; i < 64; ++i) { newOffsets[i] -= (byte)(minHeight); } // Scale to 65535 (65535 / 255 = 257) minHeight *= 257; newTerr.Offsets.Add(newOffsets); block.Minimum = minHeight; block.OffsetIndex = newTerr.Offsets.Count - 1; } } if (keepSecondHmap) { oldTerr.CopySecondHmap(newTerr); } return newTerr; }
private static void decompressBlock(Terr oldTerr, Terr newTerr, Terrblock block, bool firstHmap) { Terrblock newBlock = new Terrblock(); newBlock.Minimum = block.Minimum; newBlock.OffsetIndex = newTerr.BlocksHmap1.Count + newTerr.BlocksHmap2.Count; byte[] newOffsets = new byte[64]; byte[] oldOffsets = oldTerr.Offsets[block.OffsetIndex]; Array.Copy(oldOffsets, newOffsets, 64); if (firstHmap) { newTerr.BlocksHmap1.Add(newBlock); } else { newTerr.BlocksHmap2.Add(newBlock); } newTerr.Offsets.Add(newOffsets); }
/// <summary> /// Takes the 2nd heightmap of the old Terr item and writes it into the new. /// Assumes that old Terr is uncompressed and new Terr has no 2nd heightmap yet. /// </summary> /// <param name="oldTerr"></param> /// <param name="newTerr"></param> private static void CopySecondHmap(this Terr oldTerr, Terr newTerr) { foreach (Terrblock block in oldTerr.BlocksHmap2) { Terrblock newBlock = new Terrblock(block); newTerr.BlocksHmap2.Add(newBlock); } // It's decompressed, so the second half of the offsets references the second hmap for (int i = oldTerr.BlocksHmap1.Count; i < oldTerr.Offsets.Count; ++i) { byte[] newOffsets = new byte[64]; Array.Copy(oldTerr.Offsets[i], newOffsets, 64); newTerr.Offsets.Add(newOffsets); } }
/// <summary> /// Simply search through all offsets and check if any of them is a /// 100% match, in this case reference the same offset block. /// Naive algorithm, but fast enough in practise... /// </summary> private static void compressBlock(Terr oldTerr, Terr newTerr, Terrblock block, bool firstHmap) { Terrblock newBlock = new Terrblock(); newBlock.Minimum = block.Minimum; if (firstHmap) { newTerr.BlocksHmap1.Add(newBlock); } else { newTerr.BlocksHmap2.Add(newBlock); } byte[] oldOffsets = oldTerr.Offsets[block.OffsetIndex]; int count = 0; foreach (var newOffset in newTerr.Offsets) // outer { for (int i = 0; i < 64; ++i) { if (newOffset[i] != oldOffsets[i]) { goto next; // outer continue } } // Block matches newBlock.OffsetIndex = count; return; next: ++count; } // No Block matches byte[] newOffsets = new byte[64]; Array.Copy(oldOffsets, newOffsets, 64); newTerr.Offsets.Add(newOffsets); newBlock.OffsetIndex = newTerr.Offsets.Count - 1; }
/// <summary> /// Swaps the content of the two heightmaps in a terr block /// </summary> /// <param name="oldTerr">Source terr</param> /// <returns>New terr with swapped heightmaps</returns> public static Terr Swap(this Terr oldTerr) { Terr newTerr = new Terr(); newTerr.Width = oldTerr.Width; newTerr.Height = oldTerr.Height; foreach (Terrblock block in oldTerr.BlocksHmap1) { Terrblock newBlock = new Terrblock(block); newTerr.BlocksHmap2.Add(newBlock); } foreach (Terrblock block in oldTerr.BlocksHmap2) { Terrblock newBlock = new Terrblock(block); newTerr.BlocksHmap1.Add(newBlock); } foreach (byte[] oldOffsets in oldTerr.Offsets) { byte[] newOffsets = new byte[64]; Array.Copy(oldOffsets, newOffsets, 64); newTerr.Offsets.Add(newOffsets); } return newTerr; }