diff --git a/Makefile b/Makefile index 9a0da21f90..5a1dd680ec 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ CSC = gmcs CSFLAGS = -nologo -warn:4 -debug:+ -debug:full -optimize- -codepage:utf8 -unsafe DEFINE = DEBUG;TRACE -PROGRAMS =fileformats gl game ra cnc aftermath ra_ng server seqed +PROGRAMS =fileformats gl game ra cnc aftermath ra_ng seqed mapcvtr COMMON_LIBS = System.dll System.Core.dll System.Drawing.dll System.Xml.dll @@ -56,8 +56,13 @@ seqed_KIND = winexe seqed_DEPS = $(fileformats_TARGET) seqed_LIBS = $(COMMON_LIBS) System.Windows.Forms.dll $(seqed_DEPS) -# -platform:x86 +mapcvtr_SRCS = $(shell find MapConverter/ -iname '*.cs') +mapcvtr_TARGET = MapConverter.exe +mapcvtr_KIND = winexe +mapcvtr_DEPS = $(fileformats_TARGET) +mapcvtr_LIBS = $(COMMON_LIBS) $(mapcvtr_DEPS) +# -platform:x86 define BUILD_ASSEMBLY @@ -76,7 +81,7 @@ $(foreach prog,$(PROGRAMS),$(eval $(call BUILD_ASSEMBLY,$(prog)))) .SUFFIXES: -.PHONY: clean all default mods server seqed +.PHONY: clean all default mods seqed mapcvtr clean: @@ -84,7 +89,8 @@ clean: mods: $(ra_TARGET) $(cnc_TARGET) $(aftermath_TARGET) $(ra_ng_TARGET) seqed: $(seqed_TARGET) -all: $(fileformats_TARGET) $(gl_TARGET) $(game_TARGET) $(ra_TARGET) $(cnc_TARGET) $(aftermath_TARGET) $(ra_ng_TARGET) $(seqed_TARGET) +mapcvtr: $(mapcvtr_TARGET) +all: $(fileformats_TARGET) $(gl_TARGET) $(game_TARGET) $(ra_TARGET) $(cnc_TARGET) $(aftermath_TARGET) $(ra_ng_TARGET) $(seqed_TARGET) $(mapcvtr_TARGET) dist-osx: packaging/osx/package.sh diff --git a/MapConverter/AssemblyInfo.cs b/MapConverter/AssemblyInfo.cs new file mode 100644 index 0000000000..0b1e45b1a1 --- /dev/null +++ b/MapConverter/AssemblyInfo.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle("MapConverter")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion("1.0.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] diff --git a/MapConverter/IniMap.cs b/MapConverter/IniMap.cs new file mode 100644 index 0000000000..b668e148c8 --- /dev/null +++ b/MapConverter/IniMap.cs @@ -0,0 +1,275 @@ +#region Copyright & License Information +/* + * Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford. + * This file is part of OpenRA. + * + * OpenRA is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenRA is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenRA. If not, see . + */ +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using OpenRA; +using OpenRA.FileFormats; + +namespace MapConverter +{ + public class IniMap + { + public readonly string Title; + public readonly string Theater; + public readonly int INIFormat; + + public readonly int MapSize; + public readonly int XOffset; + public readonly int YOffset; + public int2 Offset { get { return new int2( XOffset, YOffset ); } } + + public readonly int Width; + public readonly int Height; + public int2 Size { get { return new int2(Width, Height); } } + + public readonly TileReference[ , ] MapTiles; + public readonly List Actors = new List(); + + public readonly IEnumerable SpawnPoints; + + static string Truncate( string s, int maxLength ) + { + return s.Length <= maxLength ? s : s.Substring(0,maxLength ); + } + + public void Save(string filename) + { + + } + + public IniMap(string filename) + { + IniFile file = new IniFile(FileSystem.Open(filename)); + + IniSection basic = file.GetSection("Basic"); + Title = basic.GetValue("Name", "(null)"); + INIFormat = int.Parse(basic.GetValue("NewINIFormat", "0")); + + IniSection map = file.GetSection("Map"); + Theater = Truncate(map.GetValue("Theater", "TEMPERAT"), 8); + + XOffset = int.Parse(map.GetValue("X", "0")); + YOffset = int.Parse(map.GetValue("Y", "0")); + + Width = int.Parse(map.GetValue("Width", "0")); + Height = int.Parse(map.GetValue("Height", "0")); + MapSize = (INIFormat == 3) ? 128 : 64; + + MapTiles = new TileReference[ MapSize, MapSize ]; + for (int j = 0; j < MapSize; j++) + for (int i = 0; i < MapSize; i++) + MapTiles[i, j] = new TileReference(); + + + if (INIFormat == 3) // RA map + { + UnpackRATileData(ReadPackedSection(file.GetSection("MapPack"))); + UnpackRAOverlayData(ReadPackedSection(file.GetSection("OverlayPack"))); + ReadRATrees(file); + } + else // CNC + { + UnpackCncTileData(FileSystem.Open(filename.Substring(0,filename.Length-4)+".bin")); + ReadCncOverlay(file); + ReadCncTrees(file); + } + + LoadActors(file, "STRUCTURES"); + LoadActors(file, "UNITS"); + LoadActors(file, "INFANTRY"); + + SpawnPoints = file.GetSection("Waypoints") + .Where(kv => int.Parse(kv.Value) > 0) + .Select(kv => Pair.New(int.Parse(kv.Key), new int2(int.Parse(kv.Value) % MapSize, int.Parse(kv.Value) / MapSize))) + .Where(a => a.First < 8) + .Select(a => a.Second) + .ToArray(); + } + + static MemoryStream ReadPackedSection(IniSection mapPackSection) + { + StringBuilder sb = new StringBuilder(); + for (int i = 1; ; i++) + { + string line = mapPackSection.GetValue(i.ToString(), null); + if (line == null) + break; + + sb.Append(line.Trim()); + } + + byte[] data = Convert.FromBase64String(sb.ToString()); + List chunks = new List(); + BinaryReader reader = new BinaryReader(new MemoryStream(data)); + + try + { + while (true) + { + uint length = reader.ReadUInt32() & 0xdfffffff; + byte[] dest = new byte[8192]; + byte[] src = reader.ReadBytes((int)length); + + /*int actualLength =*/ Format80.DecodeInto(src, dest); + + chunks.Add(dest); + } + } + catch (EndOfStreamException) { } + + MemoryStream ms = new MemoryStream(); + foreach (byte[] chunk in chunks) + ms.Write(chunk, 0, chunk.Length); + + ms.Position = 0; + + return ms; + } + + static byte ReadByte( Stream s ) + { + int ret = s.ReadByte(); + if( ret == -1 ) + throw new NotImplementedException(); + return (byte)ret; + } + + static ushort ReadWord(Stream s) + { + ushort ret = ReadByte(s); + ret |= (ushort)(ReadByte(s) << 8); + + return ret; + } + + void UnpackRATileData( MemoryStream ms ) + { + for( int i = 0 ; i < MapSize ; i++ ) + for( int j = 0 ; j < MapSize ; j++ ) + MapTiles[j, i].tile = ReadWord(ms); + + for( int i = 0 ; i < MapSize ; i++ ) + for( int j = 0 ; j < MapSize ; j++ ) + { + MapTiles[j, i].image = (byte)ms.ReadByte(); + if( MapTiles[ j, i ].tile == 0xff || MapTiles[ j, i ].tile == 0xffff ) + MapTiles[ j, i ].image = (byte)( i % 4 + ( j % 4 ) * 4 ); + } + } + + static string[] raOverlayNames = + { + "sbag", "cycl", "brik", "fenc", "wood", + "gold01", "gold02", "gold03", "gold04", + "gem01", "gem02", "gem03", "gem04", + "v12", "v13", "v14", "v15", "v16", "v17", "v18", + "fpls", "wcrate", "scrate", "barb", "sbag", + }; + + void UnpackRAOverlayData( MemoryStream ms ) + { + for( int i = 0 ; i < MapSize ; i++ ) + for( int j = 0 ; j < MapSize ; j++ ) + { + byte o = ReadByte( ms ); + MapTiles[ j, i ].overlay = (o == 255) ? null : raOverlayNames[o]; + } + } + + void ReadRATrees( IniFile file ) + { + IniSection terrain = file.GetSection( "TERRAIN", true ); + if( terrain == null ) + return; + + foreach( KeyValuePair kv in terrain ) + { + var loc = int.Parse( kv.Key ); + Actors.Add( new ActorReference(kv.Value, new int2(loc % MapSize, loc / MapSize), null ) ); + } + } + + void UnpackCncTileData( Stream ms ) + { + for( int i = 0 ; i < MapSize ; i++ ) + for( int j = 0 ; j < MapSize ; j++ ) + { + MapTiles[j, i].tile = (byte)ms.ReadByte(); + MapTiles[j, i].image = (byte)ms.ReadByte(); + + if( MapTiles[ j, i ].tile == 0xff ) + MapTiles[ j, i ].image = (byte)( i % 4 + ( j % 4 ) * 4 ); + } + } + + void ReadCncOverlay( IniFile file ) + { + IniSection overlay = file.GetSection( "OVERLAY", true ); + if( overlay == null ) + return; + + foreach( KeyValuePair kv in overlay ) + { + var loc = int.Parse( kv.Key ); + int2 cell = new int2(loc % MapSize, loc / MapSize); + MapTiles[ cell.X, cell.Y ].overlay = kv.Value.ToLower(); + } + } + + + void ReadCncTrees( IniFile file ) + { + IniSection terrain = file.GetSection( "TERRAIN", true ); + if( terrain == null ) + return; + + foreach( KeyValuePair kv in terrain ) + { + var loc = int.Parse( kv.Key ); + Actors.Add( new ActorReference( kv.Value.Split(',')[0], new int2(loc % MapSize, loc / MapSize),null)); + } + } + + void LoadActors(IniFile file, string section) + { + foreach (var s in file.GetSection(section, true)) + { + //num=owner,type,health,location,facing,... + var parts = s.Value.Split( ',' ); + var loc = int.Parse(parts[3]); + Actors.Add( new ActorReference( parts[1].ToLowerInvariant(), new int2(loc % MapSize, loc / MapSize), parts[0])); + } + } + + public bool IsInMap(int2 xy) + { + return IsInMap(xy.X,xy.Y); + } + + public bool IsInMap(int x, int y) + { + return (x >= XOffset && y >= YOffset && x < XOffset + Width && y < YOffset + Height); + } + } +} diff --git a/MapConverter/Main.cs b/MapConverter/Main.cs new file mode 100644 index 0000000000..e77fd7f72c --- /dev/null +++ b/MapConverter/Main.cs @@ -0,0 +1,27 @@ +using System; +using OpenRA.FileFormats; + +namespace MapConverter +{ + class MainClass + { + public static void Main (string[] args) + { + if (args.Length != 3) + { + Console.WriteLine("usage: MapConverter mod[,mod]* input-map.ini output-map.yaml"); + return; + } + + var mods = args[0].Split(','); + var manifest = new Manifest(mods); + + foreach (var folder in manifest.Folders) FileSystem.Mount(folder); + foreach (var pkg in manifest.Packages) FileSystem.Mount(pkg); + + + var map = new IniMap(args[1]); + map.Save(args[2]); + } + } +} diff --git a/MapConverter/MapConverter.csproj b/MapConverter/MapConverter.csproj new file mode 100644 index 0000000000..c64c0f5fcb --- /dev/null +++ b/MapConverter/MapConverter.csproj @@ -0,0 +1 @@ + Debug AnyCPU 9.0.21022 2.0 {BA2B4C61-D5EE-4C3E-9BA1-EB32C531FA36} Exe MapConverter MapConverter v3.5 true full false bin\Debug DEBUG prompt 4 none false bin\Release prompt 4 \ No newline at end of file diff --git a/OpenRA.Gl/OpenRA.Gl.csproj b/OpenRA.Gl/OpenRA.Gl.csproj index 2f38128d3b..79f71e80e6 100644 --- a/OpenRA.Gl/OpenRA.Gl.csproj +++ b/OpenRA.Gl/OpenRA.Gl.csproj @@ -1,4 +1,4 @@ - + Debug @@ -66,7 +66,7 @@ OpenRA.Game - +