Compare commits

..

No commits in common. "d2960720bdd7420ba6bd795068b599db75df79fa" and "d1593f38c5e1adaf466cd23a5d592b06d96f4235" have entirely different histories.

24 changed files with 414 additions and 410 deletions

View File

@ -32,19 +32,20 @@
</PropertyGroup>
<ItemGroup>
<Compile Include="src\Program.cs" />
<Compile Include="src\BaseCommand.cs" />
<Compile Include="src\ExtractCommand.cs" />
<Compile Include="src\ToJsonCommand.cs" />
<Compile Include="src\FromJsonCommand.cs" />
<Compile Include="src\CompressCommand.cs" />
<Compile Include="src\DecompressCommand.cs" />
<Compile Include="src\DumpunitsCommand.cs" />
<Compile Include="src\CopymapConverter.cs" />
<Compile Include="src\ConvertCommand.cs" />
<Compile Include="src\AddBitmapCommand.cs" />
<Compile Include="src\DelBitmapCommand.cs" />
<Compile Include="src\PackCommand.cs" />
<Compile Include="src\Bmp2MapCommand.cs" />
<Compile Include="src\BitmapHelpers.cs" />
<Compile Include="src\ConvertCommandHelper.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<ItemGroup>
@ -65,11 +66,12 @@
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Web.Extensions" />
<Reference Include="Newtonsoft.Json">
<HintPath>..\packages\Newtonsoft.Json.8.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />

View File

