Skip to content

Commit

Permalink
Merge pull request #30 from Duude92/WorldcraftPrefabLibrary
Browse files Browse the repository at this point in the history
Add support for saving prefab into WorldcraftPrefabLibrary
  • Loading branch information
LogicAndTrick authored Feb 3, 2024
2 parents 8c988c1 + 1940b89 commit 42cd941
Show file tree
Hide file tree
Showing 6 changed files with 161 additions and 33 deletions.
91 changes: 77 additions & 14 deletions Sledge.Formats.Map.Tests/Formats/TestWorldcraftPrefabLibrary.cs
Original file line number Diff line number Diff line change
@@ -1,22 +1,85 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Sledge.Formats.Map.Formats;
using Sledge.Formats.Map.Objects;

namespace Sledge.Formats.Map.Tests.Formats
namespace Sledge.Formats.Map.Tests.Formats;

[TestClass]
public class TestWorldcraftPrefabLibrary
{
[TestClass]
public class TestWorldcraftPrefabLibrary
[TestMethod]
public void TestSimplePrefab()
{
using var file = typeof(TestJackhammerFormat).Assembly.GetManifestResourceStream("Sledge.Formats.Map.Tests.Resources.prefab.boxes.ol");
var library = new WorldcraftPrefabLibrary(file);

Assert.AreEqual(2, library.Prefabs.Count);
Assert.AreEqual("some boxes", library.Description);

Assert.AreEqual("box1", library.Prefabs[0].Name);
Assert.AreEqual("box number one", library.Prefabs[0].Description);

Assert.AreEqual("box2", library.Prefabs[1].Name);
Assert.AreEqual("second box", library.Prefabs[1].Description);
}

[TestMethod]
public void TestMultiVersionPrefab()
{
using var file = typeof(TestJackhammerFormat).Assembly.GetManifestResourceStream("Sledge.Formats.Map.Tests.Resources.prefab.multi_version.ol");
var library = new WorldcraftPrefabLibrary(file);

Assert.AreEqual(3, library.Prefabs.Count);
Assert.AreEqual("1.5b", library.Prefabs[0].Name);
Assert.AreEqual("2.1", library.Prefabs[1].Name);
Assert.AreEqual("3.3", library.Prefabs[2].Name);
}

[TestMethod]
public void TestWritingPrefab()
{
[TestMethod]
public void TestPrefabLoading()
using var file = typeof(TestJackhammerFormat).Assembly.GetManifestResourceStream("Sledge.Formats.Map.Tests.Resources.prefab.boxes.ol");
var library = new WorldcraftPrefabLibrary(file);

var newLib = new WorldcraftPrefabLibrary
{
Description = "write test"
};

var testMap = new MapFile();
testMap.Worldspawn.Children.Add(new Entity
{
var file = @"D:\Downloads\worldcraft\Worldcraft 3.3\prefabs\computers.ol";
var lib = WorldcraftPrefabLibrary.FromFile(file);
Assert.AreEqual(14, lib.Prefabs.Count);
}
ClassName = "this_is_a_test"
});

newLib.Prefabs.Add(new Prefab("test1", "testing", library.Prefabs[0].Map));
newLib.Prefabs.Add(new Prefab("test2", "more testing", library.Prefabs[1].Map));
newLib.Prefabs.Add(new Prefab("test3", "even more testing", library.Prefabs[1].Map));
newLib.Prefabs.Add(new Prefab("test4", "final test", testMap));

var ms = new MemoryStream();
newLib.Write(ms);
ms.Position = 0;

var openLib = new WorldcraftPrefabLibrary(ms);

Assert.AreEqual(4, openLib.Prefabs.Count);
Assert.AreEqual("write test", openLib.Description);

Assert.AreEqual("test1", openLib.Prefabs[0].Name);
Assert.AreEqual("testing", openLib.Prefabs[0].Description);

Assert.AreEqual("test2", openLib.Prefabs[1].Name);
Assert.AreEqual("more testing", openLib.Prefabs[1].Description);

Assert.AreEqual("test3", openLib.Prefabs[2].Name);
Assert.AreEqual("even more testing", openLib.Prefabs[2].Description);

Assert.AreEqual("test4", openLib.Prefabs[3].Name);
Assert.AreEqual("final test", openLib.Prefabs[3].Description);
Assert.AreEqual(1, openLib.Prefabs[3].Map.Worldspawn.Children.Count);
Assert.AreEqual("this_is_a_test", openLib.Prefabs[3].Map.Worldspawn.Children.OfType<Entity>().First().ClassName);
}
}
}
Binary file not shown.
Binary file not shown.
6 changes: 6 additions & 0 deletions Sledge.Formats.Map.Tests/Sledge.Formats.Map.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,17 @@
<ItemGroup>
<None Remove="Resources\jmf\default-room-121.jmf" />
<None Remove="Resources\jmf\default-room-122.jmf" />
<None Remove="Resources\prefab\boxes.ol" />
<None Remove="Resources\prefab\multi_version.ol" />
</ItemGroup>

<ItemGroup>
<EmbeddedResource Include="Resources\jmf\default-room-121.jmf" />
<EmbeddedResource Include="Resources\jmf\default-room-122.jmf" />
<EmbeddedResource Include="Resources\prefab\boxes.ol" />
<EmbeddedResource Include="Resources\prefab\multi_version.ol">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</EmbeddedResource>
</ItemGroup>

