Compare commits
5 Commits
15ce34ae19
...
d1593f38c5
Author | SHA1 | Date |
---|---|---|
Adrian | d1593f38c5 | |
Adrian | 9cc13f0e8d | |
Adrian | 48929d1fbf | |
Adrian | 35d80a6585 | |
Adrian | ce92d95bd1 |
20
README.md
20
README.md
|
@ -10,25 +10,37 @@ This includes the *Age of Empires* series and *Star Wars: Galactic Battlegrounds
|
|||
|
||||
## Features
|
||||
|
||||
* Create maps from bitmaps
|
||||
* Convert AOE1 Scenario to AOE2 Scenario
|
||||
* Convert a Scenario file to JSON and vice-versa
|
||||
* Decompress and recompress compressed Scenario part for hex editing
|
||||
* Dump unit information to easily gather Unit IDs
|
||||
* Extract Scenario files from Campaign file
|
||||
* Full Campaign file editing
|
||||
|
||||
## Planned Features
|
||||
|
||||
* Create maps from bitmaps
|
||||
* Trigger implementation
|
||||
* Full conversion between all Scenario versions
|
||||
(mapping of Unit IDs are needed for this, see Section Contributing)
|
||||
|
||||
## Examples
|
||||
|
||||
### Map Generation
|
||||
|
||||
![Generated Map](doc/GeneratedMap.jpg)
|
||||
|
||||
The above map is generated using:
|
||||
|
||||
$ GenieEdit.exe bmp2map files/examples/aoe2x-Empty.scx Europe.scx /tmp/terrain.png /tmp/elevation.png
|
||||
|
||||
The images are taken from [NASA's Blue Marble](http://visibleearth.nasa.gov/) imagery.
|
||||
|
||||
### Scenario Conversion
|
||||
|
||||
[OpeningMoves-AOE2.scx](files/OpeningMoves-AOE2.scx)
|
||||
is a fully automated conversion of a Scenario file from the AOE1 demo version:
|
||||
|
||||
$ GenieEdit.exe convert files/examples/aoe2x-Empty.scx "files/examples/aoe1demo-Reigno_1-Opening Moves.scn" files/OpeningMoves-AOE2.scx
|
||||
$ GenieEdit.exe convert "files/examples/aoe1demo-Reigno_1-Opening Moves.scn" files/OpeningMoves-AOE2.scx files/examples/aoe2x-Empty.scx
|
||||
|
||||
(Stone are set to 100 to be able to build a town center from the beginning.)
|
||||
|
||||
|
@ -47,7 +59,7 @@ A unit dump looks like the following:
|
|||
# Library
|
||||
|
||||
The namespace `AdrianKousz.GenieEngine` contains classes
|
||||
to read, write, and manipulate Scenario files.
|
||||
to read, write, and manipulate Scenario and Campaign files.
|
||||
|
||||
`Scenario` contains all data structures related to a Scenario file.
|
||||
They are easily understandable.
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 80 KiB |
|
@ -19,15 +19,16 @@
|
|||
<WarningLevel>4</WarningLevel>
|
||||
<Externalconsole>true</Externalconsole>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>..\..\bin\Release</OutputPath>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<Externalconsole>true</Externalconsole>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="src\Program.cs" />
|
||||
|
@ -40,6 +41,11 @@
|
|||
<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" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<ItemGroup>
|
||||
|
@ -65,6 +71,7 @@
|
|||
<Reference Include="Newtonsoft.Json">
|
||||
<HintPath>..\packages\Newtonsoft.Json.8.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Drawing" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
using System.IO;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using AdrianKousz.Util;
|
||||
|
||||
namespace AdrianKousz.GenieEngine
|
||||
{
|
||||
public class AddBitmapCommand : BaseCommand
|
||||
{
|
||||
override public void Run()
|
||||
{
|
||||
using (var input = File.OpenRead(args[0]))
|
||||
using (var output = File.OpenWrite(args[1]))
|
||||
using (var bmpstream = File.OpenRead(args[2]))
|
||||
{
|
||||
var scn = GenieFile.Deserialize<Scenario>(input);
|
||||
var bmp = (Bitmap)Image.FromStream(bmpstream);
|
||||
var cin = scn.Cinematics;
|
||||
cin.BitmapWidth = bmp.Width;
|
||||
cin.BitmapHeight = bmp.Height;
|
||||
cin.BitmapUnknown = Scenario.ScnCinematics.ExpectedUnknownBitmapTrue;
|
||||
cin.RawBitmap = bmp.ToRawBitmap();
|
||||
GenieFile.Serialize(scn, output);
|
||||
}
|
||||
}
|
||||
|
||||
override public int GetArgumentCount()
|
||||
{
|
||||
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"
|
||||
+ "\nParameters:"
|
||||
+ "\n<input scenario> <output scenario> <bitmap>"
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using AdrianKousz.Util;
|
||||
|
||||
namespace AdrianKousz.GenieEngine
|
||||
{
|
||||
public static class BitmapHelpers
|
||||
{
|
||||
public static unsafe void FillMap(Scenario scn, Bitmap bmp, Bitmap bmp2)
|
||||
{
|
||||
using (var ubmp = new UnsafeBitmap(bmp))
|
||||
using (var ubmp2 = new UnsafeBitmap(bmp2))
|
||||
{
|
||||
var factory = new ScnDefaultFactory();
|
||||
|
||||
Check(ubmp);
|
||||
Check(ubmp2);
|
||||
|
||||
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 = ubmp.Width;
|
||||
map.SizeY = ubmp.Height;
|
||||
map.LinearTiles = new Scenario.ScnMap.Tile[map.SizeX * map.SizeY];
|
||||
|
||||
var gaiaunits = new List<Scenario.ScnUnit>();
|
||||
|
||||
var treemapping = new short[256];
|
||||
treemapping[10] = 411;
|
||||
treemapping[13] = 351;
|
||||
treemapping[17] = 414;
|
||||
treemapping[18] = 348;
|
||||
treemapping[19] = 350;
|
||||
treemapping[20] = 349;
|
||||
|
||||
var i = 0;
|
||||
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.UnitId = treeid;
|
||||
unit.PosX = posx + 0.5f;
|
||||
unit.PosY = posy + 0.5f;
|
||||
unit.Rotation = (short)Util.Math.Rand(0, 13);
|
||||
gaiaunits.Add(unit);
|
||||
}
|
||||
map.LinearTiles[i++] = new Scenario.ScnMap.Tile(*ptr++, (byte)(*ptr2++ / 37), 0);
|
||||
}
|
||||
|
||||
scn.Map = map;
|
||||
scn.Units[0] = gaiaunits;
|
||||
}
|
||||
}
|
||||
|
||||
private static void Check(UnsafeBitmap ubmp)
|
||||
{
|
||||
if (ubmp.PixelFormat != PixelFormat.Format8bppIndexed)
|
||||
throw new ArgumentException("Unsupported PixelFormat");
|
||||
if (ubmp.Stride != ubmp.Width)
|
||||
throw new NotImplementedException("bmpbit.Stride != bmpbit.Width");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Drawing;
|
||||
using AdrianKousz.Util;
|
||||
|
||||
namespace AdrianKousz.GenieEngine
|
||||
{
|
||||
public class Bmp2MapCommand : BaseCommand
|
||||
{
|
||||
override public void Run()
|
||||
{
|
||||
using (var srcstream = File.OpenRead(args[0]))
|
||||
using (var outstream = File.OpenWrite(args[1]))
|
||||
using (var bmpstream = File.OpenRead(args[2]))
|
||||
using (var bmp2stream = File.OpenRead(args[3]))
|
||||
{
|
||||
var bmp = (Bitmap)Image.FromStream(bmpstream);
|
||||
var bmp2 = (Bitmap)Image.FromStream(bmp2stream);
|
||||
|
||||
var scn = GenieFile.Deserialize<Scenario>(srcstream);
|
||||
BitmapHelpers.FillMap(scn, bmp, bmp2);
|
||||
GenieFile.Serialize(scn, outstream);
|
||||
}
|
||||
}
|
||||
|
||||
override public int GetArgumentCount()
|
||||
{
|
||||
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 37 to generate elevations 0-6."
|
||||
+ "\n"
|
||||
+ "\nParameters:"
|
||||
+ "\n<input scenario> <output scenario> <terrain bitmap> <elevation bitmap>"
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
using System.IO;
|
||||
using System.IO.Compression;
|
||||
|
||||
namespace AdrianKousz.GenieEngine
|
||||
{
|
||||
|
@ -23,5 +22,14 @@ namespace AdrianKousz.GenieEngine
|
|||
{
|
||||
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>"
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,9 +8,9 @@ namespace AdrianKousz.GenieEngine
|
|||
{
|
||||
override public void Run()
|
||||
{
|
||||
using (var refstream = File.OpenRead(args[0]))
|
||||
using (var srcstream = File.OpenRead(args[1]))
|
||||
using (var outstream = File.OpenWrite(args[2]))
|
||||
using (var srcstream = File.OpenRead(args[0]))
|
||||
using (var outstream = File.OpenWrite(args[1]))
|
||||
using (var refstream = File.OpenRead(args[2]))
|
||||
{
|
||||
var refscn = GenieFile.Deserialize<Scenario>(refstream);
|
||||
var srcscn = GenieFile.Deserialize<Scenario>(srcstream);
|
||||
|
@ -22,8 +22,7 @@ namespace AdrianKousz.GenieEngine
|
|||
srcscn.RawRemaining = refscn.RawRemaining;
|
||||
|
||||
srcscn.PlayerSettings.ForEach(x => {
|
||||
x.FilenameAI = "RandomGame";
|
||||
x.FilenameCTY = x.FilenamePER = "";
|
||||
x.FilenameAI = x.FilenameCTY = x.FilenamePER = "RandomGame";
|
||||
x.ScriptAI = x.ScriptCTY = x.ScriptPER = "";
|
||||
});
|
||||
|
||||
|
@ -48,8 +47,10 @@ namespace AdrianKousz.GenieEngine
|
|||
{
|
||||
return "Convert AOE1 scenario to AOE2"
|
||||
+ "\n"
|
||||
+ "\nThe converter needs an existing AOE2 scenario to start with. Use the following arguments:"
|
||||
+ "\n<AOE2 reference scenario> <AOE1 scenario> <output scenario>"
|
||||
+ "\nThe converter needs an existing AOE2 scenario to start with."
|
||||
+ "\n"
|
||||
+ "\nParameters:"
|
||||
+ "\n<AOE1 scenario> <output scenario> <AOE2 reference scenario>"
|
||||
;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,9 +26,9 @@ namespace AdrianKousz.GenieEngine
|
|||
// Other Buildings
|
||||
109, 68, 103,
|
||||
};
|
||||
units.ForEach((i, list) => {
|
||||
list.ForEach((j, unit) => {
|
||||
mapping.ForEach((k, buildingid) => {
|
||||
units.ForEach(list => {
|
||||
list.ForEach(unit => {
|
||||
mapping.ForEach(buildingid => {
|
||||
if (unit.UnitId == buildingid) {
|
||||
unit.PosX -= 0.5f;
|
||||
unit.PosY -= 0.5f;
|
||||
|
@ -43,7 +43,7 @@ namespace AdrianKousz.GenieEngine
|
|||
*/
|
||||
private List<Scenario.ScnUnit>[] ChangeUnits_AOE1_AOE2(IList<Scenario.ScnUnit>[] units)
|
||||
{
|
||||
var array = new int[] {
|
||||
var mapping = new short[] {
|
||||
// Resources
|
||||
66, 66,
|
||||
59, 59,
|
||||
|
@ -139,22 +139,15 @@ namespace AdrianKousz.GenieEngine
|
|||
114, 114,
|
||||
121, 121,
|
||||
129, 129,
|
||||
};
|
||||
var mapping = new Dictionary<short, short>();
|
||||
var ai = 0;
|
||||
while (ai < array.Length)
|
||||
mapping.Add((short)array[ai++], (short)array[ai++]);
|
||||
}.ToDictionary();
|
||||
|
||||
var result = new List<Scenario.ScnUnit>[Scenario.NumPlayerSections].Fill();
|
||||
|
||||
units.ForEach((i, list) => {
|
||||
list.ForEach((j, unit) => {
|
||||
if (mapping.ContainsKey(unit.UnitId)) {
|
||||
unit.UnitId = mapping[unit.UnitId];
|
||||
result[i].Add(unit);
|
||||
}
|
||||
});
|
||||
});
|
||||
var result = units.Map(list => {
|
||||
return list
|
||||
.Filter(unit => mapping.ContainsKey(unit.UnitId))
|
||||
.ForEach(unit => {
|
||||
unit.UnitId = mapping[unit.UnitId];
|
||||
}).ToList();
|
||||
}).ToArray();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -164,8 +157,8 @@ namespace AdrianKousz.GenieEngine
|
|||
*/
|
||||
private void ChangeTiles_AOE1_AOE2(IList<Scenario.ScnUnit>[] units, Scenario.ScnMap map)
|
||||
{
|
||||
units.ForEach((i, list) => {
|
||||
list.ForEach((j, unit) => {
|
||||
units.ForEach(list => {
|
||||
list.ForEach(unit => {
|
||||
var posx = (int)(unit.PosX - 0.5);
|
||||
var posy = (int)(unit.PosY - 0.5);
|
||||
var linearpos = posy * map.SizeX + posx;
|
||||
|
@ -203,12 +196,11 @@ namespace AdrianKousz.GenieEngine
|
|||
var treeids = new int[] {
|
||||
411, 351, 414, 350, 348, 349,
|
||||
};
|
||||
units.ForEach((i, list) => {
|
||||
list.ForEach((j, unit) => {
|
||||
treeids.ForEach((k, treeid) => {
|
||||
units.ForEach(list => {
|
||||
list.ForEach(unit => {
|
||||
treeids.ForEach(treeid => {
|
||||
if (unit.UnitId == treeid) {
|
||||
unit.InitialFrame = (short)Util.Math.Rand(0, 13); // Whats the maximum?
|
||||
unit.Rotation = unit.InitialFrame;
|
||||
unit.Rotation = (short)Util.Math.Rand(0, 13); // Whats the maximum?
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -20,7 +20,16 @@ namespace AdrianKousz.GenieEngine
|
|||
|
||||
override public string GetDescription()
|
||||
{
|
||||
return "Decompress a scenario file for hex editing";
|
||||
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>"
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
using System.IO;
|
||||
using System.Drawing;
|
||||
using AdrianKousz.Util;
|
||||
|
||||
namespace AdrianKousz.GenieEngine
|
||||
{
|
||||
public class DelBitmapCommand : BaseCommand
|
||||
{
|
||||
override public void Run()
|
||||
{
|
||||
using (var input = File.OpenRead(args[0]))
|
||||
using (var output = File.OpenWrite(args[1]))
|
||||
{
|
||||
var scn = GenieFile.Deserialize<Scenario>(input);
|
||||
var cin = scn.Cinematics;
|
||||
cin.BitmapWidth = 0;
|
||||
cin.BitmapHeight = 0;
|
||||
cin.BitmapUnknown = Scenario.ScnCinematics.ExpectedUnknownBitmapFalse;
|
||||
cin.RawBitmap = new byte[0];
|
||||
GenieFile.Serialize(scn, output);
|
||||
}
|
||||
}
|
||||
|
||||
override public int GetArgumentCount()
|
||||
{
|
||||
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>"
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,9 +17,10 @@ namespace AdrianKousz.GenieEngine
|
|||
|
||||
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 i = 0;
|
||||
scn.Units.ForEach(list => {
|
||||
Console.WriteLine("Units of player {0}:", i++);
|
||||
list.ForEach(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];
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using AdrianKousz.Util;
|
||||
|
||||
namespace AdrianKousz.GenieEngine
|
||||
{
|
||||
|
@ -6,9 +8,17 @@ namespace AdrianKousz.GenieEngine
|
|||
{
|
||||
override public void Run()
|
||||
{
|
||||
using (var file = File.OpenRead(args[0])) {
|
||||
var reader = new CpnReader(file);
|
||||
reader.ExtractFiles();
|
||||
using (var file = File.OpenRead(args[0]))
|
||||
{
|
||||
var cpn = GenieFile.Deserialize<Campaign>(file);
|
||||
Console.WriteLine("Extracting \"{0}\"...", cpn.Name);
|
||||
cpn.Files.ForEach(x => {
|
||||
using (var outfile = File.OpenWrite(x.Filename))
|
||||
{
|
||||
Console.WriteLine("Extracting \"{0}\" to \"{1}\"...", x.Name, x.Filename);
|
||||
outfile.Write(x.RawData);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,7 +29,7 @@ namespace AdrianKousz.GenieEngine
|
|||
|
||||
override public string GetDescription()
|
||||
{
|
||||
return "Extract scenarios from campaign file";
|
||||
return "Extract files from campaign file";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,16 @@ namespace AdrianKousz.GenieEngine
|
|||
|
||||
override public string GetDescription()
|
||||
{
|
||||
return "Read a JSON file and save as scenario";
|
||||
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>"
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
using System.IO;
|
||||
using System.Collections.Generic;
|
||||
using AdrianKousz.Util;
|
||||
|
||||
namespace AdrianKousz.GenieEngine
|
||||
{
|
||||
public class PackCommand : BaseCommand
|
||||
{
|
||||
override public void Run()
|
||||
{
|
||||
var files = new List<Campaign.CpnFile>();
|
||||
for (var i = 2; i < args.Length; i++) {
|
||||
var file = new Campaign.CpnFile();
|
||||
file.Filename = args[i];
|
||||
file.Name = Path.GetFileNameWithoutExtension(file.Filename);
|
||||
using (var stream = File.OpenRead(file.Filename))
|
||||
using (var memorystream = new MemoryStream())
|
||||
{
|
||||
stream.CopyTo(memorystream);
|
||||
file.RawData = memorystream.ToArray();
|
||||
}
|
||||
files.Add(file);
|
||||
}
|
||||
|
||||
var cpn = new Campaign();
|
||||
cpn.Files = files;
|
||||
cpn.Name = args[1];
|
||||
cpn.Version = "1.00";
|
||||
|
||||
using (var file = File.OpenWrite(args[0]))
|
||||
{
|
||||
GenieFile.Serialize(cpn, file);
|
||||
}
|
||||
}
|
||||
|
||||
override public int GetArgumentCount()
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
||||
override public string GetDescription()
|
||||
{
|
||||
return "Pack files into a campaign file";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ namespace AdrianKousz.GenieEngine
|
|||
public static void Main(string[] args)
|
||||
{
|
||||
System.Threading.Thread.CurrentThread.Name = "main";
|
||||
Log.SetUncaughtExceptionHandler();
|
||||
|
||||
if (System.Diagnostics.Debugger.IsAttached) {
|
||||
debug();
|
||||
|
@ -26,12 +27,16 @@ namespace AdrianKousz.GenieEngine
|
|||
{
|
||||
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("extract", new ExtractCommand());
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,16 @@ namespace AdrianKousz.GenieEngine
|
|||
|
||||
override public string GetDescription()
|
||||
{
|
||||
return "Dump a scenario file to JSON";
|
||||
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>"
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
using System.IO;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using AdrianKousz.Util;
|
||||
|
||||
namespace AdrianKousz.GenieEngine
|
||||
{
|
||||
|
@ -11,11 +12,11 @@ namespace AdrianKousz.GenieEngine
|
|||
{
|
||||
EnsureLittleEndian();
|
||||
|
||||
var temp = new byte[40];
|
||||
input.Read(temp, 0, temp.Length);
|
||||
var temp = input.ReadBytes(40);
|
||||
|
||||
var dibHeaderLength = BitConverter.ToInt32(temp, 0);
|
||||
var paletteLength = BitConverter.ToInt32(temp, 32) * 4;
|
||||
var bitDepth = BitConverter.ToInt32(temp, 14);
|
||||
var paletteLength = bitDepth > 8 ? 0 : (1 << bitDepth) * 4;
|
||||
var pixelArrayLength = BitConverter.ToInt32(temp, 20);
|
||||
var resultLength = dibHeaderLength + paletteLength + pixelArrayLength;
|
||||
|
||||
|
@ -37,7 +38,8 @@ namespace AdrianKousz.GenieEngine
|
|||
|
||||
var bmpHeaderLength = 14;
|
||||
var dibHeaderLength = BitConverter.ToInt32(input, 0);
|
||||
var paletteLength = BitConverter.ToInt32(input, 32) * 4;
|
||||
var bitDepth = BitConverter.ToInt32(input, 14);
|
||||
var paletteLength = bitDepth > 8 ? 0 : (1 << bitDepth) * 4;
|
||||
var pixelArrayOffsetOutput = bmpHeaderLength + dibHeaderLength + paletteLength;
|
||||
var resultLength = input.Length + bmpHeaderLength;
|
||||
|
||||
|
@ -61,10 +63,12 @@ namespace AdrianKousz.GenieEngine
|
|||
var bmpHeaderLength = 14;
|
||||
var tempstream = new MemoryStream();
|
||||
input.Save(tempstream, ImageFormat.Bmp);
|
||||
tempstream.Dispose();
|
||||
var temp = tempstream.ToArray();
|
||||
|
||||
var dibHeaderLength = BitConverter.ToInt32(temp, 0 + bmpHeaderLength);
|
||||
var paletteLength = BitConverter.ToInt32(temp, 32 + bmpHeaderLength) * 4;
|
||||
var bitDepth = BitConverter.ToInt32(temp, 14 + bmpHeaderLength);
|
||||
var paletteLength = bitDepth > 8 ? 0 : (1 << bitDepth) * 4;
|
||||
var pixelArrayOffsetInput = BitConverter.ToInt32(temp, 10);
|
||||
var pixelArrayLength = temp.Length - pixelArrayOffsetInput;
|
||||
var resultLength = dibHeaderLength + paletteLength + pixelArrayLength;
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace AdrianKousz.GenieEngine
|
||||
{
|
||||
public class Campaign
|
||||
{
|
||||
public String Version;
|
||||
public String Name;
|
||||
public IList<CpnFile> Files;
|
||||
|
||||
public class CpnFile
|
||||
{
|
||||
public String Name;
|
||||
public String Filename;
|
||||
public byte[] RawData;
|
||||
public Scenario Data;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
using System.IO;
|
||||
using AdrianKousz.Util;
|
||||
|
||||
namespace AdrianKousz.GenieEngine
|
||||
{
|
||||
public class CpnReader
|
||||
{
|
||||
private static readonly string TAG = typeof(CpnReader).Name;
|
||||
|
||||
private ExtendedBinaryReader reader;
|
||||
|
||||
public CpnReader(Stream input)
|
||||
{
|
||||
reader = new ExtendedBinaryReader(input, Scenario.Encoding);
|
||||
}
|
||||
|
||||
public void ExtractFiles()
|
||||
{
|
||||
var version = reader.ReadString(4);
|
||||
if (!version.Equals("1.00")) {
|
||||
throw new System.NotSupportedException(version);
|
||||
}
|
||||
|
||||
var name = reader.ReadZString(256);
|
||||
var count = reader.ReadInt32();
|
||||
Log.i(TAG, "Extract scenarios from \"{0}\"", name);
|
||||
|
||||
var size = new int[count];
|
||||
var offset = new int[count];
|
||||
var scenname = new string[count];
|
||||
var filename = new string[count];
|
||||
|
||||
for (var i = 0; i < count; i++) {
|
||||
size[i] = reader.ReadInt32();
|
||||
offset[i] = reader.ReadInt32();
|
||||
scenname[i] = reader.ReadZString(255);
|
||||
filename[i] = reader.ReadZString(257);
|
||||
}
|
||||
|
||||
for (var i = 0; i < count; i++) {
|
||||
Log.i(TAG, "Extract scenario {0} \"{1}\" to {2}", i, scenname[i], filename[i]);
|
||||
|
||||
using (var file = File.OpenWrite(filename[i])) {
|
||||
reader.BaseStream.Seek(offset[i], SeekOrigin.Begin);
|
||||
var data = reader.ReadBytes(size[i]);
|
||||
file.Write(data, 0, data.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using AdrianKousz.Util;
|
||||
|
||||
namespace AdrianKousz.GenieEngine
|
||||
{
|
||||
internal class CpnSerializerReader
|
||||
{
|
||||
private static readonly string TAG = typeof(CpnSerializerReader).Name;
|
||||
|
||||
public static CpnSerializerReader CreateDefault()
|
||||
{
|
||||
return new CpnSerializerReader();
|
||||
}
|
||||
|
||||
private CpnSerializerReader()
|
||||
{
|
||||
}
|
||||
|
||||
public Campaign ReadCampaign(ExtendedBinaryReader reader)
|
||||
{
|
||||
var number = 0;
|
||||
var result = new Campaign();
|
||||
|
||||
result.Version = reader.ReadString(4);
|
||||
if (!result.Version.Equals("1.00")) {
|
||||
Log.w(TAG, "Continue reading unsupported version {0}", result.Version);
|
||||
}
|
||||
|
||||
result.Name = reader.ReadZString(256);
|
||||
number = reader.ReadInt32();
|
||||
|
||||
result.Files = new PositionInfo()
|
||||
.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();
|
||||
reader.BaseStream.Seek(x.Offset, SeekOrigin.Begin);
|
||||
file.RawData = reader.ReadBytes(x.Size);
|
||||
file.Name = x.Name;
|
||||
file.Filename = x.Filename;
|
||||
return file;
|
||||
})
|
||||
.ToArray();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private class PositionInfo
|
||||
{
|
||||
public int Size;
|
||||
public int Offset;
|
||||
public string Name;
|
||||
public string Filename;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
using System;
|
||||
using AdrianKousz.Util;
|
||||
|
||||
namespace AdrianKousz.GenieEngine
|
||||
{
|
||||
internal class CpnSerializerWriter
|
||||
{
|
||||
private static readonly string TAG = typeof(CpnSerializerWriter).Name;
|
||||
|
||||
public static CpnSerializerWriter CreateDefault()
|
||||
{
|
||||
return new CpnSerializerWriter();
|
||||
}
|
||||
|
||||
private CpnSerializerWriter()
|
||||
{
|
||||
}
|
||||
|
||||
public void Write(ExtendedBinaryWriter writer, Campaign value)
|
||||
{
|
||||
var number = 0;
|
||||
|
||||
if (!value.Version.Equals("1.00")) {
|
||||
Log.w(TAG, "Continue writing unsupported version {0}", value.Version);
|
||||
}
|
||||
|
||||
var headerSize = 8 + 256;
|
||||
var listingSize = 8 + 255 + 257;
|
||||
var listingLength = listingSize * value.Files.Count;
|
||||
|
||||
number = headerSize + listingLength;
|
||||
|
||||
writer.WriteStringRaw(value.Version);
|
||||
writer.WriteZString(value.Name, 256);
|
||||
writer.Write((Int32)value.Files.Count);
|
||||
|
||||
value.Files.ForEach(x => {
|
||||
writer.Write((Int32)x.RawData.Length);
|
||||
writer.Write((Int32)number);
|
||||
writer.WriteZString(x.Name, 255);
|
||||
writer.WriteZString(x.Filename, 257);
|
||||
number += x.RawData.Length;
|
||||
});
|
||||
|
||||
value.Files.ForEach(x => {
|
||||
writer.Write(x.RawData);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,13 +21,22 @@ namespace AdrianKousz.GenieEngine
|
|||
|
||||
public static void Serialize<T>(T value, Stream output)
|
||||
{
|
||||
var scenarioValue = value as Scenario;
|
||||
if (scenarioValue != null) {
|
||||
var scnValue = value as Scenario;
|
||||
if (scnValue != null) {
|
||||
var scnwriter = ScnSerializerWriter.CreateDefault();
|
||||
using (var stream = new NonDisposingStreamWrapper(output))
|
||||
using (var writer = GetWriter(stream))
|
||||
using (var writer = GetWriter(output))
|
||||
{
|
||||
scnwriter.Write(writer, scenarioValue);
|
||||
scnwriter.Write(writer, scnValue);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var cpnValue = value as Campaign;
|
||||
if (cpnValue != null) {
|
||||
var cpnwriter = CpnSerializerWriter.CreateDefault();
|
||||
using (var writer = GetWriter(output))
|
||||
{
|
||||
cpnwriter.Write(writer, cpnValue);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -61,13 +70,20 @@ namespace AdrianKousz.GenieEngine
|
|||
{
|
||||
if (type == typeof(Scenario)) {
|
||||
var scnreader = ScnSerializerReader.CreateDefault();
|
||||
using (var stream = new NonDisposingStreamWrapper(input))
|
||||
using (var reader = GetReader(stream))
|
||||
using (var reader = GetReader(input))
|
||||
{
|
||||
return scnreader.ReadScenario(reader);
|
||||
}
|
||||
}
|
||||
|
||||
if (type == typeof(Campaign)) {
|
||||
var cpnreader = CpnSerializerReader.CreateDefault();
|
||||
using (var reader = GetReader(input))
|
||||
{
|
||||
return cpnreader.ReadCampaign(reader);
|
||||
}
|
||||
}
|
||||
|
||||
throw new System.ArgumentException("Unsupported Type");
|
||||
}
|
||||
|
||||
|
@ -77,12 +93,12 @@ namespace AdrianKousz.GenieEngine
|
|||
|
||||
public static ExtendedBinaryReader GetReader(Stream stream)
|
||||
{
|
||||
return new ExtendedBinaryReader(stream, Scenario.Encoding);
|
||||
return new ExtendedBinaryReader(new NonDisposingStreamWrapper(stream), Scenario.Encoding);
|
||||
}
|
||||
|
||||
public static ExtendedBinaryWriter GetWriter(Stream stream)
|
||||
{
|
||||
return new ExtendedBinaryWriter(stream, Scenario.Encoding);
|
||||
return new ExtendedBinaryWriter(new NonDisposingStreamWrapper(stream), Scenario.Encoding);
|
||||
}
|
||||
|
||||
public static void ScenarioCompression(Stream input, Stream output, bool decompress)
|
||||
|
@ -98,7 +114,7 @@ namespace AdrianKousz.GenieEngine
|
|||
| buffer[5] << 8
|
||||
| buffer[6] << 16
|
||||
| buffer[7] << 24
|
||||
;
|
||||
;
|
||||
|
||||
buffer = input.ReadBytes(headerLength);
|
||||
output.Write(buffer);
|
||||
|
|
|
@ -14,7 +14,7 @@ namespace AdrianKousz.GenieEngine
|
|||
public const Int32 ExpectedUnknown1 = 2;
|
||||
public const Int32 ExpectedUnknown2 = 0;
|
||||
public const Int32 ExpectedUnknown3 = 1;
|
||||
public const SByte ExpectedUnknown4 = 0;
|
||||
public const Byte ExpectedUnknown4 = 0;
|
||||
public const Single ExpectedUnknown5 = -1;
|
||||
|
||||
public String VersionString;
|
||||
|
@ -43,7 +43,7 @@ namespace AdrianKousz.GenieEngine
|
|||
public Int32 Unknown1;
|
||||
public Int32 Unknown2;
|
||||
public Int32 Unknown3;
|
||||
public SByte Unknown4;
|
||||
public Byte Unknown4;
|
||||
public Single Unknown5;
|
||||
|
||||
public class ScnPlayerSettings
|
||||
|
@ -64,7 +64,7 @@ namespace AdrianKousz.GenieEngine
|
|||
public String ScriptAI;
|
||||
public String ScriptCTY;
|
||||
public String ScriptPER;
|
||||
public SByte AIType;
|
||||
public Byte AIType;
|
||||
|
||||
public Int32 ResourceWood;
|
||||
public Int32 ResourceFood;
|
||||
|
@ -98,6 +98,9 @@ namespace AdrianKousz.GenieEngine
|
|||
|
||||
public class ScnCinematics
|
||||
{
|
||||
public const Int16 ExpectedUnknownBitmapTrue = -1;
|
||||
public const Int16 ExpectedUnknownBitmapFalse = 1;
|
||||
|
||||
public String FilenamePregame;
|
||||
public String FilenameVictory;
|
||||
public String FilenameLoss;
|
||||
|
@ -148,13 +151,13 @@ namespace AdrianKousz.GenieEngine
|
|||
|
||||
public struct Tile
|
||||
{
|
||||
public const Int32 ExpectedUnknown = 0;
|
||||
public const Byte ExpectedUnknown = 0;
|
||||
|
||||
public SByte Id;
|
||||
public SByte Elevation;
|
||||
public SByte Unknown;
|
||||
public Byte Id;
|
||||
public Byte Elevation;
|
||||
public Byte Unknown;
|
||||
|
||||
public Tile(SByte id, SByte el, SByte un)
|
||||
public Tile(Byte id, Byte el, Byte un)
|
||||
{
|
||||
Id = id;
|
||||
Elevation = el;
|
||||
|
@ -166,7 +169,7 @@ namespace AdrianKousz.GenieEngine
|
|||
public class ScnUnit
|
||||
{
|
||||
public const Single ExpectedUnknown1 = 1;
|
||||
public const SByte ExpectedUnknown2 = 2;
|
||||
public const Byte ExpectedUnknown2 = 2;
|
||||
|
||||
public Single PosX;
|
||||
public Single PosY;
|
||||
|
@ -176,7 +179,7 @@ namespace AdrianKousz.GenieEngine
|
|||
public Int16 InitialFrame;
|
||||
public Int32 GarrisonnedInId;
|
||||
public Single Unknown1;
|
||||
public SByte Unknown2;
|
||||
public Byte Unknown2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,12 +50,9 @@ namespace AdrianKousz.GenieEngine
|
|||
result.UnknownInfo = Scenario.ScnPlayerSettings.ExpectedUnknownInfo;
|
||||
result.UnknownResource = Scenario.ScnPlayerSettings.ExpectedUnknownResource;
|
||||
result.Name = "Player";
|
||||
result.FilenameAI = "";
|
||||
result.FilenameCTY = "";
|
||||
result.FilenamePER = "";
|
||||
result.ScriptAI = "";
|
||||
result.ScriptCTY = "";
|
||||
result.ScriptPER = "";
|
||||
result.NameStringID = -1;
|
||||
result.FilenameAI = result.FilenameCTY = result.FilenamePER = "RandomGame";
|
||||
result.ScriptAI = result.ScriptCTY = result.ScriptPER = "";
|
||||
result.AIType = 1;
|
||||
result.ResourceWood = 200;
|
||||
result.ResourceFood = 200;
|
||||
|
@ -91,6 +88,7 @@ namespace AdrianKousz.GenieEngine
|
|||
result.FilenameVictory = "";
|
||||
result.FilenameLoss = "";
|
||||
result.FilenameBitmap = "";
|
||||
result.BitmapUnknown = Scenario.ScnCinematics.ExpectedUnknownBitmapFalse;
|
||||
result.RawBitmap = new byte[0];
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ namespace AdrianKousz.GenieEngine
|
|||
});
|
||||
|
||||
result.Unknown3 = reader.ReadInt32();
|
||||
result.Unknown4 = reader.ReadSByte();
|
||||
result.Unknown4 = reader.ReadByte();
|
||||
result.Unknown5 = reader.ReadSingle();
|
||||
|
||||
result.Filename = reader.ReadStringInt16();
|
||||
|
@ -139,7 +139,7 @@ namespace AdrianKousz.GenieEngine
|
|||
|
||||
if (Version >= 1.18f) {
|
||||
result.PlayerSettings.ForEach(x => {
|
||||
x.AIType = reader.ReadSByte();
|
||||
x.AIType = reader.ReadByte();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -217,7 +217,7 @@ namespace AdrianKousz.GenieEngine
|
|||
result.Map.SizeY = reader.ReadInt32();
|
||||
|
||||
result.Map.LinearTiles = new Scenario.ScnMap.Tile[result.Map.SizeX * result.Map.SizeY]
|
||||
.Fill(() => new Scenario.ScnMap.Tile(reader.ReadSByte(), reader.ReadSByte(), reader.ReadSByte()));
|
||||
.Fill(() => new Scenario.ScnMap.Tile(reader.ReadByte(), reader.ReadByte(), reader.ReadByte()));
|
||||
|
||||
// ResourceCopies
|
||||
|
||||
|
@ -248,21 +248,21 @@ namespace AdrianKousz.GenieEngine
|
|||
|
||||
result.Units.ForEach(x => {
|
||||
var count = reader.ReadInt32();
|
||||
x.Fill(() => {
|
||||
x.Fill(count, () => {
|
||||
var unit = factory.MakeUnit();
|
||||
unit.PosX = reader.ReadSingle();
|
||||
unit.PosY = reader.ReadSingle();
|
||||
unit.Unknown1 = reader.ReadSingle();
|
||||
unit.Id = reader.ReadInt32();
|
||||
unit.UnitId = reader.ReadInt16();
|
||||
unit.Unknown2 = reader.ReadSByte();
|
||||
unit.Unknown2 = reader.ReadByte();
|
||||
unit.Rotation = reader.ReadSingle();
|
||||
if (Version >= 1.18f) {
|
||||
unit.InitialFrame = reader.ReadInt16();
|
||||
unit.GarrisonnedInId = reader.ReadInt32();
|
||||
}
|
||||
return unit;
|
||||
}, count);
|
||||
});
|
||||
});
|
||||
|
||||
// More Player Settings and Triggers
|
||||
|
|
|
@ -43,7 +43,7 @@ namespace AdrianKousz.GenieEngine
|
|||
// PlayerSettings 1
|
||||
|
||||
value.PlayerSettings.ForEach(x => {
|
||||
writer.WriteStringPadded(x.Name, 256);
|
||||
writer.WriteZString(x.Name, 256);
|
||||
});
|
||||
|
||||
if (Version >= 1.18f) {
|
||||
|
|
|
@ -33,7 +33,6 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="AdrianKousz.GenieEngine\BitmapUtil.cs" />
|
||||
<Compile Include="AdrianKousz.GenieEngine\CpnReader.cs" />
|
||||
<Compile Include="AssemblyInfo.cs" />
|
||||
<Compile Include="AdrianKousz.GenieEngine\ScnDefaultFactory.cs" />
|
||||
<Compile Include="AdrianKousz.GenieEngine\IScnFactory.cs" />
|
||||
|
@ -41,6 +40,9 @@
|
|||
<Compile Include="AdrianKousz.GenieEngine\Scenario.cs" />
|
||||
<Compile Include="AdrianKousz.GenieEngine\ScnSerializerReader.cs" />
|
||||
<Compile Include="AdrianKousz.GenieEngine\GenieFile.cs" />
|
||||
<Compile Include="AdrianKousz.GenieEngine\Campaign.cs" />
|
||||
<Compile Include="AdrianKousz.GenieEngine\CpnSerializerReader.cs" />
|
||||
<Compile Include="AdrianKousz.GenieEngine\CpnSerializerWriter.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<ItemGroup>
|
||||
|
|
Loading…
Reference in New Issue