@ -1,12 +1,13 @@
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using AdrianKousz.Util;
namespace AdrianKousz.GenieEngine
{
public class AddBitmapCommand : CmdApp.Command
public class AddBitmapCommand : BaseCommand
{
override public void Run(string[] args)
override public void Run()
{
using (var input = File.OpenRead(args[0]))
using (var output = File.OpenWrite(args[1]))
@ -22,11 +23,20 @@ namespace AdrianKousz.GenieEngine
GenieFile.Serialize(scn, output);
}
}
public AddBitmapCommand()
override public int GetArgumentCount()
{
MinArgumentCount = 3;
Description = "Add a pregame bitmap";
Help = "Add a pregame bitmap to a scenario."
return 3;
}
override public string GetDescription()
{
return "Add a pregame bitmap";
}
override public string GetHelp()
{
return "Add a pregame bitmap to a scenario."
+ "\nThe bitmap has to be indexed, otherwise, the game will probably crash."
+ "\nAdditionally, a game-specific palette will be used to display the image."
+ "\n"

View File

@ -0,0 +1,36 @@
using System;
using AdrianKousz.Util;
namespace AdrianKousz.GenieEngine
{
public class BaseCommand : ICommand<string[]>
{
protected string[] args;
virtual public string GetDescription()
{
return "(Empty description)";
}
virtual public string GetHelp()
{
return "(Empty help text)";
}
virtual public int GetArgumentCount()
{
return 1;
}
virtual public void SetArguments(string[] args)
{
this.args = args;
}
virtual public void Run()
{
foreach (var str in args)
Console.WriteLine(str);
}
}
}

View File

@ -9,24 +9,25 @@ namespace AdrianKousz.GenieEngine
{
public static class BitmapHelpers
{
public static unsafe void FillMap(Scenario scn, Bitmap bmp1, Bitmap bmp2)
public static unsafe void FillMap(Scenario scn, Bitmap bmp, Bitmap bmp2)
{
using (var ubmp1 = new UnsafeBitmap(bmp1))
using (var ubmp = new UnsafeBitmap(bmp))
using (var ubmp2 = new UnsafeBitmap(bmp2))
{
var factory = new ScnDefaultFactory();
EnsureValid(ubmp1, ubmp2);
Check(ubmp);
Check(ubmp2);
var ptr1 = ubmp1.Scan0;
var last1 = ptr1 + ubmp1.Stride * ubmp1.Height;
var ptr = ubmp.Scan0;
var last = ptr + ubmp.Stride * ubmp.Height;
var ptr2 = ubmp2.Scan0;
var last2 = ptr2 + ubmp2.Stride * ubmp2.Height;
var map = factory.MakeMap();
map.SizeX = ubmp1.Width;
map.SizeY = ubmp1.Height;
map.SizeX = ubmp.Width;
map.SizeY = ubmp.Height;
map.LinearTiles = new Scenario.ScnMap.Tile[map.SizeX * map.SizeY];
var gaiaunits = new List<Scenario.ScnUnit>();
@ -40,37 +41,26 @@ namespace AdrianKousz.GenieEngine
treemapping[20] = 349;
var i = 0;
var id = 0;
while (ptr1 < last1 && ptr2 < last2) {
var treeid = treemapping[*ptr1];
while (ptr < last && ptr2 < last2) {
var treeid = treemapping[*ptr];
if (treeid != 0) {
var unit = factory.MakeUnit();
var posy = i / map.SizeX;
var posx = i - (posy * map.SizeX);
unit.Id = id++;
unit.UnitId = treeid;
unit.PosX = posx + 0.5f;
unit.PosY = posy + 0.5f;
unit.Rotation = unit.InitialFrame = (short)Util.Math.Rand(0, 14);
unit.Rotation = (short)Util.Math.Rand(0, 13);
gaiaunits.Add(unit);
}
map.LinearTiles[i++] = new Scenario.ScnMap.Tile(*ptr1++, (byte)(*ptr2++ / 40), 0);
map.LinearTiles[i++] = new Scenario.ScnMap.Tile(*ptr++, (byte)(*ptr2++ / 37), 0);
}
scn.NextId = id;
scn.Map = map;
scn.Units[0] = gaiaunits;
}
}
private static void EnsureValid(UnsafeBitmap ubmp1, UnsafeBitmap ubmp2)
{
if (ubmp1.Width != ubmp2.Width || ubmp1.Height != ubmp2.Height)
throw new ArgumentException("Bitmaps have unequal dimensions");
Check(ubmp1);
Check(ubmp2);
}
private static void Check(UnsafeBitmap ubmp)
{
if (ubmp.PixelFormat != PixelFormat.Format8bppIndexed)

View File

@ -1,12 +1,13 @@
using System.IO;
using System;
using System.IO;
using System.Drawing;
using AdrianKousz.Util;
namespace AdrianKousz.GenieEngine
{
public class Bmp2MapCommand : CmdApp.Command
public class Bmp2MapCommand : BaseCommand
{
override public void Run(string[] args)
override public void Run()
{
using (var srcstream = File.OpenRead(args[0]))
using (var outstream = File.OpenWrite(args[1]))
@ -22,16 +23,24 @@ namespace AdrianKousz.GenieEngine
}
}
public Bmp2MapCommand()
override public int GetArgumentCount()
{
MinArgumentCount = 4;
Description = "Generate a scenario map based on bitmaps";
Help = "Generate a scenario map based on bitmaps with automatic forest generation."
return 4;
}
override public string GetDescription()
{
return "Generate a scenario map based on bitmaps";
}
override public string GetHelp()
{
return "Generate a scenario map based on bitmaps with automatic forest generation."
+ "\n"
+ "\nTwo bitmaps need to be specified: One terrain map and one elevation map."
+ "\nBoth bitmaps need to be indexed or grayscale."
+ "\nThe index/gray values of the first bitmap are translated directly to a terrain ID."
+ "\nThe index/gray values of the second bitmap are divided by 40 to generate elevations 0-6."
+ "\nThe index/gray values of the second bitmap are divided by 37 to generate elevations 0-6."
+ "\n"
+ "\nParameters:"
+ "\n<input scenario> <output scenario> <terrain bitmap> <elevation bitmap>"

View File

@ -1,28 +1,35 @@
using System.IO;
using AdrianKousz.Util;
namespace AdrianKousz.GenieEngine
{
public class CompressCommand : CmdApp.Command
public class CompressCommand : BaseCommand
{
override public void Run(string[] args)
override public void Run()
{
using (var input = File.OpenRead(args[0]))
using (var output = File.OpenWrite(args[1]))
{
GenieFile.ScenarioCompress(input, output);
GenieFile.ScenarioCompression(input, output, false);
}
}
public CompressCommand()
override public int GetArgumentCount()
{
MinArgumentCount = 2;
Description = "Recompress a decompressed scenario file";
Help = "Recompress a previously decompressed scenario file."
return 2;
}
override public string GetDescription()
{
return "Recompress a decompressed scenario file";
}
override public string GetHelp()
{
return "Recompress a previously decompressed scenario file."
+ "\n"
+ "\nParameters:"
+ "\n<input binary file> <output scenario>"
;
;
}
}
}

View File

@ -1,11 +1,12 @@
using System.IO;
using System;
using System.IO;
using AdrianKousz.Util;
namespace AdrianKousz.GenieEngine
{
public class ConvertCommand : CmdApp.Command
public class ConvertCommand : BaseCommand
{
override public void Run(string[] args)
override public void Run()
{
using (var srcstream = File.OpenRead(args[0]))
using (var outstream = File.OpenWrite(args[1]))
@ -14,46 +15,37 @@ namespace AdrianKousz.GenieEngine
var refscn = GenieFile.Deserialize<Scenario>(refstream);
var srcscn = GenieFile.Deserialize<Scenario>(srcstream);
refscn.Filename = srcscn.Filename;
refscn.InstructionsUncompressed = srcscn.InstructionsUncompressed;
refscn.NextId = srcscn.NextId;
refscn.PlayerCount = srcscn.PlayerCount;
srcscn.VersionString = refscn.VersionString;
srcscn.VersionNumber = refscn.VersionNumber;
srcscn.RawIndividualVictory = refscn.RawIndividualVictory;
srcscn.RawDisables = refscn.RawDisables;
srcscn.RawRemaining = refscn.RawRemaining;
refscn.PlayerSettings = srcscn.PlayerSettings;
refscn.Resources2 = srcscn.Resources2;
refscn.Messages = srcscn.Messages;
refscn.Cinematics = srcscn.Cinematics;
refscn.GlobalVictory = srcscn.GlobalVictory;
refscn.Map = srcscn.Map;
refscn.Units = srcscn.Units;
refscn.PlayerSettings.ForEach(x => {
x.NameStringID = -1;
x.FilenameAI = "RandomGame"; x.FilenameCTY = ""; x.FilenamePER = "";
x.ScriptAI = ""; x.ScriptCTY = ""; x.ScriptPER = "";
srcscn.PlayerSettings.ForEach(x => {
x.FilenameAI = x.FilenameCTY = x.FilenamePER = "RandomGame";
x.ScriptAI = x.ScriptCTY = x.ScriptPER = "";
});
Enumerables.ForBoth(refscn.PlayerSettings2, srcscn.PlayerSettings2, (r, s) => {
r.Name = s.Name;
r.CameraX = s.CameraX;
r.CameraY = s.CameraY;
r.AlliedVictory = s.AlliedVictory;
r.Stances = s.Stances;
});
var converter = new CopymapConverter();
converter.Convert(srcscn);
var converter = new ConvertCommandHelper();
converter.Convert(refscn);
GenieFile.Serialize(refscn, outstream);
GenieFile.Serialize(srcscn, outstream);
}
}
public ConvertCommand()
override public int GetArgumentCount()
{
MinArgumentCount = 3;
Description = "Convert AOE1 scenario";
Help = "Convert AOE1 scenario to AOE2"
return 3;
}
override public string GetDescription()
{
return "Convert AOE1 scenario";
}
override public string GetHelp()
{
return "Convert AOE1 scenario to AOE2"
+ "\n"
+ "\nThe converter needs an existing AOE2 scenario to start with."
+ "\n"

View File

@ -4,20 +4,44 @@ using AdrianKousz.Util;
namespace AdrianKousz.GenieEngine
{
public class ConvertCommandHelper
public class CopymapConverter
{
public void Convert(Scenario scn)
{
ChangeUnits_AOE1_AOE2(scn);
MoveUnits_AOE1_AOE2(scn);
ChangeTiles_AOE1_AOE2(scn);
RandomizeTrees_AOE2(scn);
scn.Units = ChangeUnits_AOE1_AOE2(scn.Units);
MoveUnits_AOE1_AOE2(scn.Units);
ChangeTiles_AOE1_AOE2(scn.Units, scn.Map);
RandomizeTrees_AOE2(scn.Units);
}
/**
* Some buildings are not the same size.
* They need to be moved by half a unit.
*/
private void MoveUnits_AOE1_AOE2(IList<Scenario.ScnUnit>[] units)
{
var mapping = new int[] {
// Towers
79, 199, 69, 278,
// Other Buildings
109, 68, 103,
};
units.ForEach(list => {
list.ForEach(unit => {
mapping.ForEach(buildingid => {
if (unit.UnitId == buildingid) {
unit.PosX -= 0.5f;
unit.PosY -= 0.5f;
}
});
});
});
}
/**
* Simple mapping of unit IDs
*/
private void ChangeUnits_AOE1_AOE2(Scenario scn)
private List<Scenario.ScnUnit>[] ChangeUnits_AOE1_AOE2(IList<Scenario.ScnUnit>[] units)
{
var mapping = new short[] {
// Resources
@ -117,43 +141,26 @@ namespace AdrianKousz.GenieEngine
129, 129,
}.ToDictionary();
scn.Units = scn.Units.Map(list => {
return (IList<Scenario.ScnUnit>)list
var result = units.Map(list => {
return list
.Filter(unit => mapping.ContainsKey(unit.UnitId))
.Map(unit => {
unit.UnitId = mapping[unit.UnitId];
return unit;
})
.ToList();
}).ToList();
}
.ForEach(unit => {
unit.UnitId = mapping[unit.UnitId];
}).ToList();
}).ToArray();
/**
* Some buildings are not the same size.
* They need to be moved by half a unit.
*/
private void MoveUnits_AOE1_AOE2(Scenario scn)
{
var minus = new int[] { 79, 68, 562, };
var plus = new int[] { 109, };
scn.Units.ForEach(list => {
list.ForEach(unit => {
minus.ForEach(buildingid => { if (unit.UnitId != buildingid) return; unit.PosX -= 0.5f; unit.PosY -= 0.5f; });
plus.ForEach(buildingid => { if (unit.UnitId != buildingid) return; unit.PosX += 0.5f; unit.PosY += 0.5f; });
});
});
return result;
}
/**
* Farms need to be converted including the underlying terrain.
*/
private void ChangeTiles_AOE1_AOE2(Scenario scn)
private void ChangeTiles_AOE1_AOE2(IList<Scenario.ScnUnit>[] units, Scenario.ScnMap map)
{
var map = scn.Map;
scn.Units.ForEach(list => {
units.ForEach(list => {
list.ForEach(unit => {
var posx = (int)unit.PosX;
var posy = (int)unit.PosY;
var posx = (int)(unit.PosX - 0.5);
var posy = (int)(unit.PosY - 0.5);
var linearpos = posy * map.SizeX + posx;
if (false) {
// switch
@ -184,16 +191,16 @@ namespace AdrianKousz.GenieEngine
* Forests would contain the same tree graphic
* without this function
*/
private void RandomizeTrees_AOE2(Scenario scn)
private void RandomizeTrees_AOE2(IList<Scenario.ScnUnit>[] units)
{
var treeids = new int[] {
411, 351, 414, 350, 348, 349,
};
scn.Units.ForEach(list => {
units.ForEach(list => {
list.ForEach(unit => {
treeids.ForEach(treeid => {
if (unit.UnitId == treeid) {
unit.Rotation = unit.InitialFrame = (short)Util.Math.Rand(0, 14);
unit.Rotation = (short)Util.Math.Rand(0, 13); // Whats the maximum?
}
});
});

View File

@ -1,24 +1,31 @@
using System.IO;
using AdrianKousz.Util;
namespace AdrianKousz.GenieEngine
{
public class DecompressCommand : CmdApp.Command
public class DecompressCommand : BaseCommand
{
override public void Run(string[] args)
override public void Run()
{
using (var input = File.OpenRead(args[0]))
using (var output = File.OpenWrite(args[1]))
{
GenieFile.ScenarioDecompress(input, output);
GenieFile.ScenarioCompression(input, output, true);
}
}
public DecompressCommand()
override public int GetArgumentCount()
{
MinArgumentCount = 2;
Description = "Decompress a scenario file";
Help = "Decompress a scenario file for hex editing."
return 2;
}
override public string GetDescription()
{
return "Decompress a scenario file";
}
override public string GetHelp()
{
return "Decompress a scenario file for hex editing."
+ "\n"
+ "\nParameters:"
+ "\n<input scenario> <output binary file>"

View File

@ -1,11 +1,12 @@
using System.IO;
using System.Drawing;
using AdrianKousz.Util;
namespace AdrianKousz.GenieEngine
{
public class DelBitmapCommand : CmdApp.Command
public class DelBitmapCommand : BaseCommand
{
override public void Run(string[] args)
override public void Run()
{
using (var input = File.OpenRead(args[0]))
using (var output = File.OpenWrite(args[1]))
@ -20,11 +21,19 @@ namespace AdrianKousz.GenieEngine
}
}
public DelBitmapCommand()
override public int GetArgumentCount()
{
MinArgumentCount = 2;
Description = "Remove the pregame bitmap";
Help = "Remove the pregame bitmap from a scenario."
return 2;
}
override public string GetDescription()
{
return "Remove the pregame bitmap";
}
override public string GetHelp()
{
return "Remove the pregame bitmap from a scenario."
+ "\n"
+ "\nParameters:"
+ "\n<input scenario> <output scenario>"

View File

@ -1,12 +1,13 @@
using System;
using System.IO;
using System.Collections.Generic;
using AdrianKousz.Util;
namespace AdrianKousz.GenieEngine
{
public class DumpunitsCommand : CmdApp.Command
public class DumpunitsCommand : BaseCommand
{
override public void Run(string[] args)
override public void Run()
{
Scenario scn;
using (var input = File.OpenRead(args[0]))
@ -14,24 +15,29 @@ namespace AdrianKousz.GenieEngine
scn = GenieFile.Deserialize<Scenario>(input);
}
Console.WriteLine("{0,-8} {1,-8} {2,-8} {3,-8} {4}", "Player", "ID", "UnitID", "TileID", "Position");
Console.WriteLine("{0,-8} {1,-8} {2,-8} {3}", "ID", "UnitID", "TileID", "Position");
Console.WriteLine();
var i = 0;
scn.Units.ForEach(list => {
Console.WriteLine("Units of player {0}:", i++);
list.ForEach(unit => {
var tilex = (int)unit.PosX;
var tiley = (int)unit.PosY;
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,-8} ({4,6:0.##},{5,6:0.##})", i, unit.Id, unit.UnitId, tile.Id, unit.PosX, unit.PosY);
Console.WriteLine("{0,-8} {1,-8} {2,-8} ({3,6:0.##},{4,6:0.##})", unit.Id, unit.UnitId, tile.Id, unit.PosX, unit.PosY);
});
i++;
Console.WriteLine();
});
}
public DumpunitsCommand()
override public int GetArgumentCount()
{
MinArgumentCount = 1;
Description = "Dump units and underlying map tiles";
return 1;
}
override public string GetDescription()
{
return "Dump units and map tiles below";
}
}
}

View File

@ -4,9 +4,9 @@ using AdrianKousz.Util;
namespace AdrianKousz.GenieEngine
{
public class ExtractCommand : CmdApp.Command
public class ExtractCommand : BaseCommand
{
override public void Run(string[] args)
override public void Run()
{
using (var file = File.OpenRead(args[0]))
{
@ -22,10 +22,14 @@ namespace AdrianKousz.GenieEngine
}
}
public ExtractCommand()
override public int GetArgumentCount()
{
MinArgumentCount = 1;
Description = "Extract files from campaign file";
return 1;
}
override public string GetDescription()
{
return "Extract files from campaign file";
}
}
}

View File

@ -4,9 +4,9 @@ using AdrianKousz.Util;
namespace AdrianKousz.GenieEngine
{
public class FromJsonCommand : CmdApp.Command
public class FromJsonCommand : BaseCommand
{
override public void Run(string[] args)
override public void Run()
{
using (var input = File.OpenRead(args[0]))
using (var reader = input.GetReader())
@ -18,11 +18,19 @@ namespace AdrianKousz.GenieEngine
}
}
public FromJsonCommand()
override public int GetArgumentCount()
{
MinArgumentCount = 2;
Description = "Convert a JSON file to a scenario";
Help = "Read a Scenario structure formatted as JSON and save it as a Scenario."
return 2;
}
override public string GetDescription()
{
return "Convert a JSON file to a scenario";
}
override public string GetHelp()
{
return "Read a Scenario structure formatted as JSON and save it as a Scenario."
+ "\n"
+ "\nParameters:"
+ "\n<input json> <output scenario>"

View File

@ -4,9 +4,9 @@ using AdrianKousz.Util;
namespace AdrianKousz.GenieEngine
{
public class PackCommand : CmdApp.Command
public class PackCommand : BaseCommand
{
override public void Run(string[] args)
override public void Run()
{
var files = new List<Campaign.CpnFile>();
for (var i = 2; i < args.Length; i++) {
@ -33,10 +33,14 @@ namespace AdrianKousz.GenieEngine
}
}
public PackCommand()
override public int GetArgumentCount()
{
MinArgumentCount = 3;
Description = "Pack files into a campaign file";
return 3;
}
override public string GetDescription()
{
return "Pack files into a campaign file";
}
}
}

View File

@ -1,39 +1,51 @@
using System.Collections.Generic;
using System.Windows.Forms;
using AdrianKousz.Util;
using AdrianKousz.Util;
using System.Collections.Generic;
namespace AdrianKousz.GenieEngine
{
public class Program
public class Program : IApp<string, string[]>
{
public static void Main(string[] args)
{
System.Threading.Thread.CurrentThread.Name = "main";
Log.SetUncaughtExceptionHandler();
new CmdApp.Builder()
.Header("Scenario Editor")
.NoArg(new MessageBoxCommand())
.Add("convert", new ConvertCommand())
.Add("bmp2map", new Bmp2MapCommand())
.Add("addbmp", new AddBitmapCommand())
.Add("delbmp", new DelBitmapCommand())
.Add("compress", new CompressCommand())
.Add("decompress", new DecompressCommand())
.Add("tojson", new ToJsonCommand())
.Add("fromjson", new FromJsonCommand())
.Add("dumpunits", new DumpunitsCommand())
.Add("extract", new ExtractCommand())
.Add("pack", new PackCommand())
.Build()
.Run(args);
if (System.Diagnostics.Debugger.IsAttached) {
debug();
return;
}
new CmdApp(new Program(), args).Run();
}
private class MessageBoxCommand : CmdApp.Command
public string GetHeader()
{
public override void Run(string[] args)
{
MessageBox.Show("Start the program on the command line like \n> GenieEdit.exe help");
return "Scenario Editor";
}
public IDictionary<string, ICommand<string[]>> GetCommands()
{
var result = new Dictionary<string, ICommand<string[]>>();
result.Add("convert", new ConvertCommand());
result.Add("bmp2map", new Bmp2MapCommand());
result.Add("addbmp", new AddBitmapCommand());
result.Add("delbmp", new DelBitmapCommand());
result.Add("compress", new CompressCommand());
result.Add("decompress", new DecompressCommand());
result.Add("tojson", new ToJsonCommand());
result.Add("fromjson", new FromJsonCommand());
result.Add("dumpunits", new DumpunitsCommand());
result.Add("extract", new ExtractCommand());
result.Add("pack", new PackCommand());
return result;
}
private static void debug()
{
var fn = "";
using (var istream = System.IO.File.OpenRead(fn)) {
var scn = GenieFile.Deserialize<Scenario>(istream);
System.Diagnostics.Debugger.Break(); // Great to inspect scn
}
}
}

View File

@ -4,9 +4,9 @@ using AdrianKousz.Util;
namespace AdrianKousz.GenieEngine
{
public class ToJsonCommand : CmdApp.Command
public class ToJsonCommand : BaseCommand
{
override public void Run(string[] args)
override public void Run()
{
using (var input = File.OpenRead(args[0]))
using (var output = File.OpenWrite(args[1]))
@ -18,11 +18,19 @@ namespace AdrianKousz.GenieEngine
}
}
public ToJsonCommand()
override public int GetArgumentCount()
{
MinArgumentCount = 2;
Description = "Convert a scenario to a JSON file";
Help = "Save a Scenario structure formatted as JSON."
return 2;
}
override public string GetDescription()
{
return "Convert a scenario to a JSON file";
}
override public string GetHelp()
{
return "Save a Scenario structure formatted as JSON."
+ "\n"
+ "\nParameters:"
+ "\n<input scenario> <output json>"

View File

@ -31,12 +31,14 @@ namespace AdrianKousz.GenieEngine
number = reader.ReadInt32();
result.Files = new PositionInfo()
.CreateArray(number)
.Fill(() => new PositionInfo {
Size = reader.ReadInt32(),
Offset = reader.ReadInt32(),
Name = reader.ReadZString(255),
Filename = reader.ReadZString(257),
.CreateList()
.Fill(number, () => {
return new PositionInfo {
Size = reader.ReadInt32(),
Offset = reader.ReadInt32(),
Name = reader.ReadZString(255),
Filename = reader.ReadZString(257),
};
})
.Map(x => {
var file = new Campaign.CpnFile();
@ -46,7 +48,7 @@ namespace AdrianKousz.GenieEngine
file.Filename = x.Filename;
return file;
})
.ToList();
.ToArray();
return result;
}

View File

@ -101,29 +101,13 @@ namespace AdrianKousz.GenieEngine
return new ExtendedBinaryWriter(new NonDisposingStreamWrapper(stream), Scenario.Encoding);
}
public static void ScenarioDecompress(Stream input, Stream output)
public static void ScenarioCompression(Stream input, Stream output, bool decompress)
{
CopyHeader(input, output);
var mode = decompress ? CompressionMode.Decompress : CompressionMode.Compress;
using (var comp = new DeflateStream(input, CompressionMode.Decompress))
{
comp.CopyTo(output);
}
}
byte[] buffer;
public static void ScenarioCompress(Stream input, Stream output)
{
CopyHeader(input, output);
using (var comp = new DeflateStream(output, CompressionMode.Compress))
{
input.CopyTo(comp);
}
}
private static void CopyHeader(Stream input, Stream output)
{
var buffer = input.ReadBytes(8);
buffer = input.ReadBytes(8);
output.Write(buffer);
var headerLength = buffer[4]
@ -134,6 +118,11 @@ namespace AdrianKousz.GenieEngine
buffer = input.ReadBytes(headerLength);
output.Write(buffer);
using (var comp = new DeflateStream(input, mode))
{
comp.CopyTo(output);
}
}
#endregion

View File

@ -4,11 +4,10 @@
{
Scenario MakeScenario();
Scenario.ScnPlayerSettings MakePlayerSettings();
Scenario.ScnResource2 MakeResources2();
Scenario.ScnPlayerSettings2 MakePlayerSettings2();
Scenario.ScnMessages MakeMessages();
Scenario.ScnCinematics MakeCinematics();
Scenario.ScnGlobalVictory MakeGlobalVictory();
Scenario.ScnResourceCopy MakeResourceCopy();
Scenario.ScnMap MakeMap();
Scenario.ScnUnit MakeUnit();
}

View File

@ -22,30 +22,30 @@ namespace AdrianKousz.GenieEngine
public String Filename;
public DateTime Timestamp;
public String InstructionsUncompressed;
public Int32 NextId;
public Int32 PlayerCount;
public IList<ScnPlayerSettings> PlayerSettings;
public IList<ScnResource2> Resources2;
public IList<ScnPlayerSettings2> PlayerSettings2;
public ScnMessages Messages;
public ScnCinematics Cinematics;
public ScnGlobalVictory GlobalVictory;
public ScnMap Map;
public IList<IList<ScnUnit>> Units;
public ScnPlayerSettings[] PlayerSettings;
public ScnResourceCopy[] ResourceCopies;
public IList<ScnUnit>[] Units;
// NOTE: Raw byte arrays that do not have a parser (yet?)
public Byte[] RawIndividualVictory;
public Byte[] RawDisables;
public Byte[] RawRemaining;
// NOTE: Unknown values
public Int32 Unknown1;
public Int32 Unknown2;
public Int32 Unknown3;
public Byte Unknown4;
public Single Unknown5;
public Byte[] RawIndividualVictory;
public Byte[] RawDisables;
public Byte[] RawRemaining;
public class ScnPlayerSettings
{
public const Int32 ExpectedUnknownInfo = 4;
@ -55,11 +55,17 @@ namespace AdrianKousz.GenieEngine
public Int32 NameStringID;
public Boolean Active;
public Boolean Human;
public Byte AIType;
public Int32 Civ;
public Int32 StartingAge;
public Int32 UnknownInfo;
public String FilenameAI;
public String FilenameCTY;
public String FilenamePER;
public String ScriptAI;
public String ScriptCTY;
public String ScriptPER;
public Byte AIType;
public Int32 ResourceWood;
public Int32 ResourceFood;
public Int32 ResourceGold;
@ -67,51 +73,9 @@ namespace AdrianKousz.GenieEngine
public Int32 ResourceOre;
public Int32 UnknownResource;
public Boolean AlliedVictory;
public Int32[] Diplomacy;
public String FilenameAI;
public String FilenameCTY;
public String FilenamePER;
public String ScriptAI;
public String ScriptCTY;
public String ScriptPER;
}
public class ScnResource2
{
public const Single ExpectedUnknown = 0;
public Single Wood;
public Single Food;
public Single Gold;
public Single Stone;
public Single Ore;
public Single Unknown;
public Single PopulationLimit;
}
public class ScnPlayerSettings2
{
public String Name;
public Single CameraX;
public Single CameraY;
public Int16 UnknownX;
public Int16 UnknownY;
public Int32 Color;
public Byte AlliedVictory;
public Stance[] Stances;
public Single Unknown1;
public Int32 Unknown2;
public Byte[] Unknown;
public class Stance
{
public Byte Stance1;
public Int32 Stance2;
}
public Boolean AlliedVictory;
public Int32 StartingAge;
}
public class ScnMessages
@ -163,6 +127,19 @@ namespace AdrianKousz.GenieEngine
public Int32 Unknown;
}
public class ScnResourceCopy
{
public const Single ExpectedUnknownResource = 0;
public Single ResourceGold;
public Single ResourceWood;
public Single ResourceFood;
public Single ResourceStone;
public Single ResourceOre;
public Single UnknownResource;
public Single PopulationLimit;
}
public class ScnMap
{
public Int32 CameraX;

View File

@ -32,10 +32,13 @@ namespace AdrianKousz.GenieEngine
result.Cinematics = MakeCinematics();
result.GlobalVictory = MakeGlobalVictory();
result.Map = MakeMap();
result.PlayerSettings = result.PlayerSettings.NewList().Fill(Scenario.NumPlayers, MakePlayerSettings);
result.Resources2 = result.Resources2.NewList().Fill(Scenario.NumPlayerSections - 1, MakeResources2);
result.PlayerSettings2 = result.PlayerSettings2.NewList().Fill(Scenario.NumPlayerSections - 1, MakePlayerSettings2);
result.Units = MakeUnitList(Scenario.NumPlayerSections);
result.ResourceCopies = new Scenario.ScnResourceCopy[Scenario.NumPlayerSections - 1]
.Fill(MakeResourceCopy);
result.PlayerSettings = new Scenario.ScnPlayerSettings[Scenario.NumPlayers]
.Fill(MakePlayerSettings);
result.Units = new List<Scenario.ScnUnit>[Scenario.NumPlayerSections]
.Fill();
}
return result;
@ -60,25 +63,6 @@ namespace AdrianKousz.GenieEngine
return result;
}
public Scenario.ScnResource2 MakeResources2()
{
// AOE2 standard
var result = new Scenario.ScnResource2();
result.Unknown = Scenario.ScnResource2.ExpectedUnknown;
result.Food = 200;
result.Wood = 200;
result.Gold = 100;
result.Stone = 200;
result.PopulationLimit = 75;
return result;
}
public Scenario.ScnPlayerSettings2 MakePlayerSettings2()
{
var result = new Scenario.ScnPlayerSettings2();
return result;
}
public Scenario.ScnMessages MakeMessages()
{
var result = new Scenario.ScnMessages();
@ -117,6 +101,19 @@ namespace AdrianKousz.GenieEngine
return result;
}
public Scenario.ScnResourceCopy MakeResourceCopy()
{
// AOE2 standard
var result = new Scenario.ScnResourceCopy();
result.UnknownResource = Scenario.ScnResourceCopy.ExpectedUnknownResource;
result.ResourceFood = 200;
result.ResourceWood = 200;
result.ResourceGold = 100;
result.ResourceStone = 200;
result.PopulationLimit = 75;
return result;
}
public Scenario.ScnMap MakeMap()
{
var result = new Scenario.ScnMap();
@ -138,13 +135,6 @@ namespace AdrianKousz.GenieEngine
result.GarrisonnedInId = -1;
return result;
}
public static IList<IList<Scenario.ScnUnit>> MakeUnitList(int count = Scenario.NumPlayerSections)
{
IList<IList<Scenario.ScnUnit>> result = new List<IList<Scenario.ScnUnit>>()
.Fill(count, () => (IList<Scenario.ScnUnit>)new List<Scenario.ScnUnit>());
return result;
}
}
}

View File

@ -33,7 +33,7 @@ namespace AdrianKousz.GenieEngine
result.VersionString = reader.ReadString(4);
reader.ReadInt32(); // Header Length
result.Unknown1 = reader.ReadInt32();
result.Timestamp = reader.ReadInt32().ToDateTime();
result.Timestamp = DateTimes.FromUnixTime(reader.ReadInt32());
result.InstructionsUncompressed = reader.ReadZStringInt32();
result.Unknown2 = reader.ReadInt32();
result.PlayerCount = reader.ReadInt32();
@ -49,7 +49,8 @@ namespace AdrianKousz.GenieEngine
result.VersionNumber = reader.ReadSingle();
Version = result.VersionNumber;
result.PlayerSettings = result.PlayerSettings.NewList().Fill(Scenario.NumPlayers, factory.MakePlayerSettings);
result.PlayerSettings = new Scenario.ScnPlayerSettings[Scenario.NumPlayers]
.Fill(factory.MakePlayerSettings);
// PlayerSettings 1
@ -64,8 +65,8 @@ namespace AdrianKousz.GenieEngine
}
result.PlayerSettings.ForEach(x => {
x.Active = reader.ReadBooleanInt32();
x.Human = reader.ReadBooleanInt32();
x.Active = reader.ReadBoolean();
x.Human = reader.ReadBoolean();
x.Civ = reader.ReadInt32();
x.UnknownInfo = reader.ReadInt32();
});
@ -109,7 +110,7 @@ namespace AdrianKousz.GenieEngine
result.Cinematics.FilenameLoss = reader.ReadStringInt16();
result.Cinematics.FilenameBitmap = reader.ReadStringInt16();
var hasBitmap = reader.ReadBooleanInt32();
var hasBitmap = reader.ReadBoolean();
result.Cinematics.BitmapWidth = reader.ReadInt32();
result.Cinematics.BitmapHeight = reader.ReadInt32();
@ -165,13 +166,13 @@ namespace AdrianKousz.GenieEngine
result.GlobalVictory = factory.MakeGlobalVictory();
result.GlobalVictory.RequireConquest = reader.ReadBooleanInt32();
result.GlobalVictory.RequireConquest = reader.ReadBoolean();
result.GlobalVictory.Ruins = reader.ReadInt32();
result.GlobalVictory.Artifacts = reader.ReadInt32();
result.GlobalVictory.Discovery = reader.ReadInt32();
result.GlobalVictory.PercentExplored = reader.ReadInt32();
result.GlobalVictory.Unknown = reader.ReadInt32();
result.GlobalVictory.RequireAllCustom = reader.ReadBooleanInt32();
result.GlobalVictory.RequireAllCustom = reader.ReadBoolean();
result.GlobalVictory.Mode = reader.ReadInt32();
result.GlobalVictory.Score = reader.ReadInt32();
result.GlobalVictory.Time = reader.ReadInt32();
@ -187,7 +188,7 @@ namespace AdrianKousz.GenieEngine
ReadSeparator(reader, "Player Environment");
result.PlayerSettings.ForEach(x => {
x.AlliedVictory = reader.ReadBooleanInt32();
x.AlliedVictory = reader.ReadBoolean();
});
if (Version >= 1.15f) number = (Scenario.NumPlayers * ( 20 /*Disables*/) + 3 /*Unknowns*/) * 4 /*Intsize*/;
@ -222,17 +223,18 @@ namespace AdrianKousz.GenieEngine
number = reader.ReadInt32();
result.Resources2 = result.Resources2.NewList().Fill(number - 1, factory.MakeResources2);
result.ResourceCopies = new Scenario.ScnResourceCopy[number - 1]
.Fill(factory.MakeResourceCopy);
result.Resources2.ForEach(x => {
x.Food = reader.ReadSingle();
x.Wood = reader.ReadSingle();
x.Gold = reader.ReadSingle();
x.Stone = reader.ReadSingle();
result.ResourceCopies.ForEach(x => {
x.ResourceFood = reader.ReadSingle();
x.ResourceWood = reader.ReadSingle();
x.ResourceGold = reader.ReadSingle();
x.ResourceStone = reader.ReadSingle();
if (Version >= 1.18f) {
x.Ore = reader.ReadSingle();
x.ResourceOre = reader.ReadInt32();
if (Version < 1.3f) {
x.Unknown = reader.ReadSingle();
x.UnknownResource = reader.ReadInt32();
}
}
if (Version >= 1.22f) {
@ -242,7 +244,7 @@ namespace AdrianKousz.GenieEngine
// Units
result.Units = ScnDefaultFactory.MakeUnitList(number);
result.Units = new List<Scenario.ScnUnit>[number].Fill();
result.Units.ForEach(x => {
var count = reader.ReadInt32();
@ -263,39 +265,7 @@ namespace AdrianKousz.GenieEngine
});
});
// PlayerSettings2
number = reader.ReadInt32();
result.PlayerSettings2 = result.PlayerSettings2.NewList().Fill(number - 1, factory.MakePlayerSettings2);
result.PlayerSettings2.ForEach(x => {
x.Name = reader.ReadZStringInt16();
x.CameraX = reader.ReadSingle();
x.CameraY = reader.ReadSingle();
x.UnknownX = reader.ReadInt16();
x.UnknownY = reader.ReadInt16();
x.AlliedVictory = reader.ReadByte();
number = reader.ReadInt16();
x.Stances = new Scenario.ScnPlayerSettings2.Stance[number].Fill();
x.Stances.ForEach(y => { y.Stance1 = reader.ReadByte(); });
x.Stances.ForEach(y => { y.Stance2 = reader.ReadInt32(); });
if (Version >= 1.18f) {
x.Color = reader.ReadInt32();
}
x.Unknown1 = reader.ReadSingle();
x.Unknown2 = reader.ReadInt32();
if (x.Unknown1 == 1.0f || x.Unknown1 == 2.0f) {
x.Unknown = reader.ReadBytes(1 + (int)x.Unknown1 * 8 + x.Unknown2 * 44);
} else {
var msg = string.Format("Numbers {0}/{1} for {2}", x.Unknown1, x.Unknown2, x.Name);
throw new System.NotImplementedException(msg);
}
});
// Triggers
// More Player Settings and Triggers
var bytestream = new MemoryStream();
reader.BaseStream.CopyTo(bytestream);

View File

@ -17,15 +17,13 @@ namespace AdrianKousz.GenieEngine
public void Write(ExtendedBinaryWriter writer, Scenario value)
{
EnsureValid(value);
var Version = value.VersionNumber;
var UncompressedHeaderLength = 20 + writer.GetZByteCount(value.InstructionsUncompressed);
// Uncompressed header
writer.WriteStringRaw(value.VersionString);
writer.Write((System.Int32)UncompressedHeaderLength);
writer.Write((int)UncompressedHeaderLength);
writer.Write(value.Unknown1);
writer.Write((System.Int32)DateTimes.ToUnixTime(value.Timestamp));
writer.WriteZStringInt32(value.InstructionsUncompressed);
@ -55,8 +53,8 @@ namespace AdrianKousz.GenieEngine
}
value.PlayerSettings.ForEach(x => {
writer.WriteBooleanInt32(x.Active);
writer.WriteBooleanInt32(x.Human);
writer.Write(x.Active);
writer.Write(x.Human);
writer.Write(x.Civ);
writer.Write(x.UnknownInfo);
});
@ -98,7 +96,7 @@ namespace AdrianKousz.GenieEngine
var hasBitmap = value.Cinematics.RawBitmap.Length > 0;
writer.WriteBooleanInt32(hasBitmap);
writer.Write(hasBitmap);
writer.Write(value.Cinematics.BitmapWidth);
writer.Write(value.Cinematics.BitmapHeight);
writer.Write(value.Cinematics.BitmapUnknown);
@ -115,9 +113,9 @@ namespace AdrianKousz.GenieEngine
});
value.PlayerSettings.ForEach(x => {
writer.Write((System.Int32)writer.GetByteCount(x.ScriptAI));
writer.Write((System.Int32)writer.GetByteCount(x.ScriptCTY));
writer.Write((System.Int32)writer.GetByteCount(x.ScriptPER));
writer.Write((int)writer.GetByteCount(x.ScriptAI));
writer.Write((int)writer.GetByteCount(x.ScriptCTY));
writer.Write((int)writer.GetByteCount(x.ScriptPER));
writer.WriteStringRaw(x.ScriptAI);
writer.WriteStringRaw(x.ScriptCTY);
writer.WriteStringRaw(x.ScriptPER);
@ -150,13 +148,13 @@ namespace AdrianKousz.GenieEngine
// Diplomacy
writer.WriteBooleanInt32(value.GlobalVictory.RequireConquest);
writer.Write(value.GlobalVictory.RequireConquest);
writer.Write(value.GlobalVictory.Ruins);
writer.Write(value.GlobalVictory.Artifacts);
writer.Write(value.GlobalVictory.Discovery);
writer.Write(value.GlobalVictory.PercentExplored);
writer.Write(value.GlobalVictory.Unknown);
writer.WriteBooleanInt32(value.GlobalVictory.RequireAllCustom);
writer.Write(value.GlobalVictory.RequireAllCustom);
writer.Write(value.GlobalVictory.Mode);
writer.Write(value.GlobalVictory.Score);
writer.Write(value.GlobalVictory.Time);
@ -174,7 +172,7 @@ namespace AdrianKousz.GenieEngine
WriteSeparator(writer, "Player Environment");
value.PlayerSettings.ForEach(x => {
writer.WriteBooleanInt32(x.AlliedVictory);
writer.Write(x.AlliedVictory);
});
writer.Write(value.RawDisables);
@ -205,17 +203,17 @@ namespace AdrianKousz.GenieEngine
// ResourceCopies
writer.Write((System.Int32)value.Units.Count);
writer.Write((int)value.Units.Length);
value.Resources2.ForEach(x => {
writer.Write(x.Food);
writer.Write(x.Wood);
writer.Write(x.Gold);
writer.Write(x.Stone);
value.ResourceCopies.ForEach(x => {
writer.Write(x.ResourceFood);
writer.Write(x.ResourceWood);
writer.Write(x.ResourceGold);
writer.Write(x.ResourceStone);
if (Version >= 1.18f) {
writer.Write(x.Ore);
writer.Write(x.ResourceOre);
if (Version < 1.3f) {
writer.Write(x.Unknown);
writer.Write(x.UnknownResource);
}
}
if (Version >= 1.22f) {
@ -226,7 +224,7 @@ namespace AdrianKousz.GenieEngine
// Units
value.Units.ForEach(x => {
writer.Write((System.Int32)x.Count);
writer.Write((int)x.Count);
x.ForEach(y => {
writer.Write(y.PosX);
writer.Write(y.PosY);
@ -242,31 +240,7 @@ namespace AdrianKousz.GenieEngine
});
});
// PlayerSettings2
writer.Write((System.Int32)(value.PlayerSettings2.Count + 1));
value.PlayerSettings2.ForEach(x => {
writer.WriteZStringInt16(x.Name);
writer.Write(x.CameraX);
writer.Write(x.CameraY);
writer.Write(x.UnknownX);
writer.Write(x.UnknownY);
writer.Write(x.AlliedVictory);
writer.Write((System.Int16)x.Stances.Length);
x.Stances.ForEach(y => { writer.Write(y.Stance1); });
x.Stances.ForEach(y => { writer.Write(y.Stance2); });
if (Version >= 1.18f) {
writer.Write(x.Color);
}
writer.Write(x.Unknown1);
writer.Write(x.Unknown2);
writer.Write(x.Unknown);
});
// Triggers
// More Player Settings and Triggers
writer.Write(value.RawRemaining);
@ -282,14 +256,6 @@ namespace AdrianKousz.GenieEngine
{
writer.Write(Scenario.Separator);
}
private void EnsureValid(Scenario value)
{
if (value.PlayerSettings.Count != Scenario.NumPlayers)
throw new System.InvalidOperationException("PlayerSettings.Count != 16");
if (value.Units.Count - 1 != value.Resources2.Count)
throw new System.InvalidOperationException("Conflicting unit and resource copy count");
}
}
}

View File

@ -2,4 +2,4 @@
[assembly: AssemblyTitle("ScenLib")]
[assembly: AssemblyDescription("Read and write scenario data from Genie Engine games")]
[assembly: AssemblyVersion("0.6")]
[assembly: AssemblyVersion("0.5")]