From 4f98f03cfa748dd176081d95b6c1590df2cd4e81 Mon Sep 17 00:00:00 2001 From: Adrian Date: Sun, 14 Feb 2016 21:03:01 +0100 Subject: [PATCH 1/5] Small refactoring --- .../AdrianKousz.GenieEngine.Data/AIData.cs | 2 +- src/lib/AdrianKousz.GenieEngine/CpnReader.cs | 2 +- src/lib/AdrianKousz.GenieEngine/ScnFactory.cs | 20 +++++----- src/lib/AdrianKousz.GenieEngine/ScnReader.cs | 40 +++++++++---------- src/lib/AdrianKousz.GenieEngine/ScnWriter.cs | 4 +- 5 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/lib/AdrianKousz.GenieEngine.Data/AIData.cs b/src/lib/AdrianKousz.GenieEngine.Data/AIData.cs index 72bc324..21d2011 100644 --- a/src/lib/AdrianKousz.GenieEngine.Data/AIData.cs +++ b/src/lib/AdrianKousz.GenieEngine.Data/AIData.cs @@ -11,7 +11,7 @@ namespace AdrianKousz.GenieEngine.Data public String[] PlayerCustomAIs; public String[] PlayerCustomCTYs; public String[] PlayerCustomPERs; - public Byte[] RawAITypes; + public Byte[] AITypes; } } diff --git a/src/lib/AdrianKousz.GenieEngine/CpnReader.cs b/src/lib/AdrianKousz.GenieEngine/CpnReader.cs index 159d138..daf4586 100644 --- a/src/lib/AdrianKousz.GenieEngine/CpnReader.cs +++ b/src/lib/AdrianKousz.GenieEngine/CpnReader.cs @@ -12,7 +12,7 @@ namespace AdrianKousz.GenieEngine public CpnReader(Stream input) { - reader = new ExtendedBinaryReader(input, Data.Consts.Encoding); + reader = new ExtendedBinaryReader(input, Consts.Encoding); } public void ExtractFiles() diff --git a/src/lib/AdrianKousz.GenieEngine/ScnFactory.cs b/src/lib/AdrianKousz.GenieEngine/ScnFactory.cs index 7c4ead8..c6952c1 100644 --- a/src/lib/AdrianKousz.GenieEngine/ScnFactory.cs +++ b/src/lib/AdrianKousz.GenieEngine/ScnFactory.cs @@ -14,14 +14,14 @@ namespace AdrianKousz.GenieEngine result.Header = GetScenarioHeader(); - result.Players = new PlayerInfo[AdrianKousz.GenieEngine.Data.Consts.NumPlayers]; + result.Players = new PlayerInfo[Consts.NumPlayers]; result.Players.Fill(GetPlayerInfo); result.Messages = GetMessages(); result.Cinematics = GetCinematics(); result.AIData = GetAIData(); - result.Resources = new ResourceInfo[AdrianKousz.GenieEngine.Data.Consts.NumPlayers]; + result.Resources = new ResourceInfo[Consts.NumPlayers]; result.Resources.Fill(GetResourceInfo); result.Units = new List[Scenario.PlayerSections]; result.Units.Fill(() => new List()); @@ -89,20 +89,20 @@ namespace AdrianKousz.GenieEngine public AIData GetAIData() { var result = new AIData(); - result.PlayerAIs = new string[AdrianKousz.GenieEngine.Data.Consts.NumPlayers]; - result.PlayerCTYs = new string[AdrianKousz.GenieEngine.Data.Consts.NumPlayers]; - result.PlayerPERs = new string[AdrianKousz.GenieEngine.Data.Consts.NumPlayers]; - result.PlayerCustomAIs = new string[AdrianKousz.GenieEngine.Data.Consts.NumPlayers]; - result.PlayerCustomCTYs = new string[AdrianKousz.GenieEngine.Data.Consts.NumPlayers]; - result.PlayerCustomPERs = new string[AdrianKousz.GenieEngine.Data.Consts.NumPlayers]; + result.PlayerAIs = new string[Consts.NumPlayers]; + result.PlayerCTYs = new string[Consts.NumPlayers]; + result.PlayerPERs = new string[Consts.NumPlayers]; + result.PlayerCustomAIs = new string[Consts.NumPlayers]; + result.PlayerCustomCTYs = new string[Consts.NumPlayers]; + result.PlayerCustomPERs = new string[Consts.NumPlayers]; result.PlayerAIs.Fill(""); result.PlayerCTYs.Fill(""); result.PlayerPERs.Fill(""); result.PlayerCustomAIs.Fill(""); result.PlayerCustomCTYs.Fill(""); result.PlayerCustomPERs.Fill(""); - result.RawAITypes = new byte[AdrianKousz.GenieEngine.Data.Consts.NumPlayers]; - result.RawAITypes.Fill((byte)1); + result.AITypes = new byte[Consts.NumPlayers]; + result.AITypes.Fill((byte)1); return result; } diff --git a/src/lib/AdrianKousz.GenieEngine/ScnReader.cs b/src/lib/AdrianKousz.GenieEngine/ScnReader.cs index f6e6164..a62e1a3 100644 --- a/src/lib/AdrianKousz.GenieEngine/ScnReader.cs +++ b/src/lib/AdrianKousz.GenieEngine/ScnReader.cs @@ -15,7 +15,7 @@ namespace AdrianKousz.GenieEngine public ScnReader(Stream input) { - reader = new ExtendedBinaryReader(input, Data.Consts.Encoding); + reader = new ExtendedBinaryReader(input, Consts.Encoding); } #region Reader Methods @@ -28,7 +28,7 @@ namespace AdrianKousz.GenieEngine result.Header = ReadScenarioHeader(result.Header); var comp = new DeflateStream(reader.BaseStream, CompressionMode.Decompress); - reader = new ExtendedBinaryReader(comp, Data.Consts.Encoding); + reader = new ExtendedBinaryReader(comp, Consts.Encoding); result.NextId = reader.ReadInt32(); @@ -37,31 +37,31 @@ namespace AdrianKousz.GenieEngine Log.i(TAG, "Detected version {0}", version); - result.Players = FillArray(ReadPlayerInfo, result.Players, AdrianKousz.GenieEngine.Data.Consts.NumPlayers); + result.Players = FillArray(ReadPlayerInfo, result.Players, Consts.NumPlayers); result.Messages = ReadMessages(result.Messages); result.Cinematics = ReadCinematics(result.Cinematics); result.AIData = ReadAIData(result.AIData); ReadSeparator("Resources"); - result.Resources = FillArray(ReadResourceInfo, result.Resources, AdrianKousz.GenieEngine.Data.Consts.NumPlayers); + result.Resources = FillArray(ReadResourceInfo, result.Resources, Consts.NumPlayers); ReadSeparator("Victory"); result.RawGlobalVictory = reader.ReadBytes(10 * 4); - result.RawDiplomacy = reader.ReadBytes(AdrianKousz.GenieEngine.Data.Consts.NumPlayers * AdrianKousz.GenieEngine.Data.Consts.NumPlayers * 4); + result.RawDiplomacy = reader.ReadBytes(Consts.NumPlayers * Consts.NumPlayers * 4); result.RawIndividualVictory = reader.ReadBytes(11520); ReadSeparator("Player Settings"); - result.RawAlliedVictory = reader.ReadBytes(AdrianKousz.GenieEngine.Data.Consts.NumPlayers * 4); + result.RawAlliedVictory = reader.ReadBytes(Consts.NumPlayers * 4); - if (1.15f <= version) number = (AdrianKousz.GenieEngine.Data.Consts.NumPlayers * ( 20 /*Disables*/) + 3 /*Unknowns*/) * 4 /*Intsize*/; - if (1.18f <= version) number = (AdrianKousz.GenieEngine.Data.Consts.NumPlayers * (3 /*Lengths*/ + 80 /*Disables*/) + 3 /*Unknowns*/) * 4 /*Intsize*/; - if (1.30f <= version) number = (AdrianKousz.GenieEngine.Data.Consts.NumPlayers * (3 /*Lengths*/ + 180 /*Disables*/) + 3 /*Unknowns*/) * 4 /*Intsize*/; + if (1.15f <= version) number = (Consts.NumPlayers * ( 20 /*Disables*/) + 3 /*Unknowns*/) * 4 /*Intsize*/; + if (1.18f <= version) number = (Consts.NumPlayers * (3 /*Lengths*/ + 80 /*Disables*/) + 3 /*Unknowns*/) * 4 /*Intsize*/; + if (1.30f <= version) number = (Consts.NumPlayers * (3 /*Lengths*/ + 180 /*Disables*/) + 3 /*Unknowns*/) * 4 /*Intsize*/; result.RawDisables = reader.ReadBytes(number); - result.StartingAges = new int[AdrianKousz.GenieEngine.Data.Consts.NumPlayers]; + result.StartingAges = new int[Consts.NumPlayers]; result.StartingAges.Fill(reader.ReadInt32); ReadSeparator("Map"); @@ -168,20 +168,20 @@ namespace AdrianKousz.GenieEngine { result = Misc.MayNew(result); - result.PlayerAIs = new string[AdrianKousz.GenieEngine.Data.Consts.NumPlayers]; - result.PlayerCTYs = new string[AdrianKousz.GenieEngine.Data.Consts.NumPlayers]; - result.PlayerPERs = new string[AdrianKousz.GenieEngine.Data.Consts.NumPlayers]; - result.PlayerCustomAIs = new string[AdrianKousz.GenieEngine.Data.Consts.NumPlayers]; - result.PlayerCustomCTYs = new string[AdrianKousz.GenieEngine.Data.Consts.NumPlayers]; - result.PlayerCustomPERs = new string[AdrianKousz.GenieEngine.Data.Consts.NumPlayers]; - result.RawAITypes = new byte[AdrianKousz.GenieEngine.Data.Consts.NumPlayers]; + result.PlayerAIs = new string[Consts.NumPlayers]; + result.PlayerCTYs = new string[Consts.NumPlayers]; + result.PlayerPERs = new string[Consts.NumPlayers]; + result.PlayerCustomAIs = new string[Consts.NumPlayers]; + result.PlayerCustomCTYs = new string[Consts.NumPlayers]; + result.PlayerCustomPERs = new string[Consts.NumPlayers]; + result.AITypes = new byte[Consts.NumPlayers]; result.PlayerAIs.Fill(reader.ReadStringInt16); result.PlayerCTYs.Fill(reader.ReadStringInt16); result.PlayerPERs.Fill(reader.ReadStringInt16); int l1, l2, l3; - for (var i = 0; i < AdrianKousz.GenieEngine.Data.Consts.NumPlayers; ++i) { + for (var i = 0; i < Consts.NumPlayers; ++i) { l1 = reader.ReadInt32(); l2 = reader.ReadInt32(); l3 = reader.ReadInt32(); @@ -191,7 +191,7 @@ namespace AdrianKousz.GenieEngine } if (1.18f <= version) { - result.RawAITypes.Fill(reader.ReadByte); + result.AITypes.Fill(reader.ReadByte); } return result; @@ -279,7 +279,7 @@ namespace AdrianKousz.GenieEngine public void ReadSeparator(string name) { int v = reader.ReadInt32(); - if (v != AdrianKousz.GenieEngine.Data.Consts.Separator) { + if (v != Consts.Separator) { var msg = "Separator \"{0}\" = {1}"; msg = string.Format(msg, name, v); throw new InvalidDataException(msg); diff --git a/src/lib/AdrianKousz.GenieEngine/ScnWriter.cs b/src/lib/AdrianKousz.GenieEngine/ScnWriter.cs index f4876c5..0d3b79b 100644 --- a/src/lib/AdrianKousz.GenieEngine/ScnWriter.cs +++ b/src/lib/AdrianKousz.GenieEngine/ScnWriter.cs @@ -12,7 +12,7 @@ namespace AdrianKousz.GenieEngine public ScnWriter(Stream input) { - writer = new ExtendedBinaryWriter(input, Data.Consts.Encoding); + writer = new ExtendedBinaryWriter(input, Consts.Encoding); } public void Write(ScenarioHeader value) @@ -29,7 +29,7 @@ namespace AdrianKousz.GenieEngine public void WriteSeperator() { - writer.Write(AdrianKousz.GenieEngine.Data.Consts.Separator); + writer.Write(Consts.Separator); } public void Flush() From b65cbcd9808720aa32dd53fecc144251fb288ae4 Mon Sep 17 00:00:00 2001 From: Adrian Date: Mon, 15 Feb 2016 02:10:16 +0100 Subject: [PATCH 2/5] ScnWriter completed Additionally: - Use a factory to create empty objects - Move constants to Scenario class - Scenario class is in serialization order - Some refactoring The code is untested, because DeflateStream does not write using Flush. --- .../Cinematics.cs | 4 + .../AdrianKousz.GenieEngine.Data/Consts.cs | 14 - .../GlobalVictoryInfo.cs | 19 ++ .../AdrianKousz.GenieEngine.Data/Scenario.cs | 33 ++- .../AdrianKousz.GenieEngine.Data/UnitInfo.cs | 3 +- src/lib/AdrianKousz.GenieEngine/CpnReader.cs | 2 +- .../AdrianKousz.GenieEngine/IScnFactory.cs | 20 ++ src/lib/AdrianKousz.GenieEngine/ScnConvert.cs | 22 ++ .../ScnDefaultFactory.cs | 148 ++++++++++ src/lib/AdrianKousz.GenieEngine/ScnFactory.cs | 137 ---------- src/lib/AdrianKousz.GenieEngine/ScnReader.cs | 257 +++++++++--------- src/lib/AdrianKousz.GenieEngine/ScnWriter.cs | 226 ++++++++++++++- src/lib/GenieEngineLib.csproj | 6 +- 13 files changed, 598 insertions(+), 293 deletions(-) delete mode 100644 src/lib/AdrianKousz.GenieEngine.Data/Consts.cs create mode 100644 src/lib/AdrianKousz.GenieEngine.Data/GlobalVictoryInfo.cs create mode 100644 src/lib/AdrianKousz.GenieEngine/IScnFactory.cs create mode 100644 src/lib/AdrianKousz.GenieEngine/ScnConvert.cs create mode 100644 src/lib/AdrianKousz.GenieEngine/ScnDefaultFactory.cs delete mode 100644 src/lib/AdrianKousz.GenieEngine/ScnFactory.cs diff --git a/src/lib/AdrianKousz.GenieEngine.Data/Cinematics.cs b/src/lib/AdrianKousz.GenieEngine.Data/Cinematics.cs index 33ff9d7..25411b9 100644 --- a/src/lib/AdrianKousz.GenieEngine.Data/Cinematics.cs +++ b/src/lib/AdrianKousz.GenieEngine.Data/Cinematics.cs @@ -9,6 +9,10 @@ namespace AdrianKousz.GenieEngine.Data public String CinemaLossFn; public String BackgroundFn; + public Int32 BitmapWidth; + public Int32 BitmapHeight; + public Int16 BitmapUnknown; + public Byte[] RawBitmap; } } diff --git a/src/lib/AdrianKousz.GenieEngine.Data/Consts.cs b/src/lib/AdrianKousz.GenieEngine.Data/Consts.cs deleted file mode 100644 index 9c8c3ad..0000000 --- a/src/lib/AdrianKousz.GenieEngine.Data/Consts.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Text; - -namespace AdrianKousz.GenieEngine.Data -{ - public static class Consts - { - public const Int32 Separator = -99; - public const Int32 NumPlayers = 16; - public const Int32 NumUnitSections = 9; - public static readonly Encoding Encoding = Encoding.GetEncoding(1252); - public static readonly Char[] NullChar = new Char[] { '\0' }; - } -} diff --git a/src/lib/AdrianKousz.GenieEngine.Data/GlobalVictoryInfo.cs b/src/lib/AdrianKousz.GenieEngine.Data/GlobalVictoryInfo.cs new file mode 100644 index 0000000..eb7a80b --- /dev/null +++ b/src/lib/AdrianKousz.GenieEngine.Data/GlobalVictoryInfo.cs @@ -0,0 +1,19 @@ +using System; + +namespace AdrianKousz.GenieEngine.Data +{ + public class GlobalVictoryInfo + { + public Boolean RequireConquest; + public Int32 Ruins; + public Int32 Artifacts; + public Int32 Discovery; + public Int32 PercentExplored; + public Int32 Unknown; + public Boolean RequireAllCustom; + public Int32 Mode; + public Int32 Score; + public Int32 Time; + } +} + diff --git a/src/lib/AdrianKousz.GenieEngine.Data/Scenario.cs b/src/lib/AdrianKousz.GenieEngine.Data/Scenario.cs index abd8e66..9d660cd 100644 --- a/src/lib/AdrianKousz.GenieEngine.Data/Scenario.cs +++ b/src/lib/AdrianKousz.GenieEngine.Data/Scenario.cs @@ -1,12 +1,15 @@ using System; using System.Collections.Generic; -using AdrianKousz.GenieEngine.Data; +using System.Text; namespace AdrianKousz.GenieEngine.Data { public class Scenario { - public const Int32 PlayerSections = 9; + public const Int32 Separator = -99; + public const Int32 NumPlayers = 16; + public const Int32 NumPlayerSections = 9; + public static readonly Encoding Encoding = Encoding.GetEncoding(1252); public ScenarioHeader Header; @@ -19,18 +22,30 @@ namespace AdrianKousz.GenieEngine.Data public Messages Messages; public Cinematics Cinematics; public AIData AIData; + + // Seperator "Resources" + public ResourceInfo[] Resources; + + // Seperator "Victory" + + public GlobalVictoryInfo GlobalVictory; + public Int32[] LinearDiplomacy; + public Byte[] RawIndividualVictory; + + // Seperator "Player Settings" + + public Int32[] AlliedVictory; + public Byte[] RawDisables; + public Int32[] StartingAges; + + // Seperator "Map" + public Map Map; public ResourceInfoCopy[] ResourcesCopy; public IList[] Units; - public Byte[] RawGlobalVictory; - public Byte[] RawDiplomacy; - public Byte[] RawIndividualVictory; - public Byte[] RawAlliedVictory; - public Byte[] RawDisables; - - public Int32[] StartingAges; + public byte[] RawRemaining; } } diff --git a/src/lib/AdrianKousz.GenieEngine.Data/UnitInfo.cs b/src/lib/AdrianKousz.GenieEngine.Data/UnitInfo.cs index 295c603..52f51e0 100644 --- a/src/lib/AdrianKousz.GenieEngine.Data/UnitInfo.cs +++ b/src/lib/AdrianKousz.GenieEngine.Data/UnitInfo.cs @@ -4,7 +4,8 @@ namespace AdrianKousz.GenieEngine.Data { public class UnitInfo { - public const Single ExpectedUnknown = 2f; + public const Single ExpectedUnknown1 = 2f; + public const Byte ExpectedUnknown2 = 2; public Single PosX; public Single PosY; diff --git a/src/lib/AdrianKousz.GenieEngine/CpnReader.cs b/src/lib/AdrianKousz.GenieEngine/CpnReader.cs index daf4586..197ebc0 100644 --- a/src/lib/AdrianKousz.GenieEngine/CpnReader.cs +++ b/src/lib/AdrianKousz.GenieEngine/CpnReader.cs @@ -12,7 +12,7 @@ namespace AdrianKousz.GenieEngine public CpnReader(Stream input) { - reader = new ExtendedBinaryReader(input, Consts.Encoding); + reader = new ExtendedBinaryReader(input, Scenario.Encoding); } public void ExtractFiles() diff --git a/src/lib/AdrianKousz.GenieEngine/IScnFactory.cs b/src/lib/AdrianKousz.GenieEngine/IScnFactory.cs new file mode 100644 index 0000000..229a636 --- /dev/null +++ b/src/lib/AdrianKousz.GenieEngine/IScnFactory.cs @@ -0,0 +1,20 @@ +using System; +using AdrianKousz.GenieEngine.Data; + +namespace AdrianKousz.GenieEngine +{ + public interface IScnFactory + { + Scenario MakeScenario(bool deep); + ScenarioHeader MakeScenarioHeader(); + PlayerInfo MakePlayerInfo(); + Messages MakeMessages(); + Cinematics MakeCinematics(); + AIData MakeAIData(); + ResourceInfo MakeResourceInfo(); + ResourceInfoCopy MakeResourceInfoCopy(); + GlobalVictoryInfo MakeGlobalVictoryInfo(); + Map MakeMap(); + UnitInfo MakeUnitInfo(); + } +} diff --git a/src/lib/AdrianKousz.GenieEngine/ScnConvert.cs b/src/lib/AdrianKousz.GenieEngine/ScnConvert.cs new file mode 100644 index 0000000..a0375bb --- /dev/null +++ b/src/lib/AdrianKousz.GenieEngine/ScnConvert.cs @@ -0,0 +1,22 @@ +using System; +using AdrianKousz.Util; +using AdrianKousz.GenieEngine.Data; + +namespace AdrianKousz.GenieEngine +{ + public class ScnConvert + { + public void Convert(Scenario value, float version) + { + + } + public void Convert(UnitInfo value, float from, float to) + { + if (from < 1.18f && to >= 1.18f) { + value.InitialFrame = 0; + value.GarrisonnedInId = -1; + } + } + } +} + diff --git a/src/lib/AdrianKousz.GenieEngine/ScnDefaultFactory.cs b/src/lib/AdrianKousz.GenieEngine/ScnDefaultFactory.cs new file mode 100644 index 0000000..57fd10d --- /dev/null +++ b/src/lib/AdrianKousz.GenieEngine/ScnDefaultFactory.cs @@ -0,0 +1,148 @@ +using System.Collections.Generic; +using AdrianKousz.Util; +using AdrianKousz.GenieEngine.Data; + +namespace AdrianKousz.GenieEngine +{ + public class ScnDefaultFactory : IScnFactory + { + public Scenario MakeScenario(bool deep) + { + var result = new Scenario(); + result.OriginalVersion = 0; + result.NextId = 1; + + result.Units = new IList[Scenario.NumPlayerSections] + .Fill(() => new List()); + + return result; + } + + public ScenarioHeader MakeScenarioHeader() + { + var result = new ScenarioHeader(); + result.Unknown1 = ScenarioHeader.ExpectedUnknown1; + result.Unknown2 = ScenarioHeader.ExpectedUnknown2; + result.OriginalVersion = "0.00"; + result.Timestamp = System.DateTime.Now; + result.Instructions = ""; + result.PlayerCount = 2; + return result; + } + + public PlayerInfo MakePlayerInfo() + { + var result = new PlayerInfo(); + result.Unknown = PlayerInfo.ExpectedUnknown; + result.Name = "Player"; + result.StringTable = -1; + result.Active = false; + result.Human = false; + result.Civ = 0; + return result; + } + + public Messages MakeMessages() + { + var result = new Messages(); + result.Unknown1 = Messages.ExpectedUnknown1; + result.Unknown2 = Messages.ExpectedUnknown2; + result.Unknown3 = Messages.ExpectedUnknown3; + result.OriginalFilename = ""; + result.TextInstructions = ""; + result.TextHints = ""; + result.TextVictory = ""; + result.TextLoss = ""; + result.TextHistory = ""; + result.TextScouts = ""; + result.StringTableInstructions = -1; + result.StringTableHints = -1; + result.StringTableVictory = -1; + result.StringTableLoss = -1; + result.StringTableHistory = -1; + result.StringTableScouts = -1; + + return result; + } + + public Cinematics MakeCinematics() + { + var result = new Cinematics(); + result.CinemaPregameFn = ""; + result.CinemaVictoryFn = ""; + result.CinemaLossFn = ""; + result.BackgroundFn = ""; + result.RawBitmap = new byte[0]; + return result; + } + + public AIData MakeAIData() + { + var result = new AIData(); + result.PlayerAIs = new string[Scenario.NumPlayers].Fill(""); + result.PlayerCTYs = new string[Scenario.NumPlayers].Fill(""); + result.PlayerPERs = new string[Scenario.NumPlayers].Fill(""); + result.PlayerCustomAIs = new string[Scenario.NumPlayers].Fill(""); + result.PlayerCustomCTYs = new string[Scenario.NumPlayers].Fill(""); + result.PlayerCustomPERs = new string[Scenario.NumPlayers].Fill(""); + result.AITypes = new byte[Scenario.NumPlayers].Fill((byte)1); + return result; + } + + public GlobalVictoryInfo MakeGlobalVictoryInfo() + { + var result = new GlobalVictoryInfo(); + result.RequireConquest = true; + result.RequireAllCustom = false; + return result; + } + + public ResourceInfo MakeResourceInfo() + { + // AOE2 standard + var result = new ResourceInfo(); + result.Unknown = ResourceInfo.ExpectedUnknown; + result.Food = 200; + result.Wood = 200; + result.Gold = 100; + result.Stone = 200; + return result; + } + + public ResourceInfoCopy MakeResourceInfoCopy() + { + // AOE2 standard + var result = new ResourceInfoCopy(); + result.Unknown = ResourceInfoCopy.ExpectedUnknown; + result.Food = 200; + result.Wood = 200; + result.Gold = 100; + result.Stone = 200; + result.PopulationLimit = 75; + return result; + } + + public Map MakeMap() + { + var result = new Map(); + var size = 144; + result.CameraX = -1; + result.CameraY = -1; + result.GeneratorId = 2; + result.SizeX = size; + result.SizeY = size; + result.LinearTiles = new Map.Tile[size*size].Fill(() => new Map.Tile(0, 1, 0)); + return result; + } + + public UnitInfo MakeUnitInfo() + { + var result = new UnitInfo(); + result.Unknown1 = UnitInfo.ExpectedUnknown1; + result.Unknown2 = UnitInfo.ExpectedUnknown2; + result.GarrisonnedInId = -1; + return result; + } + } +} + diff --git a/src/lib/AdrianKousz.GenieEngine/ScnFactory.cs b/src/lib/AdrianKousz.GenieEngine/ScnFactory.cs deleted file mode 100644 index c6952c1..0000000 --- a/src/lib/AdrianKousz.GenieEngine/ScnFactory.cs +++ /dev/null @@ -1,137 +0,0 @@ -using System.Collections.Generic; -using AdrianKousz.Util; -using AdrianKousz.GenieEngine.Data; - -namespace AdrianKousz.GenieEngine -{ - public class ScnFactory - { - public Scenario GetScenario() - { - var result = new Scenario(); - result.OriginalVersion = -1; - result.NextId = 1; - - result.Header = GetScenarioHeader(); - - result.Players = new PlayerInfo[Consts.NumPlayers]; - result.Players.Fill(GetPlayerInfo); - - result.Messages = GetMessages(); - result.Cinematics = GetCinematics(); - result.AIData = GetAIData(); - - result.Resources = new ResourceInfo[Consts.NumPlayers]; - result.Resources.Fill(GetResourceInfo); - result.Units = new List[Scenario.PlayerSections]; - result.Units.Fill(() => new List()); - - return result; - } - - public ScenarioHeader GetScenarioHeader() - { - var result = new ScenarioHeader(); - result.Unknown1 = ScenarioHeader.ExpectedUnknown1; - result.Unknown2 = ScenarioHeader.ExpectedUnknown2; - result.OriginalVersion = "0.99"; - result.Timestamp = System.DateTime.Now; - result.Instructions = ""; - result.PlayerCount = 0; - return result; - } - - public PlayerInfo GetPlayerInfo(int number) - { - var result = new PlayerInfo(); - result.Unknown = PlayerInfo.ExpectedUnknown; - result.Name = "Player " + number; - result.StringTable = -1; - result.Active = true; - result.Human = true; - result.Civ = number; - return result; - } - - public Messages GetMessages() - { - var result = new Messages(); - result.Unknown1 = Messages.ExpectedUnknown1; - result.Unknown2 = Messages.ExpectedUnknown2; - result.Unknown3 = Messages.ExpectedUnknown3; - result.OriginalFilename = ""; - result.TextInstructions = ""; - result.TextHints = ""; - result.TextVictory = ""; - result.TextLoss = ""; - result.TextHistory = ""; - result.TextScouts = ""; - result.StringTableInstructions = -1; - result.StringTableHints = -1; - result.StringTableVictory = -1; - result.StringTableLoss = -1; - result.StringTableHistory = -1; - result.StringTableScouts = -1; - - return result; - } - - public Cinematics GetCinematics() - { - var result = new Cinematics(); - result.CinemaPregameFn = ""; - result.CinemaVictoryFn = ""; - result.CinemaLossFn = ""; - result.BackgroundFn = ""; - return result; - } - - public AIData GetAIData() - { - var result = new AIData(); - result.PlayerAIs = new string[Consts.NumPlayers]; - result.PlayerCTYs = new string[Consts.NumPlayers]; - result.PlayerPERs = new string[Consts.NumPlayers]; - result.PlayerCustomAIs = new string[Consts.NumPlayers]; - result.PlayerCustomCTYs = new string[Consts.NumPlayers]; - result.PlayerCustomPERs = new string[Consts.NumPlayers]; - result.PlayerAIs.Fill(""); - result.PlayerCTYs.Fill(""); - result.PlayerPERs.Fill(""); - result.PlayerCustomAIs.Fill(""); - result.PlayerCustomCTYs.Fill(""); - result.PlayerCustomPERs.Fill(""); - result.AITypes = new byte[Consts.NumPlayers]; - result.AITypes.Fill((byte)1); - return result; - } - - public ResourceInfo GetResourceInfo() - { - // AOE2 standard - var result = new ResourceInfo(); - result.Unknown = ResourceInfo.ExpectedUnknown; - result.Food = 200; - result.Wood = 200; - result.Gold = 100; - result.Stone = 200; - result.Ore = 0; - return result; - } - - public Map GetMap() - { - var result = new Map(); - var size = 200; - result.CameraX = 0; - result.CameraY = 0; - result.GeneratorId = 0; - result.SizeX = size; - result.SizeY = size; - result.LinearTiles = new Map.Tile[size*size]; - result.LinearTiles.Fill(); - return result; - } - } -} - diff --git a/src/lib/AdrianKousz.GenieEngine/ScnReader.cs b/src/lib/AdrianKousz.GenieEngine/ScnReader.cs index a62e1a3..227a9f1 100644 --- a/src/lib/AdrianKousz.GenieEngine/ScnReader.cs +++ b/src/lib/AdrianKousz.GenieEngine/ScnReader.cs @@ -8,81 +8,86 @@ namespace AdrianKousz.GenieEngine { public class ScnReader { - private static readonly string TAG = typeof(ScnReader).Name; + public float Version = 0; - private float version = 0.99f; private ExtendedBinaryReader reader; + private IScnFactory factory; - public ScnReader(Stream input) + public ScnReader(Stream input) : this (input, new ScnDefaultFactory()) { - reader = new ExtendedBinaryReader(input, Consts.Encoding); + } + + public ScnReader(Stream input, IScnFactory factory) + { + reader = new ExtendedBinaryReader(input, Scenario.Encoding); + this.factory = factory; } #region Reader Methods - public Scenario ReadScenario(Scenario result) + public Scenario ReadScenario() { - result = Misc.MayNew(result); + var result = factory.MakeScenario(false); int number = 0; - result.Header = ReadScenarioHeader(result.Header); + result.Header = ReadScenarioHeader(); var comp = new DeflateStream(reader.BaseStream, CompressionMode.Decompress); - reader = new ExtendedBinaryReader(comp, Consts.Encoding); + reader = new ExtendedBinaryReader(comp, Scenario.Encoding); result.NextId = reader.ReadInt32(); - version = reader.ReadSingle(); - result.OriginalVersion = version; - Log.i(TAG, "Detected version {0}", version); + Version = reader.ReadSingle(); + result.OriginalVersion = Version; - - result.Players = FillArray(ReadPlayerInfo, result.Players, Consts.NumPlayers); - result.Messages = ReadMessages(result.Messages); - result.Cinematics = ReadCinematics(result.Cinematics); - result.AIData = ReadAIData(result.AIData); + result.Players = FillArray(ReadPlayerInfo, result.Players, Scenario.NumPlayers); + result.Messages = ReadMessages(); + result.Cinematics = ReadCinematics(); + result.AIData = ReadAIData(); ReadSeparator("Resources"); - result.Resources = FillArray(ReadResourceInfo, result.Resources, Consts.NumPlayers); + result.Resources = FillArray(ReadResourceInfo, result.Resources, Scenario.NumPlayers); ReadSeparator("Victory"); - result.RawGlobalVictory = reader.ReadBytes(10 * 4); - result.RawDiplomacy = reader.ReadBytes(Consts.NumPlayers * Consts.NumPlayers * 4); + result.GlobalVictory = ReadGlobalVictoryInfo(); + result.LinearDiplomacy = FillArray(reader.ReadInt32, result.LinearDiplomacy, Scenario.NumPlayers * Scenario.NumPlayers); result.RawIndividualVictory = reader.ReadBytes(11520); ReadSeparator("Player Settings"); - result.RawAlliedVictory = reader.ReadBytes(Consts.NumPlayers * 4); + result.AlliedVictory = FillArray(reader.ReadInt32, result.AlliedVictory, Scenario.NumPlayers); - if (1.15f <= version) number = (Consts.NumPlayers * ( 20 /*Disables*/) + 3 /*Unknowns*/) * 4 /*Intsize*/; - if (1.18f <= version) number = (Consts.NumPlayers * (3 /*Lengths*/ + 80 /*Disables*/) + 3 /*Unknowns*/) * 4 /*Intsize*/; - if (1.30f <= version) number = (Consts.NumPlayers * (3 /*Lengths*/ + 180 /*Disables*/) + 3 /*Unknowns*/) * 4 /*Intsize*/; + if (Version >= 1.15f) number = (Scenario.NumPlayers * ( 20 /*Disables*/) + 3 /*Unknowns*/) * 4 /*Intsize*/; + if (Version >= 1.18f) number = (Scenario.NumPlayers * (3 /*Lengths*/ + 80 /*Disables*/) + 3 /*Unknowns*/) * 4 /*Intsize*/; + if (Version >= 1.30f) number = (Scenario.NumPlayers * (3 /*Lengths*/ + 180 /*Disables*/) + 3 /*Unknowns*/) * 4 /*Intsize*/; result.RawDisables = reader.ReadBytes(number); - result.StartingAges = new int[Consts.NumPlayers]; - result.StartingAges.Fill(reader.ReadInt32); + result.StartingAges = FillArray(reader.ReadInt32, result.StartingAges, Scenario.NumPlayers); ReadSeparator("Map"); - result.Map = ReadMap(result.Map); + result.Map = ReadMap(); number = reader.ReadInt32(); result.ResourcesCopy = FillArray(ReadResourceInfoCopy, result.ResourcesCopy, number - 1); - result.Units = new IList[number]; - result.Units.Fill(ReadUnitInfo); + result.Units = FillArray(ReadUnitInfoList, result.Units, number); + + var bytestream = new MemoryStream(); + reader.BaseStream.CopyTo(bytestream); + result.RawRemaining = bytestream.ToArray(); return result; } - public ScenarioHeader ReadScenarioHeader(ScenarioHeader result) + public ScenarioHeader ReadScenarioHeader() { - result = Misc.MayNew(result); + var result = factory.MakeScenarioHeader(); result.OriginalVersion = reader.ReadString(4); - Skip(4, "Header Length"); + reader.ReadInt32(); // Header Length result.Unknown1 = reader.ReadInt32(); result.Timestamp = DateTimes.FromUnixTime(reader.ReadInt32()); result.Instructions = reader.ReadZStringInt32(); @@ -92,12 +97,12 @@ namespace AdrianKousz.GenieEngine return result; } - public PlayerInfo ReadPlayerInfo(PlayerInfo result) + public PlayerInfo ReadPlayerInfo() { - result = Misc.MayNew(result); + var result = factory.MakePlayerInfo(); result.Name = reader.ReadZString(256); - if (1.18f <= version) { + if (Version >= 1.18f) { result.StringTable = reader.ReadInt32(); } result.Active = reader.ReadBoolean(); @@ -108,23 +113,23 @@ namespace AdrianKousz.GenieEngine return result; } - public Messages ReadMessages(Messages result) + public Messages ReadMessages() { - result = Misc.MayNew(result); + var result = factory.MakeMessages(); result.Unknown1 = reader.ReadInt32(); result.Unknown2 = reader.ReadByte(); result.Unknown3 = reader.ReadSingle(); result.OriginalFilename = reader.ReadStringInt16(); - if (1.18f <= version) { + if (Version >= 1.18f) { result.StringTableInstructions = reader.ReadInt32(); result.StringTableHints = reader.ReadInt32(); result.StringTableVictory = reader.ReadInt32(); result.StringTableLoss = reader.ReadInt32(); result.StringTableHistory = reader.ReadInt32(); } - if (1.22f <= version) { + if (Version >= 1.22f) { result.StringTableScouts = reader.ReadInt32(); } @@ -133,16 +138,16 @@ namespace AdrianKousz.GenieEngine result.TextVictory = reader.ReadZStringInt16(); result.TextLoss = reader.ReadZStringInt16(); result.TextHistory = reader.ReadZStringInt16(); - if (1.22f <= version) { + if (Version >= 1.22f) { result.TextScouts = reader.ReadZStringInt16(); } return result; } - public Cinematics ReadCinematics(Cinematics result) + public Cinematics ReadCinematics() { - result = Misc.MayNew(result); + var result = factory.MakeCinematics(); result.CinemaPregameFn = reader.ReadStringInt16(); result.CinemaVictoryFn = reader.ReadStringInt16(); @@ -151,37 +156,31 @@ namespace AdrianKousz.GenieEngine var hasBitmap = reader.ReadBoolean(); - Skip(4, "Bitmap width"); - Skip(4, "Bitmap height"); - Skip(2, "Bitmap Unknown"); + result.BitmapWidth = reader.ReadInt32(); + result.BitmapHeight = reader.ReadInt32(); + result.BitmapUnknown = reader.ReadInt16(); if (hasBitmap) { result.RawBitmap = BitmapUtil.ReadRawBitmap(reader.BaseStream); - } else { - result.RawBitmap = new byte[0]; } return result; } - public AIData ReadAIData(AIData result) + public AIData ReadAIData() { - result = Misc.MayNew(result); + var result = factory.MakeAIData(); - result.PlayerAIs = new string[Consts.NumPlayers]; - result.PlayerCTYs = new string[Consts.NumPlayers]; - result.PlayerPERs = new string[Consts.NumPlayers]; - result.PlayerCustomAIs = new string[Consts.NumPlayers]; - result.PlayerCustomCTYs = new string[Consts.NumPlayers]; - result.PlayerCustomPERs = new string[Consts.NumPlayers]; - result.AITypes = new byte[Consts.NumPlayers]; + result.PlayerAIs = FillArray(reader.ReadStringInt16, result.PlayerAIs, Scenario.NumPlayers); + result.PlayerCTYs = FillArray(reader.ReadStringInt16, result.PlayerAIs, Scenario.NumPlayers); + result.PlayerPERs = FillArray(reader.ReadStringInt16, result.PlayerAIs, Scenario.NumPlayers); - result.PlayerAIs.Fill(reader.ReadStringInt16); - result.PlayerCTYs.Fill(reader.ReadStringInt16); - result.PlayerPERs.Fill(reader.ReadStringInt16); + result.PlayerCustomAIs = new string[Scenario.NumPlayers]; + result.PlayerCustomCTYs = new string[Scenario.NumPlayers]; + result.PlayerCustomPERs = new string[Scenario.NumPlayers]; int l1, l2, l3; - for (var i = 0; i < Consts.NumPlayers; ++i) { + for (var i = 0; i < Scenario.NumPlayers; ++i) { l1 = reader.ReadInt32(); l2 = reader.ReadInt32(); l3 = reader.ReadInt32(); @@ -190,88 +189,112 @@ namespace AdrianKousz.GenieEngine result.PlayerCustomPERs[i] = reader.ReadString(l3); } - if (1.18f <= version) { - result.AITypes.Fill(reader.ReadByte); + if (Version >= 1.18f) { + result.AITypes = FillArray(reader.ReadByte, result.AITypes, Scenario.NumPlayers); } return result; } - public ResourceInfo ReadResourceInfo(ResourceInfo result) + public ResourceInfo ReadResourceInfo() { - result = Misc.MayNew(result); + var result = factory.MakeResourceInfo(); result.Gold = reader.ReadInt32(); result.Wood = reader.ReadInt32(); result.Food = reader.ReadInt32(); result.Stone = reader.ReadInt32(); - if (1.18f <= version) { + if (Version >= 1.18f) { result.Ore = reader.ReadInt32(); - result.Unknown = reader.ReadInt32(); + if (Version < 1.3f) { + result.Unknown = reader.ReadInt32(); + } } return result; } - public Map ReadMap(Map result) + public ResourceInfoCopy ReadResourceInfoCopy() { - result = Misc.MayNew(result); - - if (1.18f <= version) { - result.CameraX = reader.ReadInt32(); - result.CameraY = reader.ReadInt32(); - } - if (1.22f <= version) { - result.GeneratorId = reader.ReadInt32(); - } - - result.SizeX = reader.ReadInt32(); - result.SizeY = reader.ReadInt32(); - - result.LinearTiles = new Map.Tile[result.SizeX * result.SizeY]; - result.LinearTiles.Fill(() => new Map.Tile(reader.ReadByte(), reader.ReadByte(), reader.ReadByte())); - - return result; - } - - public ResourceInfoCopy ReadResourceInfoCopy(ResourceInfoCopy result) - { - result = Misc.MayNew(result); + var result = factory.MakeResourceInfoCopy(); result.Food = reader.ReadSingle(); result.Wood = reader.ReadSingle(); result.Gold = reader.ReadSingle(); result.Stone = reader.ReadSingle(); - if (1.18f <= version) { + if (Version >= 1.18f) { result.Ore = reader.ReadInt32(); - result.Unknown = reader.ReadInt32(); + if (Version < 1.3f) { + result.Unknown = reader.ReadInt32(); + } } - if (1.22f <= version) { + if (Version >= 1.22f) { result.PopulationLimit = reader.ReadSingle(); } return result; } - public IList ReadUnitInfo() + public GlobalVictoryInfo ReadGlobalVictoryInfo() { - var number = reader.ReadInt32(); - var result = new List(number); + var result = factory.MakeGlobalVictoryInfo(); - for (var i = 0; i < number; i++) { - var unit = new UnitInfo(); - unit.PosX = reader.ReadSingle(); - unit.PosY = reader.ReadSingle(); - unit.Unknown1 = reader.ReadSingle(); - unit.Id = reader.ReadInt32(); - unit.UnitId = reader.ReadInt16(); - unit.Unknown2 = reader.ReadByte(); - unit.Rotation = reader.ReadSingle(); - if (1.18f <= version) { - unit.InitialFrame = reader.ReadInt16(); - unit.GarrisonnedInId = reader.ReadInt32(); - } - result.Add(unit); + result.RequireConquest = reader.ReadBoolean(); + result.Ruins = reader.ReadInt32(); + result.Artifacts = reader.ReadInt32(); + result.Discovery = reader.ReadInt32(); + result.PercentExplored = reader.ReadInt32(); + result.Unknown = reader.ReadInt32(); + result.RequireAllCustom = reader.ReadBoolean(); + result.Mode = reader.ReadInt32(); + result.Score = reader.ReadInt32(); + result.Time = reader.ReadInt32(); + + return result; + } + + public Map ReadMap() + { + var result = factory.MakeMap(); + + if (Version >= 1.18f) { + result.CameraX = reader.ReadInt32(); + result.CameraY = reader.ReadInt32(); + } + if (Version >= 1.22f) { + result.GeneratorId = reader.ReadInt32(); + } + + result.SizeX = reader.ReadInt32(); + result.SizeY = reader.ReadInt32(); + + result.LinearTiles = new Map.Tile[result.SizeX * result.SizeY] + .Fill(() => new Map.Tile(reader.ReadByte(), reader.ReadByte(), reader.ReadByte())); + + return result; + } + + public IList ReadUnitInfoList() + { + var count = reader.ReadInt32(); + var result = new List().Fill(ReadUnitInfo, count); + return result; + } + + public UnitInfo ReadUnitInfo() + { + var result = factory.MakeUnitInfo(); + + result.PosX = reader.ReadSingle(); + result.PosY = reader.ReadSingle(); + result.Unknown1 = reader.ReadSingle(); + result.Id = reader.ReadInt32(); + result.UnitId = reader.ReadInt16(); + result.Unknown2 = reader.ReadByte(); + result.Rotation = reader.ReadSingle(); + if (Version >= 1.18f) { + result.InitialFrame = reader.ReadInt16(); + result.GarrisonnedInId = reader.ReadInt32(); } return result; @@ -279,7 +302,7 @@ namespace AdrianKousz.GenieEngine public void ReadSeparator(string name) { int v = reader.ReadInt32(); - if (v != Consts.Separator) { + if (v != Scenario.Separator) { var msg = "Separator \"{0}\" = {1}"; msg = string.Format(msg, name, v); throw new InvalidDataException(msg); @@ -290,26 +313,14 @@ namespace AdrianKousz.GenieEngine #region Debug Methods - private T[] FillArray(System.Func f, T[] array, int length) where T : new() + private T[] FillArray(System.Func f, T[] array, int length) { - if (array == null) { + if (array == null || array.Length != length) array = new T[length]; - } else if (array.Length != length) { - var temp = new T[length]; - System.Array.Copy(array, temp, System.Math.Min(array.Length, temp.Length)); - } - array.Fill(f); - return array; } - private void Skip(int count, string name) - { - Log.d(TAG, "Skipping {0} bytes in \"{1}\"", count, name); - reader.ReadBytes(count); - } - #endregion } } diff --git a/src/lib/AdrianKousz.GenieEngine/ScnWriter.cs b/src/lib/AdrianKousz.GenieEngine/ScnWriter.cs index 0d3b79b..eaed0e3 100644 --- a/src/lib/AdrianKousz.GenieEngine/ScnWriter.cs +++ b/src/lib/AdrianKousz.GenieEngine/ScnWriter.cs @@ -1,4 +1,6 @@ using System.IO; +using System.IO.Compression; +using System.Collections.Generic; using AdrianKousz.Util; using AdrianKousz.GenieEngine.Data; @@ -6,18 +8,65 @@ namespace AdrianKousz.GenieEngine { public class ScnWriter { - private static readonly string TAG = typeof(ScnWriter).Name; + public float Version = 0; private ExtendedBinaryWriter writer; - public ScnWriter(Stream input) + public ScnWriter(Stream output) { - writer = new ExtendedBinaryWriter(input, Consts.Encoding); + writer = new ExtendedBinaryWriter(output, Scenario.Encoding); + } + + public void Write(Scenario value) + { + Version = Version == 0 ? value.OriginalVersion : Version; + + Write(value.Header); + Flush(); + + var comp = new DeflateStream(writer.BaseStream, CompressionMode.Compress); + writer = new ExtendedBinaryWriter(comp, Scenario.Encoding); + + writer.Write(value.NextId); + writer.Write(Version); + + value.Players.ForEach(Write); + Write(value.Messages); + Write(value.Cinematics); + Write(value.AIData); + + WriteSeperator("Resources"); + + value.Resources.ForEach(Write); + + WriteSeperator("Victory"); + + Write(value.GlobalVictory); + value.LinearDiplomacy.ForEach(writer.Write); + writer.Write(value.RawIndividualVictory); + + WriteSeperator("Player Settings"); + + value.AlliedVictory.ForEach(writer.Write); + writer.Write(value.RawDisables); + value.StartingAges.ForEach(writer.Write); + + WriteSeperator("Map"); + + Write(value.Map); + + writer.Write((int)value.Units.Length); + value.ResourcesCopy.ForEach(Write); + value.Units.ForEach(Write); + + writer.Write(value.RawRemaining); + + Flush(); } public void Write(ScenarioHeader value) { - var instructionlength = writer.GetByteCount(value.Instructions + " "); + var instructionlength = writer.GetByteCount(value.Instructions); writer.WriteStringRaw(value.OriginalVersion); writer.Write(20 + instructionlength); writer.Write(value.Unknown1); @@ -27,15 +76,180 @@ namespace AdrianKousz.GenieEngine writer.Write(value.PlayerCount); } - public void WriteSeperator() + public void Write(PlayerInfo value) { - writer.Write(Consts.Separator); + writer.WriteStringPadded(value.Name, 256); + if (Version >= 1.18f) { + writer.Write(value.StringTable); + } + writer.Write(value.Active); + writer.Write(value.Human); + writer.Write(value.Civ); + writer.Write(value.Unknown); + } + + public void Write(Messages value) + { + writer.Write(value.Unknown1); + writer.Write(value.Unknown2); + writer.Write(value.Unknown3); + writer.WriteStringInt16(value.OriginalFilename); + + if (Version >= 1.18f) { + writer.Write(value.StringTableInstructions); + writer.Write(value.StringTableHints); + writer.Write(value.StringTableVictory); + writer.Write(value.StringTableLoss); + writer.Write(value.StringTableHistory); + } + if (Version >= 1.22f) { + writer.Write(value.StringTableScouts); + } + + writer.WriteZStringInt16(value.TextInstructions); + writer.WriteZStringInt16(value.TextHints); + writer.WriteZStringInt16(value.TextVictory); + writer.WriteZStringInt16(value.TextLoss); + writer.WriteZStringInt16(value.TextHistory); + if (Version >= 1.22f) { + writer.WriteZStringInt16(value.TextScouts); + } + } + + public void Write(Cinematics value) + { + writer.WriteStringInt16(value.CinemaPregameFn); + writer.WriteStringInt16(value.CinemaVictoryFn); + writer.WriteStringInt16(value.CinemaLossFn); + writer.WriteStringInt16(value.BackgroundFn); + + writer.Write(value.BitmapWidth); + writer.Write(value.BitmapHeight); + writer.Write(value.BitmapUnknown); + if (value.RawBitmap != null) + writer.Write(value.RawBitmap); + } + + public void Write(AIData value) + { + value.PlayerAIs.ForEach(writer.WriteStringInt16); + value.PlayerCTYs.ForEach(writer.WriteStringInt16); + value.PlayerPERs.ForEach(writer.WriteStringInt16); + + for (var i = 0; i < Scenario.NumPlayers; ++i) { + var str1 = value.PlayerCustomAIs[i]; + var str2 = value.PlayerCustomCTYs[i]; + var str3 = value.PlayerCustomPERs[i]; + writer.Write(writer.GetByteCount(str1)); + writer.Write(writer.GetByteCount(str2)); + writer.Write(writer.GetByteCount(str3)); + writer.WriteStringRaw(str1); + writer.WriteStringRaw(str2); + writer.WriteStringRaw(str3); + } + + if (Version >= 1.18f) { + value.AITypes.ForEach(writer.Write); + } + } + + public void Write(ResourceInfo value) + { + writer.Write(value.Gold); + writer.Write(value.Wood); + writer.Write(value.Food); + writer.Write(value.Stone); + if (Version >= 1.18f) { + writer.Write(value.Ore); + if (Version < 1.3f) { + writer.Write(value.Unknown); + } + } + } + + public void Write(ResourceInfoCopy value) + { + writer.Write(value.Food); + writer.Write(value.Wood); + writer.Write(value.Gold); + writer.Write(value.Stone); + if (Version >= 1.18f) { + writer.Write(value.Ore); + if (Version < 1.3f) { + writer.Write(value.Unknown); + } + } + if (Version >= 1.22f) { + writer.Write(value.PopulationLimit); + } + } + + public void Write(GlobalVictoryInfo value) + { + writer.Write(value.RequireConquest); + writer.Write(value.Ruins); + writer.Write(value.Artifacts); + writer.Write(value.Discovery); + writer.Write(value.PercentExplored); + writer.Write(value.Unknown); + writer.Write(value.RequireAllCustom); + writer.Write(value.Mode); + writer.Write(value.Score); + writer.Write(value.Time); + } + + public void Write(Map value) + { + if (Version >= 1.18f) { + writer.Write(value.CameraX); + writer.Write(value.CameraY); + } + if (Version >= 1.22f) { + writer.Write(value.GeneratorId); + } + writer.Write(value.SizeX); + writer.Write(value.SizeY); + + value.LinearTiles.ForEach(x => { writer.Write(x.Id); writer.Write(x.Elevation); writer.Write(x.Unknown); }); + } + + public void Write(IList value) + { + int number = value.Count; + writer.Write(number); + value.ForEach(Write); + } + + public void Write(UnitInfo value) + { + writer.Write(value.PosX); + writer.Write(value.PosY); + writer.Write(value.Unknown1); + writer.Write(value.Id); + writer.Write(value.UnitId); + writer.Write(value.Unknown2); + writer.Write(value.Rotation); + if (Version >= 1.18f) { + writer.Write(value.InitialFrame); + writer.Write(value.GarrisonnedInId); + } + } + + public void WriteSeperator(string name) + { + writer.Write(Scenario.Separator); } public void Flush() { writer.Flush(); } + + private void EnsureSetVersion() + { + if (Version <= 1) + throw new System.InvalidOperationException("Set output version"); + } } } diff --git a/src/lib/GenieEngineLib.csproj b/src/lib/GenieEngineLib.csproj index c5e45d1..e62c696 100644 --- a/src/lib/GenieEngineLib.csproj +++ b/src/lib/GenieEngineLib.csproj @@ -34,13 +34,11 @@ - - @@ -50,6 +48,10 @@ + + + + From bb5ac298a450f80066251d54552cac80a763a171 Mon Sep 17 00:00:00 2001 From: Adrian Date: Mon, 15 Feb 2016 04:26:53 +0100 Subject: [PATCH 3/5] Fix some serialization bugs --- .../PlayerInfo.cs | 2 -- .../AdrianKousz.GenieEngine.Data/Scenario.cs | 2 ++ .../AdrianKousz.GenieEngine.Data/UnitInfo.cs | 2 +- .../ScnDefaultFactory.cs | 2 -- src/lib/AdrianKousz.GenieEngine/ScnReader.cs | 12 ++++----- src/lib/AdrianKousz.GenieEngine/ScnWriter.cs | 25 +++++++++++++------ 6 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/lib/AdrianKousz.GenieEngine.Data/PlayerInfo.cs b/src/lib/AdrianKousz.GenieEngine.Data/PlayerInfo.cs index f48f24a..43050b3 100644 --- a/src/lib/AdrianKousz.GenieEngine.Data/PlayerInfo.cs +++ b/src/lib/AdrianKousz.GenieEngine.Data/PlayerInfo.cs @@ -6,8 +6,6 @@ namespace AdrianKousz.GenieEngine.Data { public const Int32 ExpectedUnknown = 4; - public String Name; - public Int32 StringTable; public Boolean Active; public Boolean Human; public Int32 Civ; diff --git a/src/lib/AdrianKousz.GenieEngine.Data/Scenario.cs b/src/lib/AdrianKousz.GenieEngine.Data/Scenario.cs index 9d660cd..053e1ff 100644 --- a/src/lib/AdrianKousz.GenieEngine.Data/Scenario.cs +++ b/src/lib/AdrianKousz.GenieEngine.Data/Scenario.cs @@ -18,6 +18,8 @@ namespace AdrianKousz.GenieEngine.Data public Int32 NextId; public Single OriginalVersion; + public String[] PlayerNames; + public Int32[] StringTablePlayerNames; public PlayerInfo[] Players; public Messages Messages; public Cinematics Cinematics; diff --git a/src/lib/AdrianKousz.GenieEngine.Data/UnitInfo.cs b/src/lib/AdrianKousz.GenieEngine.Data/UnitInfo.cs index 52f51e0..c44c52e 100644 --- a/src/lib/AdrianKousz.GenieEngine.Data/UnitInfo.cs +++ b/src/lib/AdrianKousz.GenieEngine.Data/UnitInfo.cs @@ -11,7 +11,7 @@ namespace AdrianKousz.GenieEngine.Data public Single PosY; public Single Unknown1; public Int32 Id; - public Int32 UnitId; + public Int16 UnitId; public Byte Unknown2; public Single Rotation; public Int16 InitialFrame; diff --git a/src/lib/AdrianKousz.GenieEngine/ScnDefaultFactory.cs b/src/lib/AdrianKousz.GenieEngine/ScnDefaultFactory.cs index 57fd10d..ab4bf23 100644 --- a/src/lib/AdrianKousz.GenieEngine/ScnDefaultFactory.cs +++ b/src/lib/AdrianKousz.GenieEngine/ScnDefaultFactory.cs @@ -34,8 +34,6 @@ namespace AdrianKousz.GenieEngine { var result = new PlayerInfo(); result.Unknown = PlayerInfo.ExpectedUnknown; - result.Name = "Player"; - result.StringTable = -1; result.Active = false; result.Human = false; result.Civ = 0; diff --git a/src/lib/AdrianKousz.GenieEngine/ScnReader.cs b/src/lib/AdrianKousz.GenieEngine/ScnReader.cs index 227a9f1..bf7811b 100644 --- a/src/lib/AdrianKousz.GenieEngine/ScnReader.cs +++ b/src/lib/AdrianKousz.GenieEngine/ScnReader.cs @@ -40,6 +40,10 @@ namespace AdrianKousz.GenieEngine Version = reader.ReadSingle(); result.OriginalVersion = Version; + result.PlayerNames = FillArray(() => reader.ReadZString(256), result.PlayerNames, Scenario.NumPlayers); + if (Version >= 1.18f) { + result.StringTablePlayerNames = FillArray(reader.ReadInt32, result.StringTablePlayerNames, Scenario.NumPlayers); + } result.Players = FillArray(ReadPlayerInfo, result.Players, Scenario.NumPlayers); result.Messages = ReadMessages(); result.Cinematics = ReadCinematics(); @@ -101,10 +105,6 @@ namespace AdrianKousz.GenieEngine { var result = factory.MakePlayerInfo(); - result.Name = reader.ReadZString(256); - if (Version >= 1.18f) { - result.StringTable = reader.ReadInt32(); - } result.Active = reader.ReadBoolean(); result.Human = reader.ReadBoolean(); result.Civ = reader.ReadInt32(); @@ -172,8 +172,8 @@ namespace AdrianKousz.GenieEngine var result = factory.MakeAIData(); result.PlayerAIs = FillArray(reader.ReadStringInt16, result.PlayerAIs, Scenario.NumPlayers); - result.PlayerCTYs = FillArray(reader.ReadStringInt16, result.PlayerAIs, Scenario.NumPlayers); - result.PlayerPERs = FillArray(reader.ReadStringInt16, result.PlayerAIs, Scenario.NumPlayers); + result.PlayerCTYs = FillArray(reader.ReadStringInt16, result.PlayerCTYs, Scenario.NumPlayers); + result.PlayerPERs = FillArray(reader.ReadStringInt16, result.PlayerPERs, Scenario.NumPlayers); result.PlayerCustomAIs = new string[Scenario.NumPlayers]; result.PlayerCustomCTYs = new string[Scenario.NumPlayers]; diff --git a/src/lib/AdrianKousz.GenieEngine/ScnWriter.cs b/src/lib/AdrianKousz.GenieEngine/ScnWriter.cs index eaed0e3..2e78042 100644 --- a/src/lib/AdrianKousz.GenieEngine/ScnWriter.cs +++ b/src/lib/AdrianKousz.GenieEngine/ScnWriter.cs @@ -24,12 +24,20 @@ namespace AdrianKousz.GenieEngine Write(value.Header); Flush(); - var comp = new DeflateStream(writer.BaseStream, CompressionMode.Compress); + // TODO: Make wrapper. + // This is a workaround for now. A wrapper for the reader and writer will be created. + var originalWriter = writer; + var compressedPart = new MemoryStream(); + var comp = new DeflateStream(compressedPart, CompressionMode.Compress); writer = new ExtendedBinaryWriter(comp, Scenario.Encoding); writer.Write(value.NextId); writer.Write(Version); + value.PlayerNames.ForEach((x) => writer.WriteStringPadded(x, 256)); + if (Version >= 1.18f) { + value.StringTablePlayerNames.ForEach(writer.Write); + } value.Players.ForEach(Write); Write(value.Messages); Write(value.Cinematics); @@ -62,11 +70,14 @@ namespace AdrianKousz.GenieEngine writer.Write(value.RawRemaining); Flush(); + + comp.Dispose(); + originalWriter.Write(compressedPart.ToArray()); } public void Write(ScenarioHeader value) { - var instructionlength = writer.GetByteCount(value.Instructions); + var instructionlength = writer.GetZByteCount(value.Instructions); writer.WriteStringRaw(value.OriginalVersion); writer.Write(20 + instructionlength); writer.Write(value.Unknown1); @@ -78,10 +89,6 @@ namespace AdrianKousz.GenieEngine public void Write(PlayerInfo value) { - writer.WriteStringPadded(value.Name, 256); - if (Version >= 1.18f) { - writer.Write(value.StringTable); - } writer.Write(value.Active); writer.Write(value.Human); writer.Write(value.Civ); @@ -123,10 +130,14 @@ namespace AdrianKousz.GenieEngine writer.WriteStringInt16(value.CinemaLossFn); writer.WriteStringInt16(value.BackgroundFn); + var hasBitmap = value.RawBitmap.Length > 0; + + writer.Write(hasBitmap); writer.Write(value.BitmapWidth); writer.Write(value.BitmapHeight); writer.Write(value.BitmapUnknown); - if (value.RawBitmap != null) + + if (hasBitmap) writer.Write(value.RawBitmap); } From 41cb3e798b8207f597800bd949aea37a24252d51 Mon Sep 17 00:00:00 2001 From: Adrian Date: Mon, 15 Feb 2016 20:22:58 +0100 Subject: [PATCH 4/5] Rudimentary AOE1 to AOE2 converter and unit dumper --- src/cmdapp/GenieEngineApp.csproj | 7 + src/cmdapp/src/CompressCommand.cs | 2 +- src/cmdapp/src/CopymapCommand.cs | 57 +++++ src/cmdapp/src/CopymapConverter.cs | 206 ++++++++++++++++++ src/cmdapp/src/DecompressCommand.cs | 4 +- src/cmdapp/src/DumpCommand.cs | 4 +- src/cmdapp/src/DumpunitsCommand.cs | 44 ++++ src/cmdapp/src/Program.cs | 17 ++ .../AdrianKousz.GenieEngine.Data/UnitInfo.cs | 2 +- .../ScnDefaultFactory.cs | 2 + 10 files changed, 339 insertions(+), 6 deletions(-) create mode 100644 src/cmdapp/src/CopymapCommand.cs create mode 100644 src/cmdapp/src/CopymapConverter.cs create mode 100644 src/cmdapp/src/DumpunitsCommand.cs diff --git a/src/cmdapp/GenieEngineApp.csproj b/src/cmdapp/GenieEngineApp.csproj index 067681b..b3128ef 100644 --- a/src/cmdapp/GenieEngineApp.csproj +++ b/src/cmdapp/GenieEngineApp.csproj @@ -36,6 +36,9 @@ + + + @@ -50,6 +53,10 @@ {662FDCF9-3F6C-48DA-BFE9-F317BEFED692} ExUtil + + {8306E345-3A99-4F22-AF14-3CEA7425C2EF} + MainUtil + diff --git a/src/cmdapp/src/CompressCommand.cs b/src/cmdapp/src/CompressCommand.cs index df42cb1..13a012e 100644 --- a/src/cmdapp/src/CompressCommand.cs +++ b/src/cmdapp/src/CompressCommand.cs @@ -12,7 +12,7 @@ namespace AdrianKousz.GenieEngine var reader = new ScnReader(file); var writer = new ScnWriter(outfile); - var header = reader.ReadScenarioHeader(null); + var header = reader.ReadScenarioHeader(); reader.ReadSeparator("Custom"); writer.Write(header); writer.Flush(); diff --git a/src/cmdapp/src/CopymapCommand.cs b/src/cmdapp/src/CopymapCommand.cs new file mode 100644 index 0000000..3129569 --- /dev/null +++ b/src/cmdapp/src/CopymapCommand.cs @@ -0,0 +1,57 @@ +using System; +using System.IO; + +namespace AdrianKousz.GenieEngine +{ + public class CopymapCommand : BaseCommand + { + override public void Run() + { + Data.Scenario result; + using (var dststream = File.OpenRead(args[0])) + using (var srcstream = File.OpenRead(args[1])) + { + var srcreader = new ScnReader(srcstream); + var src = srcreader.ReadScenario(); + var dstreader = new ScnReader(dststream); + var dst = dstreader.ReadScenario(); + var converter = new CopymapConverter(); + dst.PlayerNames = src.PlayerNames; + dst.StringTablePlayerNames = src.StringTablePlayerNames; + dst.Players = src.Players; + dst.Resources = src.Resources; + dst.ResourcesCopy = src.ResourcesCopy; + dst.Messages = src.Messages; + converter.Convert(dst, src); + result = dst; + } + using (var outstream = File.OpenWrite(args[2])) { + var outwriter = new ScnWriter(outstream); + outwriter.Write(result); + } + } + + override public int GetArgumentCount() + { + return 3; + } + + override public string GetDescription() + { + return "Copy parts of scenarios"; + } + + override public string GetHelp() + { + return "Copy the parts below from scenario file 2 into scenario file 1." + + "\nThe result is written to scenario file 3." + + "\n- Messages" + + "\n- Player information" + + "\n- Resources" + + "\n- Map" + + "\n- Units" + ; + } + } +} + diff --git a/src/cmdapp/src/CopymapConverter.cs b/src/cmdapp/src/CopymapConverter.cs new file mode 100644 index 0000000..e752383 --- /dev/null +++ b/src/cmdapp/src/CopymapConverter.cs @@ -0,0 +1,206 @@ +using System; +using System.Collections.Generic; +using AdrianKousz.Util; +using AdrianKousz.GenieEngine.Data; + +namespace AdrianKousz.GenieEngine +{ + public class CopymapConverter + { + public void Convert(Scenario dst, Scenario src) + { + dst.Map = src.Map; + dst.Units = ChangeUnits_AOE1_AOE2(src.Units); + MoveUnits_AOE1_AOE2(dst.Units); + ChangeTiles_AOE1_AOE2(dst.Units, dst.Map); + RandomizeTrees_AOE2(dst.Units); + } + + /** + * Some buildings are not the same size. + * They need to be moved by half a unit. + */ + private void MoveUnits_AOE1_AOE2(IList[] units) + { + var mapping = new int[] { + // Towers + 79, 199, 69, 278, + // Other Buildings + 109, 68, 103, + }; + units.ForEach((i, list) => { + list.ForEach((j, unit) => { + mapping.ForEach((k, buildingid) => { + if (unit.UnitId == buildingid) { + unit.PosX -= 0.5f; + unit.PosY -= 0.5f; + } + }); + }); + }); + } + + /** + * Simple mapping of unit IDs + */ + private List[] ChangeUnits_AOE1_AOE2(IList[] units) + { + var array = new int[] { + 66, 66, + 59, 59, + 102, 102, + 264, 264, + 83, 83, + 125, 125, + + // Buildings + 72, 72, + 117, 117, + 155, 155, + 79, 598, + 199, 79, + 69, 79, + 278, 79, + 70, 70, + 159, 285, + 109, 109, + 12, 12, + 68, 68, + 103, 562, + 101, 101, + + // Trees + 141, 349, + 144, 349, + 145, 349, + 146, 349, + 197, 350, + 194, 350, + 193, 350, + 192, 350, + 135, 350, + 137, 350, + 138, 350, + 139, 350, + 161, 350, + 198, 350, + 203, 350, + 226, 350, + 391, 350, + 392, 350, + 150, 351, + 151, 351, + 152, 351, + 153, 351, + 134, 411, + 140, 411, + 148, 411, + 136, 411, + 142, 411, + 143, 411, + 147, 411, + 195, 411, + 365, 411, + 366, 411, + 367, 411, + 393, 411, + + // Fish + 260, 69, + 263, 69, + 53, 456, + 52, 457, + 370, 451, + + // Animals + 48, 48, + 90, 48, + 65, 65, + 384, 65, + 126, 126, + 89, 126, + 96, 96, + + // Keep and process later + 50, 50, + 113, 113, + 114, 114, + 121, 121, + 129, 129, + }; + var mapping = new Dictionary(); + var ai = 0; + while (ai < array.Length) + mapping.Add(array[ai++], array[ai++]); + + var result = new List[Scenario.NumPlayerSections]; + result.Fill(); + + units.ForEach((i, list) => { + list.ForEach((j, unit) => { + if (mapping.ContainsKey(unit.UnitId)) { + unit.UnitId = (short)mapping[unit.UnitId]; + result[i].Add(unit); + } + }); + }); + + return result; + } + + /** + * Farms need to be converted including the underlying terrain. + */ + private void ChangeTiles_AOE1_AOE2(IList[] units, Map map) + { + units.ForEach((i, list) => { + list.ForEach((j, unit) => { + var posx = (int)(unit.PosX - 0.5); + var posy = (int)(unit.PosY - 0.5); + var linearpos = posy * map.SizeX + posx; + if (false) { + // switch + } else if (unit.UnitId == 50) { + map.LinearTiles[linearpos].Id = 7; + map.LinearTiles[linearpos - 1].Id = 7; + map.LinearTiles[linearpos + 1].Id = 7; + map.LinearTiles[linearpos - map.SizeX].Id = 7; + map.LinearTiles[linearpos - map.SizeX - 1].Id = 7; + map.LinearTiles[linearpos - map.SizeX + 1].Id = 7; + map.LinearTiles[linearpos + map.SizeX].Id = 7; + map.LinearTiles[linearpos + map.SizeX - 1].Id = 7; + map.LinearTiles[linearpos + map.SizeX + 1].Id = 7; + } else if (unit.UnitId == 113 || unit.UnitId == 114 || unit.UnitId == 121 || unit.UnitId == 129) { + if (map.LinearTiles[linearpos].Id == 20) { + map.LinearTiles[linearpos].Id = 17; + unit.UnitId = 414; + } else { + unit.UnitId = 351; + } + } + }); + }); + } + + /** + * Forests would contain the same tree graphic + * without this function + */ + private void RandomizeTrees_AOE2(IList[] units) + { + var treeids = new int[] { + 411, 351, 414, 350, 348, 349, + }; + units.ForEach((i, list) => { + list.ForEach((j, unit) => { + treeids.ForEach((k, treeid) => { + if (unit.UnitId == treeid) { + unit.InitialFrame = (short)Util.Math.Rand(0, 13); // Whats the maximum? + unit.Rotation = unit.InitialFrame; + } + }); + }); + }); + } + } +} diff --git a/src/cmdapp/src/DecompressCommand.cs b/src/cmdapp/src/DecompressCommand.cs index 107474c..bc4f905 100644 --- a/src/cmdapp/src/DecompressCommand.cs +++ b/src/cmdapp/src/DecompressCommand.cs @@ -12,9 +12,9 @@ namespace AdrianKousz.GenieEngine var reader = new ScnReader(file); var writer = new ScnWriter(outfile); - var header = reader.ReadScenarioHeader(null); + var header = reader.ReadScenarioHeader(); writer.Write(header); - writer.WriteSeperator(); + writer.WriteSeperator("Custom"); writer.Flush(); using (var comp = new DeflateStream(file, CompressionMode.Decompress)) { diff --git a/src/cmdapp/src/DumpCommand.cs b/src/cmdapp/src/DumpCommand.cs index e046eb6..19ef4cd 100644 --- a/src/cmdapp/src/DumpCommand.cs +++ b/src/cmdapp/src/DumpCommand.cs @@ -9,7 +9,7 @@ namespace AdrianKousz.GenieEngine { using (var stream = File.OpenRead(args[0])) { var aoereader = new ScnReader(stream); - var scn = aoereader.ReadScenario(null); + var scn = aoereader.ReadScenario(); var ser = new System.Web.Script.Serialization.JavaScriptSerializer(); var result = ser.Serialize(scn); Console.WriteLine(result); @@ -23,7 +23,7 @@ namespace AdrianKousz.GenieEngine override public string GetDescription() { - return "Dump a scenario file to XML"; + return "Dump a scenario file to JSON"; } } } diff --git a/src/cmdapp/src/DumpunitsCommand.cs b/src/cmdapp/src/DumpunitsCommand.cs new file mode 100644 index 0000000..d995615 --- /dev/null +++ b/src/cmdapp/src/DumpunitsCommand.cs @@ -0,0 +1,44 @@ +using System; +using System.IO; +using System.Collections.Generic; +using AdrianKousz.Util; +using AdrianKousz.GenieEngine.Data; + +namespace AdrianKousz.GenieEngine +{ + public class DumpunitsCommand : BaseCommand + { + override public void Run() + { + Scenario scn; + using (var stream = File.OpenRead(args[0])) { + var aoereader = new ScnReader(stream); + scn = aoereader.ReadScenario(); + } + + Console.WriteLine("{0,-8} {1,-8} {2,-8} {3}", "ID", "UnitID", "TileID", "Position"); + Console.WriteLine(); + scn.Units.ForEach((i, list) => { + Console.WriteLine("Units of player {0}:", i); + list.ForEach((j, unit) => { + var tilex = (int)(unit.PosX - 0.5); + var tiley = (int)(unit.PosY - 0.5); + var tile = scn.Map.LinearTiles[tiley * scn.Map.SizeX + tilex]; + Console.WriteLine("{0,-8} {1,-8} {2,-8} ({3,6:0.##},{4,6:0.##})", unit.Id, unit.UnitId, tile.Id, unit.PosX, unit.PosY); + }); + Console.WriteLine(); + }); + } + + override public int GetArgumentCount() + { + return 1; + } + + override public string GetDescription() + { + return "Dump units and map tiles below"; + } + } +} + diff --git a/src/cmdapp/src/Program.cs b/src/cmdapp/src/Program.cs index 83ec435..773b35a 100644 --- a/src/cmdapp/src/Program.cs +++ b/src/cmdapp/src/Program.cs @@ -9,6 +9,11 @@ namespace AdrianKousz.GenieEngine { System.Threading.Thread.CurrentThread.Name = "main"; + if (System.Diagnostics.Debugger.IsAttached) { + debug(); + return; + } + new CmdApp(new Program(), args).Run(); } @@ -20,11 +25,23 @@ namespace AdrianKousz.GenieEngine public IDictionary> GetCommands() { var result = new Dictionary>(); + result.Add("copymap", new CopymapCommand()); result.Add("compress", new CompressCommand()); result.Add("decompress", new DecompressCommand()); result.Add("extract", new ExtractCommand()); result.Add("dump", new DumpCommand()); + result.Add("dumpunits", new DumpunitsCommand()); return result; } + + private static void debug() + { + var fn = ""; + using (var istream = System.IO.File.OpenRead(fn)) { + var ireader = new ScnReader(istream); + var scn = ireader.ReadScenario(); + System.Diagnostics.Debugger.Break(); // Great to inspect scn + } + } } } diff --git a/src/lib/AdrianKousz.GenieEngine.Data/UnitInfo.cs b/src/lib/AdrianKousz.GenieEngine.Data/UnitInfo.cs index c44c52e..e6531b3 100644 --- a/src/lib/AdrianKousz.GenieEngine.Data/UnitInfo.cs +++ b/src/lib/AdrianKousz.GenieEngine.Data/UnitInfo.cs @@ -4,7 +4,7 @@ namespace AdrianKousz.GenieEngine.Data { public class UnitInfo { - public const Single ExpectedUnknown1 = 2f; + public const Single ExpectedUnknown1 = 1f; public const Byte ExpectedUnknown2 = 2; public Single PosX; diff --git a/src/lib/AdrianKousz.GenieEngine/ScnDefaultFactory.cs b/src/lib/AdrianKousz.GenieEngine/ScnDefaultFactory.cs index ab4bf23..0288cb4 100644 --- a/src/lib/AdrianKousz.GenieEngine/ScnDefaultFactory.cs +++ b/src/lib/AdrianKousz.GenieEngine/ScnDefaultFactory.cs @@ -12,6 +12,8 @@ namespace AdrianKousz.GenieEngine result.OriginalVersion = 0; result.NextId = 1; + result.StringTablePlayerNames = new int[Scenario.NumPlayers]; + result.Units = new IList[Scenario.NumPlayerSections] .Fill(() => new List()); From 82d8551ab10c4dd119a791caf5698699d55af965 Mon Sep 17 00:00:00 2001 From: Adrian Date: Mon, 15 Feb 2016 20:42:18 +0100 Subject: [PATCH 5/5] Update Readme, add notes and conversion example --- README.md | 54 ++++++++++++++++++++++++ files/OpeningMoves-AOE2.scx | Bin 0 -> 19834 bytes files/examples/aoe1x-Empty.scx | Bin 0 -> 495 bytes files/examples/aoe2x-Empty.scx | Bin 0 -> 508 bytes files/units-aoe1.txt | 59 ++++++++++++++++++++++++++ files/units-aoe2.txt | 74 +++++++++++++++++++++++++++++++++ 6 files changed, 187 insertions(+) create mode 100644 files/OpeningMoves-AOE2.scx create mode 100644 files/examples/aoe1x-Empty.scx create mode 100644 files/examples/aoe2x-Empty.scx create mode 100644 files/units-aoe1.txt create mode 100644 files/units-aoe2.txt diff --git a/README.md b/README.md index 675ddfe..5b30cc2 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,61 @@ Command line application and library to edit scenario and campaign files. **Alpha version** +## Usage + + Available commands are: + copymap Copy parts of scenarios + compress Recompress a decompressed scenario file + decompress Decompress a scenario file for hex editing + extract Extract scenarios from campaign file + dump Dump a scenario file to JSON + dumpunits Dump units and map tiles below + +The command `copymap` is able convert an AOE1 to an AOE2 scenario. +There seem to be some issues with cliffs. + +[OpeningMoves-AOE2.scx](files/OpeningMoves-AOE2.scx) +is a fully automated conversion of a scenario from the AOE1 demo version: + + $ GenieEdit.exe copymap files/examples/aoe2x-Empty.scx "files/examples/aoe1demo-Reigno_1-Opening Moves.scn" files/OpeningMoves-AOE2.scx + +(Stone are set to 100 to be able to build a town center from the beginning.) + +It can also be used to dump units from any version of a scenario: + + $ GenieEdit.exe dumpunits Debug2.scx + Scenario Editor + + ID UnitID TileID Position + + Units of player 0: + 0 69 23 ( 1,5, 11,5) + 1 69 23 ( 2,5, 11,5) + ... + +### Code + +Have a look at the command implementations. They are very short. + ## Build Clone my projects MainUtil.cs ExUtil.cs and this repository and build `src/GenieEdit.sln`. + +## Contributing + +Contributions are very welcome. ☺ +Keep in mind that the API is still very unstable. + +Suggestions: + +1. Gather UnitIDs of AOE1 +2. Much help is appreciated to figure out ID mappings + for the conversion process. +3. Ideas how to configure the converter (mapping tables). + There are some notes in German: + [AOE1](files/units-aoe1.txt), + [AOE2](files/units-aoe2.txt) +3. The application should be able to generate maps from images + in the future. How should units be placed using the image? +4. Feedback about the API. diff --git a/files/OpeningMoves-AOE2.scx b/files/OpeningMoves-AOE2.scx new file mode 100644 index 0000000000000000000000000000000000000000..2f1bf01bb2a9b8fa7781c2c35d8689351fd2db7e GIT binary patch literal 19834 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 literal 0 HcmV?d00001 diff --git a/files/examples/aoe1x-Empty.scx b/files/examples/aoe1x-Empty.scx new file mode 100644 index 0000000000000000000000000000000000000000..6f40b16b91b1c649260ed37bc3eba8010da5638c GIT binary patch literal 495 zcmXriGc*)oU|?VZVtejGVL%EDK>Rni_httN${c?g@ABg9{`<~8 zzwO`uYIA&b`2Tk|FaG;!`hRPG@_g}bSDU)E=a>I_{ko)o{=rw9*RAHi+J5h;_v!O? z#|y80_5ivG8C@#O{Q6sIfAHt;XJml#FaMl;^tobvRgdsvXtuNAAHU!6;V<8o{`uzWk5^x9E7w0~;`^AtdhfqY6}exJU0P5ObnEV~#6Jl?ZY_LR zw^wF;cfMY)c$<2A`|GDy3o`c@)a*^I*m+0hos>S${S4`Q@#oraALBl39^Ib2GuJ{M iD3~R`Zrbj)*^gTv-j+T5_s6|Q^;`dnKRJA#odE#b9_+CI literal 0 HcmV?d00001 diff --git a/files/examples/aoe2x-Empty.scx b/files/examples/aoe2x-Empty.scx new file mode 100644 index 0000000000000000000000000000000000000000..7652ee529833cc7fd07b35641bce9623d70f0154 GIT binary patch literal 508 zcmXriGcpumU|?VZ;;CN_h5;!s0P)}4Ih`dO$Z-7O2o zlRx_v-Sa(H{nPUQ=Y9Xpc=*-S^Y6u0|9z5sb?>a-A1-xOZ?9SW;l+=mm#0r|KR)@R z)xQ0ocz>MUI%%KTk5#YFi|uC<|9kq^guOOzf~Yq_TF}`};sLeqX)!rJ}#*t{?jGf2He3^CaKT+W%ImLxY7ZY89_@tUSN|_vGhC ze;aW1`_Jp0-ya{lbKcx#H;?=B_a6$Z*&uAS`BCv^Y4KgA;rITwPQ1M4^F(QG<)dCT zd4F6=oj-15Q#KcO|1>peN#2UPIZwh)p2$h9TaxwSji-439X)BwXixW5ZBjjVG-W%r xQFeld 3x3 50 +Dorfzentrum 3x3 109 +Kaserne 3x3 12 +Kornspeicher 3x3 68 +Lagergrube 3x3 103 +Stall 101 + +Baum 134 +Buche 140 +Eiche 141 144 145 146 +Esche, Fichte 148 136 +Kiefer 197 194 193 192 135 137 138 139 391 392 +Palme 150-153 + +Wald Gelaende TileID=10: +142 143 147 195 365 366 367 393 + +Palmenhain Gelaende TileID=13: +113 114 121 129 + +Dschungel TileID=20: +113 114 121 129 + +Kiefernwald TileID=19: +161 198 203 226 391 392 + +Fisch Kueste 260 +Fisch Kueste 263 +Fisch Lachs 53 +Fisch Thun 52 +Fisch Wal 370 + +Aligator 1 +Aligator Koenig 362 +Elefant 48 +Elefant Koenig 90 +Gazelle 65 +Gazelle Koenig 384 +Loewe 126 +Loewe Koenig 89 +Vogel Adler 96 +Vogel Habicht 95 diff --git a/files/units-aoe2.txt b/files/units-aoe2.txt new file mode 100644 index 0000000..2ed0091 --- /dev/null +++ b/files/units-aoe2.txt @@ -0,0 +1,74 @@ +Gold 66 +Bush 59 +Stone 102 +Klippe 3x3 264 +Dorfbewohner 83 + +Palisadenwall 72 +Steinwallall 117 +Befestigter Wall 155 + +Aussenposten 1x1 598 +Beobachtungsturm 1x1 79 +Wachturm 1x1 79 +Hauptturm 1x1 79 + +Haus 2x2 70 +Reliquie 285 +Bauernhof=>Feld 3x3 50 (TileID=7) +Dorfzentrum 4x4 109 +Kaserne 3x3 12 +Muehle 2x2 68 +Holzlager 2x2 562 +Stall 3x3 101 + +Bambus 348 +Baum A-L 399-410 +Baum TD 248 +Dschungelbaum 414 +Eiche 349 +Kiefer 350 +Palme 351 +Verschneiter Kiefer 413 +Waldbaum 411 + +Wald Gelaende TileID=10: +411 + +Palmenhain Gelaende TileID=13: +351 + +Dschungel TileID=17: +414 + +Kiefernwald TileID=19: +350 + +Bambus TileID=18: +348 + +Eichenwald TileID=20: +349 + +Fisch Barsch 53 +Fisch Dorado 455 +Fisch Lachs 456 +Fisch Schnapper 458 +Fisch Thun 457 + +Fisch Marlin 1 451 +Fisch Marlin 2 450 +Fisch Kueste 69 + +Tiere: +Vogel Falke 96 +Jaguar 812 +Wildes Pferd 835 +Pferd 814 +Reh 65 +Schaf 594 +Truthahn 833 +Urwolf 89 +Wolf 126 +Wilder Eber 810 +Wildschwein 48