<ItemGroup>
Expand Down
85 changes: 66 additions & 19 deletions Sledge.Formats.Map/Formats/WorldcraftPrefabLibrary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ namespace Sledge.Formats.Map.Formats
{
public class WorldcraftPrefabLibrary
{
private static readonly string PrefabLibraryHeader = "Worldcraft Prefab Library\r\n" + (char)0x1A;
private const float Version = 0.1f;

public string Description { get; set; }
public List<Prefab> Prefabs { get; set; }

public static WorldcraftPrefabLibrary FromFile(string file)
Expand All @@ -18,57 +22,100 @@ public static WorldcraftPrefabLibrary FromFile(string file)
}
}

public WorldcraftPrefabLibrary()
{
Description = "";
Prefabs = new List<Prefab>();
}

public WorldcraftPrefabLibrary(Stream stream)
{
Prefabs = new List<Prefab>();
using (var br = new BinaryReader(stream))
{
var header = br.ReadFixedLengthString(Encoding.ASCII, 28);
var prefabLibraryHeader = "Worldcraft Prefab Library\r\n" + (char)0x1A;
Util.Assert(header == prefabLibraryHeader, $"Incorrect prefab library header. Expected '{prefabLibraryHeader}', got '{header}'.");
var header = br.ReadFixedLengthString(Encoding.ASCII, PrefabLibraryHeader.Length);
Util.Assert(header == PrefabLibraryHeader, $"Incorrect prefab library header. Expected '{PrefabLibraryHeader}', got '{header}'.");

var version = br.ReadSingle();
Util.Assert(Math.Abs(version - 0.1f) < 0.01, $"Unsupported prefab library version number. Expected 0.1, got {version}.");
Util.Assert(Math.Abs(version - Version) < 0.01, $"Unsupported prefab library version number. Expected {Version}, got {version}.");

var rmf = new WorldcraftRmfFormat();

var offset = br.ReadUInt32();
var num = br.ReadUInt32();
Description = br.ReadFixedLengthString(Encoding.ASCII, 500);

br.BaseStream.Seek(offset, SeekOrigin.Begin);
for (var i = 0; i < num; i++)
{
var objOffset = br.ReadUInt32();
var objLength = br.ReadUInt32();
var name = br.ReadFixedLengthString(Encoding.ASCII, 31);
var desc = br.ReadFixedLengthString(Encoding.ASCII, 205);
var _ = br.ReadBytes(300);
var desc = br.ReadFixedLengthString(Encoding.ASCII, 500);
br.ReadBytes(5); // unknown/padding

using (var substream = new SubStream(br.BaseStream, objOffset, objLength))
{

Prefabs.Add(new Prefab
{
Name = name,
Description = desc,
Map = rmf.Read(substream)
});
Prefabs.Add(new Prefab(name, desc, rmf.Read(substream)));
}
}
}
}

public void Save(string file)
public void WriteToFile(string file)
{
using (var stream = File.OpenWrite(file))
{
Save(stream);
Write(stream);
}
}

public void Save(Stream stream)
public void Write(Stream stream)
{
throw new NotImplementedException("Writing prefab libraries is not currently supported.");
}
}
if (!stream.CanSeek) throw new ArgumentException("stream must be seekable.", nameof(stream));
using (var bw = new BinaryWriter(stream, Encoding.ASCII, true))
{
var rmf = new WorldcraftRmfFormat();

var offsets = new List<(uint offs, uint len)>();

bw.WriteFixedLengthString(Encoding.ASCII, PrefabLibraryHeader.Length, PrefabLibraryHeader);
bw.Write(Version);
var infoOffsetPosition = stream.Position;
bw.Write(0); // offset, write this at the end
bw.Write(Prefabs.Count);
bw.WriteFixedLengthString(Encoding.ASCII, 500, Description);
bw.Write(0); // unknown, worldcraft always has 4 extra bytes after the description

// write all the map data
foreach (var prefab in Prefabs)
{
var pos = (uint) stream.Position;
rmf.Write(stream, prefab.Map, null);
var len = (uint) stream.Position - pos;
offsets.Add((pos, len));
}

// now go back and write the index offset to the header
var startOffset = (uint) stream.Position;
stream.Seek(infoOffsetPosition, SeekOrigin.Begin);
bw.Write(startOffset);
stream.Seek(startOffset, SeekOrigin.Begin);

// now write the index
for (var i = 0; i < Prefabs.Count; i++)
{
var prefab = Prefabs[i];
var (offs, len) = offsets[i];

bw.Write(offs);
bw.Write(len);
bw.WriteFixedLengthString(Encoding.ASCII, 31, prefab.Name);
bw.WriteFixedLengthString(Encoding.ASCII, 500, prefab.Description);
bw.Write(new byte[5]); // unknown/padding
}

}
}
}
}
12 changes: 12 additions & 0 deletions Sledge.Formats.Map/Objects/Prefab.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,17 @@ public class Prefab
public string Name { get; set; }
public string Description { get; set; }
public MapFile Map { get; set; }

public Prefab()
{
//
}

public Prefab(string name, string description, MapFile map)
{
Name = name;
Description = description;
Map = map;
}
}
}

0 comments on commit 42cd941

Please sign in to comment.