UltimateZoneBuilder/Source/Core/IO/DoomMapSetIO.cs
MaxED 0369c969d1 According to dotnetperls.com, "new Dictionary<string, [anything]>(StringComparer.Ordinal)" works 17% faster than "new Dictionary<string, [anything]>()", so let's stick that everywhere and see what happens :)
Draw Curve Mode: added settings panel.
Sectors mode: added "Make Door" button to the toolbar.
Swapped Side panel and Info panel z-order. 
Interface: split toolbar into 3 separate toolbars. All toolbar buttons are now viewable at 1024x768.
Interface: grouped stuff in "Modes" menu a bit better.
Interface: added "Draw [stuff]" buttons to modes toolbar.
Interface: reorganized main menu. Hope it makes more sense now.
API: added General.Interface.AddModesButton() and General.Interface.AddModesMenu(), which can be used to add buttons to specific group in "Modes" toolbar and menu items to specific group in "Modes" menu, so actions, which behave like an editing mode, but are not part of one can be added there.
2014-02-26 14:11:06 +00:00

630 lines
21 KiB
C#

#region ================== Copyright (c) 2007 Pascal vd Heiden
/*
* Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com
* This program is released under GNU General Public License
*
* This program 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.
*
*/
#endregion
#region ================== Namespaces
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using CodeImp.DoomBuilder.Map;
using CodeImp.DoomBuilder.Geometry;
using CodeImp.DoomBuilder.Types;
#endregion
namespace CodeImp.DoomBuilder.IO
{
internal class DoomMapSetIO : MapSetIO
{
#region ================== Constants
#endregion
#region ================== Constructor / Disposer
// Constructor
public DoomMapSetIO(WAD wad, MapManager manager) : base(wad, manager)
{
}
#endregion
#region ================== Properties
public override int MaxSidedefs { get { return ushort.MaxValue; } }
public override int MaxVertices { get { return ushort.MaxValue; } }
public override int MaxLinedefs { get { return ushort.MaxValue; } }
public override int MaxSectors { get { return ushort.MaxValue; } }
public override int MaxThings { get { return int.MaxValue; } }
public override int MinTextureOffset { get { return short.MinValue; } }
public override int MaxTextureOffset { get { return short.MaxValue; } }
public override int VertexDecimals { get { return 0; } }
public override string DecimalsFormat { get { return "0"; } }
public override bool HasLinedefTag { get { return true; } }
public override bool HasThingTag { get { return false; } }
public override bool HasThingAction { get { return false; } }
public override bool HasCustomFields { get { return false; } }
public override bool HasThingHeight { get { return false; } }
public override bool HasActionArgs { get { return false; } }
public override bool HasMixedActivations { get { return false; } }
public override bool HasPresetActivations { get { return false; } }
public override bool HasBuiltInActivations { get { return true; } }
public override bool HasNumericLinedefFlags { get { return true; } }
public override bool HasNumericThingFlags { get { return true; } }
public override bool HasNumericLinedefActivations { get { return true; } }
public override int MaxTag { get { return ushort.MaxValue; } }
public override int MinTag { get { return ushort.MinValue; } }
public override int MaxAction { get { return ushort.MaxValue; } }
public override int MinAction { get { return ushort.MinValue; } }
public override int MaxArgument { get { return 0; } }
public override int MinArgument { get { return 0; } }
public override int MaxEffect { get { return ushort.MaxValue; } }
public override int MinEffect { get { return ushort.MinValue; } }
public override int MaxBrightness { get { return short.MaxValue; } }
public override int MinBrightness { get { return short.MinValue; } }
public override int MaxThingType { get { return ushort.MaxValue; } }
public override int MinThingType { get { return ushort.MinValue; } }
public override float MaxCoordinate { get { return short.MaxValue; } }
public override float MinCoordinate { get { return short.MinValue; } }
public override int MaxThingAngle { get { return short.MaxValue; } }
public override int MinThingAngle { get { return short.MinValue; } }
public override Dictionary<string, Dictionary<string, UniversalType>> UIFields { get { return uifields; } } //mxd
#endregion
#region ================== Reading
// This reads a map from the file and returns a MapSet
public override MapSet Read(MapSet map, string mapname)
{
Dictionary<int, Vertex> vertexlink;
Dictionary<int, Sector> sectorlink;
// Find the index where first map lump begins
int firstindex = wad.FindLumpIndex(mapname) + 1;
// Read vertices
vertexlink = ReadVertices(map, firstindex);
// Read sectors
sectorlink = ReadSectors(map, firstindex);
// Read linedefs and sidedefs
ReadLinedefs(map, firstindex, vertexlink, sectorlink);
// Read things
ReadThings(map, firstindex);
// Remove unused vertices
map.RemoveUnusedVertices();
// Return result;
return map;
}
// This reads the THINGS from WAD file
private void ReadThings(MapSet map, int firstindex)
{
MemoryStream mem;
BinaryReader reader;
int num, i, x, y, type, flags, angle;
Dictionary<string, bool> stringflags;
Thing t;
// Get the lump from wad file
Lump lump = wad.FindLump("THINGS", firstindex);
if(lump == null) throw new Exception("Could not find required lump THINGS!");
// Prepare to read the items
mem = new MemoryStream(lump.Stream.ReadAllBytes());
num = (int)lump.Stream.Length / 10;
reader = new BinaryReader(mem);
// Read items from the lump
map.SetCapacity(0, 0, 0, 0, map.Things.Count + num);
for(i = 0; i < num; i++)
{
// Read properties from stream
x = reader.ReadInt16();
y = reader.ReadInt16();
angle = reader.ReadInt16();
type = reader.ReadUInt16();
flags = reader.ReadUInt16();
// Make string flags
stringflags = new Dictionary<string, bool>(StringComparer.Ordinal);
foreach(KeyValuePair<string, string> f in manager.Config.ThingFlags)
{
int fnum;
if(int.TryParse(f.Key, out fnum)) stringflags[f.Key] = ((flags & fnum) == fnum);
}
// Create new item
t = map.CreateThing();
t.Update(type, x, y, 0, angle, stringflags, 0, 0, new int[Thing.NUM_ARGS]);
}
// Done
mem.Dispose();
}
// This reads the VERTICES from WAD file
// Returns a lookup table with indices
private Dictionary<int, Vertex> ReadVertices(MapSet map, int firstindex)
{
MemoryStream mem;
Dictionary<int, Vertex> link;
BinaryReader reader;
int num, i, x, y;
Vertex v;
// Get the lump from wad file
Lump lump = wad.FindLump("VERTEXES", firstindex);
if(lump == null) throw new Exception("Could not find required lump VERTEXES!");
// Prepare to read the items
mem = new MemoryStream(lump.Stream.ReadAllBytes());
num = (int)lump.Stream.Length / 4;
reader = new BinaryReader(mem);
// Create lookup table
link = new Dictionary<int, Vertex>(num);
// Read items from the lump
map.SetCapacity(map.Vertices.Count + num, 0, 0, 0, 0);
for(i = 0; i < num; i++)
{
// Read properties from stream
x = reader.ReadInt16();
y = reader.ReadInt16();
// Create new item
v = map.CreateVertex(new Vector2D(x, y));
// Add it to the lookup table
link.Add(i, v);
}
// Done
mem.Dispose();
// Return lookup table
return link;
}
// This reads the SECTORS from WAD file
// Returns a lookup table with indices
private Dictionary<int, Sector> ReadSectors(MapSet map, int firstindex)
{
MemoryStream mem;
Dictionary<int, Sector> link;
BinaryReader reader;
int num, i, hfloor, hceil, bright, special, tag;
string tfloor, tceil;
Sector s;
// Get the lump from wad file
Lump lump = wad.FindLump("SECTORS", firstindex);
if(lump == null) throw new Exception("Could not find required lump SECTORS!");
// Prepare to read the items
mem = new MemoryStream(lump.Stream.ReadAllBytes());
num = (int)lump.Stream.Length / 26;
reader = new BinaryReader(mem);
// Create lookup table
link = new Dictionary<int, Sector>(num);
// Read items from the lump
map.SetCapacity(0, 0, 0, map.Sectors.Count + num, 0);
for(i = 0; i < num; i++)
{
// Read properties from stream
hfloor = reader.ReadInt16();
hceil = reader.ReadInt16();
tfloor = Lump.MakeNormalName(reader.ReadBytes(8), WAD.ENCODING);
tceil = Lump.MakeNormalName(reader.ReadBytes(8), WAD.ENCODING);
bright = reader.ReadInt16();
special = reader.ReadUInt16();
tag = reader.ReadUInt16();
// Create new item
s = map.CreateSector();
s.Update(hfloor, hceil, tfloor, tceil, special, tag, bright);
// Add it to the lookup table
link.Add(i, s);
}
// Done
mem.Dispose();
// Return lookup table
return link;
}
// This reads the LINEDEFS and SIDEDEFS from WAD file
private void ReadLinedefs(MapSet map, int firstindex,
Dictionary<int, Vertex> vertexlink, Dictionary<int, Sector> sectorlink)
{
MemoryStream linedefsmem, sidedefsmem;
BinaryReader readline, readside;
Lump linedefslump, sidedefslump;
int num, numsides, i, offsetx, offsety, v1, v2;
int s1, s2, flags, action, tag, sc;
Dictionary<string, bool> stringflags;
string thigh, tmid, tlow;
Linedef l;
Sidedef s;
// Get the linedefs lump from wad file
linedefslump = wad.FindLump("LINEDEFS", firstindex);
if(linedefslump == null) throw new Exception("Could not find required lump LINEDEFS!");
// Get the sidedefs lump from wad file
sidedefslump = wad.FindLump("SIDEDEFS", firstindex);
if(sidedefslump == null) throw new Exception("Could not find required lump SIDEDEFS!");
// Prepare to read the items
linedefsmem = new MemoryStream(linedefslump.Stream.ReadAllBytes());
sidedefsmem = new MemoryStream(sidedefslump.Stream.ReadAllBytes());
num = (int)linedefslump.Stream.Length / 14;
numsides = (int)sidedefslump.Stream.Length / 30;
readline = new BinaryReader(linedefsmem);
readside = new BinaryReader(sidedefsmem);
// Read items from the lump
map.SetCapacity(0, map.Linedefs.Count + num, map.Sidedefs.Count + numsides, 0, 0);
for(i = 0; i < num; i++)
{
// Read properties from stream
v1 = readline.ReadUInt16();
v2 = readline.ReadUInt16();
flags = readline.ReadUInt16();
action = readline.ReadUInt16();
tag = readline.ReadUInt16();
s1 = readline.ReadUInt16();
s2 = readline.ReadUInt16();
// Make string flags
stringflags = new Dictionary<string, bool>(StringComparer.Ordinal);
foreach(string f in manager.Config.SortedLinedefFlags)
{
int fnum;
if(int.TryParse(f, out fnum)) stringflags[f] = ((flags & fnum) == fnum);
}
// Create new linedef
if(vertexlink.ContainsKey(v1) && vertexlink.ContainsKey(v2))
{
// Check if not zero-length
if(Vector2D.ManhattanDistance(vertexlink[v1].Position, vertexlink[v2].Position) > 0.0001f)
{
l = map.CreateLinedef(vertexlink[v1], vertexlink[v2]);
l.Update(stringflags, 0, tag, action, new int[Linedef.NUM_ARGS]);
l.UpdateCache();
// Line has a front side?
if(s1 != ushort.MaxValue)
{
// Read front sidedef
if((s1 * 30L) <= (sidedefsmem.Length - 30L))
{
sidedefsmem.Seek(s1 * 30, SeekOrigin.Begin);
offsetx = readside.ReadInt16();
offsety = readside.ReadInt16();
thigh = Lump.MakeNormalName(readside.ReadBytes(8), WAD.ENCODING);
tlow = Lump.MakeNormalName(readside.ReadBytes(8), WAD.ENCODING);
tmid = Lump.MakeNormalName(readside.ReadBytes(8), WAD.ENCODING);
sc = readside.ReadUInt16();
// Create front sidedef
if(sectorlink.ContainsKey(sc))
{
s = map.CreateSidedef(l, true, sectorlink[sc]);
s.Update(offsetx, offsety, thigh, tmid, tlow);
}
else
{
General.ErrorLogger.Add(ErrorType.Warning, "Sidedef " + s1 + " references invalid sector " + sc + ". Sidedef has been removed.");
}
}
else
{
General.ErrorLogger.Add(ErrorType.Warning, "Linedef references invalid sidedef " + s1 + ". Sidedef has been removed.");
}
}
// Line has a back side?
if(s2 != ushort.MaxValue)
{
// Read back sidedef
if((s2 * 30L) <= (sidedefsmem.Length - 30L))
{
sidedefsmem.Seek(s2 * 30, SeekOrigin.Begin);
offsetx = readside.ReadInt16();
offsety = readside.ReadInt16();
thigh = Lump.MakeNormalName(readside.ReadBytes(8), WAD.ENCODING);
tlow = Lump.MakeNormalName(readside.ReadBytes(8), WAD.ENCODING);
tmid = Lump.MakeNormalName(readside.ReadBytes(8), WAD.ENCODING);
sc = readside.ReadUInt16();
// Create back sidedef
if(sectorlink.ContainsKey(sc))
{
s = map.CreateSidedef(l, false, sectorlink[sc]);
s.Update(offsetx, offsety, thigh, tmid, tlow);
}
else
{
General.ErrorLogger.Add(ErrorType.Warning, "Sidedef " + s2 + " references invalid sector " + sc + ". Sidedef has been removed.");
}
}
else
{
General.ErrorLogger.Add(ErrorType.Warning, "Linedef " + i + " references invalid sidedef " + s2 + ". Sidedef has been removed.");
}
}
}
else
{
General.ErrorLogger.Add(ErrorType.Warning, "Linedef " + i + " is zero-length. Linedef has been removed.");
}
}
else
{
General.ErrorLogger.Add(ErrorType.Warning, "Linedef " + i + " references one or more invalid vertices. Linedef has been removed.");
}
}
// Done
linedefsmem.Dispose();
sidedefsmem.Dispose();
}
#endregion
#region ================== Writing
// This writes a MapSet to the file
public override void Write(MapSet map, string mapname, int position)
{
Dictionary<Vertex, int> vertexids = new Dictionary<Vertex,int>();
Dictionary<Sidedef, int> sidedefids = new Dictionary<Sidedef,int>();
Dictionary<Sector, int> sectorids = new Dictionary<Sector,int>();
// First index everything
foreach(Vertex v in map.Vertices) vertexids.Add(v, vertexids.Count);
foreach(Sidedef sd in map.Sidedefs) sidedefids.Add(sd, sidedefids.Count);
foreach(Sector s in map.Sectors) sectorids.Add(s, sectorids.Count);
// Write lumps to wad (note the backwards order because they
// are all inserted at position+1 when not found)
WriteSectors(map, position, manager.Config.MapLumpNames);
WriteVertices(map, position, manager.Config.MapLumpNames);
WriteSidedefs(map, position, manager.Config.MapLumpNames, sectorids);
WriteLinedefs(map, position, manager.Config.MapLumpNames, sidedefids, vertexids);
WriteThings(map, position, manager.Config.MapLumpNames);
}
// This writes the THINGS to WAD file
private void WriteThings(MapSet map, int position, IDictionary maplumps)
{
MemoryStream mem;
BinaryWriter writer;
Lump lump;
int insertpos;
int flags;
// Create memory to write to
mem = new MemoryStream();
writer = new BinaryWriter(mem, WAD.ENCODING);
// Go for all things
foreach(Thing t in map.Things)
{
// Convert flags
flags = 0;
foreach(KeyValuePair<string, bool> f in t.Flags)
{
int fnum;
if(f.Value && int.TryParse(f.Key, out fnum)) flags |= fnum;
}
// Write properties to stream
writer.Write((Int16)t.Position.x);
writer.Write((Int16)t.Position.y);
writer.Write((Int16)t.AngleDoom);
writer.Write((UInt16)t.Type);
writer.Write((UInt16)flags);
}
// Find insert position and remove old lump
insertpos = MapManager.RemoveSpecificLump(wad, "THINGS", position, MapManager.TEMP_MAP_HEADER, maplumps);
if(insertpos == -1) insertpos = position + 1;
if(insertpos > wad.Lumps.Count) insertpos = wad.Lumps.Count;
// Create the lump from memory
lump = wad.Insert("THINGS", insertpos, (int)mem.Length);
lump.Stream.Seek(0, SeekOrigin.Begin);
mem.WriteTo(lump.Stream);
mem.Flush();
}
// This writes the VERTEXES to WAD file
private void WriteVertices(MapSet map, int position, IDictionary maplumps)
{
MemoryStream mem;
BinaryWriter writer;
Lump lump;
int insertpos;
// Create memory to write to
mem = new MemoryStream();
writer = new BinaryWriter(mem, WAD.ENCODING);
// Go for all vertices
foreach(Vertex v in map.Vertices)
{
// Write properties to stream
writer.Write((Int16)(int)Math.Round(v.Position.x));
writer.Write((Int16)(int)Math.Round(v.Position.y));
}
// Find insert position and remove old lump
insertpos = MapManager.RemoveSpecificLump(wad, "VERTEXES", position, MapManager.TEMP_MAP_HEADER, maplumps);
if(insertpos == -1) insertpos = position + 1;
if(insertpos > wad.Lumps.Count) insertpos = wad.Lumps.Count;
// Create the lump from memory
lump = wad.Insert("VERTEXES", insertpos, (int)mem.Length);
lump.Stream.Seek(0, SeekOrigin.Begin);
mem.WriteTo(lump.Stream);
mem.Flush();
}
// This writes the LINEDEFS to WAD file
private void WriteLinedefs(MapSet map, int position, IDictionary maplumps, IDictionary<Sidedef, int> sidedefids, IDictionary<Vertex, int> vertexids)
{
MemoryStream mem;
BinaryWriter writer;
Lump lump;
ushort sid;
int insertpos;
int flags;
// Create memory to write to
mem = new MemoryStream();
writer = new BinaryWriter(mem, WAD.ENCODING);
// Go for all lines
foreach(Linedef l in map.Linedefs)
{
// Convert flags
flags = 0;
foreach(KeyValuePair<string, bool> f in l.Flags)
{
int fnum;
if(f.Value && int.TryParse(f.Key, out fnum)) flags |= fnum;
}
// Write properties to stream
writer.Write((UInt16)vertexids[l.Start]);
writer.Write((UInt16)vertexids[l.End]);
writer.Write((UInt16)flags);
writer.Write((UInt16)l.Action);
writer.Write((UInt16)l.Tag);
// Front sidedef
if(l.Front == null) sid = ushort.MaxValue;
else sid = (UInt16)sidedefids[l.Front];
writer.Write(sid);
// Back sidedef
if(l.Back == null) sid = ushort.MaxValue;
else sid = (UInt16)sidedefids[l.Back];
writer.Write(sid);
}
// Find insert position and remove old lump
insertpos = MapManager.RemoveSpecificLump(wad, "LINEDEFS", position, MapManager.TEMP_MAP_HEADER, maplumps);
if(insertpos == -1) insertpos = position + 1;
if(insertpos > wad.Lumps.Count) insertpos = wad.Lumps.Count;
// Create the lump from memory
lump = wad.Insert("LINEDEFS", insertpos, (int)mem.Length);
lump.Stream.Seek(0, SeekOrigin.Begin);
mem.WriteTo(lump.Stream);
mem.Flush();
}
// This writes the SIDEDEFS to WAD file
private void WriteSidedefs(MapSet map, int position, IDictionary maplumps, IDictionary<Sector, int> sectorids)
{
MemoryStream mem;
BinaryWriter writer;
Lump lump;
int insertpos;
// Create memory to write to
mem = new MemoryStream();
writer = new BinaryWriter(mem, WAD.ENCODING);
// Go for all sidedefs
foreach(Sidedef sd in map.Sidedefs)
{
// Write properties to stream
writer.Write((Int16)sd.OffsetX);
writer.Write((Int16)sd.OffsetY);
writer.Write(Lump.MakeFixedName(sd.HighTexture, WAD.ENCODING));
writer.Write(Lump.MakeFixedName(sd.LowTexture, WAD.ENCODING));
writer.Write(Lump.MakeFixedName(sd.MiddleTexture, WAD.ENCODING));
writer.Write((UInt16)sectorids[sd.Sector]);
}
// Find insert position and remove old lump
insertpos = MapManager.RemoveSpecificLump(wad, "SIDEDEFS", position, MapManager.TEMP_MAP_HEADER, maplumps);
if(insertpos == -1) insertpos = position + 1;
if(insertpos > wad.Lumps.Count) insertpos = wad.Lumps.Count;
// Create the lump from memory
lump = wad.Insert("SIDEDEFS", insertpos, (int)mem.Length);
lump.Stream.Seek(0, SeekOrigin.Begin);
mem.WriteTo(lump.Stream);
mem.Flush();
}
// This writes the SECTORS to WAD file
private void WriteSectors(MapSet map, int position, IDictionary maplumps)
{
MemoryStream mem;
BinaryWriter writer;
Lump lump;
int insertpos;
// Create memory to write to
mem = new MemoryStream();
writer = new BinaryWriter(mem, WAD.ENCODING);
// Go for all sectors
foreach(Sector s in map.Sectors)
{
// Write properties to stream
writer.Write((Int16)s.FloorHeight);
writer.Write((Int16)s.CeilHeight);
writer.Write(Lump.MakeFixedName(s.FloorTexture, WAD.ENCODING));
writer.Write(Lump.MakeFixedName(s.CeilTexture, WAD.ENCODING));
writer.Write((Int16)s.Brightness);
writer.Write((UInt16)s.Effect);
writer.Write((UInt16)s.Tag);
}
// Find insert position and remove old lump
insertpos = MapManager.RemoveSpecificLump(wad, "SECTORS", position, MapManager.TEMP_MAP_HEADER, maplumps);
if(insertpos == -1) insertpos = position + 1;
if(insertpos > wad.Lumps.Count) insertpos = wad.Lumps.Count;
// Create the lump from memory
lump = wad.Insert("SECTORS", insertpos, (int)mem.Length);
lump.Stream.Seek(0, SeekOrigin.Begin);
mem.WriteTo(lump.Stream);
mem.Flush();
}
#endregion
}
}