-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial support for prefab libraries added to JACK beta version
- Loading branch information
1 parent
f1d34f0
commit fe515c8
Showing
5 changed files
with
268 additions
and
2 deletions.
There are no files selected for viewing
96 changes: 96 additions & 0 deletions
96
Sledge.Formats.Map.Tests/Formats/TestJackhammerPrefabLibrary.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
using System.IO; | ||
using System.Linq; | ||
using Microsoft.VisualStudio.TestTools.UnitTesting; | ||
using Sledge.Formats.Map.Formats; | ||
using Sledge.Formats.Map.Objects; | ||
|
||
namespace Sledge.Formats.Map.Tests.Formats; | ||
|
||
[TestClass] | ||
public class TestJackhammerPrefabLibrary | ||
{ | ||
/* | ||
// Commented out because I don't want to include the default JACK files due to licensing. | ||
// Put them into the jack_prefab as embedded resources if you want to run this test. | ||
[DataTestMethod] | ||
[DataRow("Computers.jol")] | ||
[DataRow("Crates.jol")] | ||
[DataRow("Old Stuff.jol")] | ||
[DataRow("Random Objects.jol")] | ||
[DataRow("Usable Objects.jol")] | ||
public void TestDefaultPrefabs(string name) | ||
{ | ||
using var file = typeof(TestJackhammerPrefabLibrary).Assembly.GetManifestResourceStream($"Sledge.Formats.Map.Tests.Resources.jack_prefab.{name}"); | ||
var library = new JackhammerPrefabLibrary(file); | ||
Console.WriteLine(library.Description); | ||
foreach (var p in library.Prefabs) | ||
{ | ||
Console.WriteLine(p.Name + " : " + p.Description); | ||
} | ||
} | ||
*/ | ||
|
||
// todo: need to create the boxes.jol file for the test, however as of writing it requires the paid version | ||
[TestMethod] | ||
public void TestSimplePrefab() | ||
{ | ||
using var file = typeof(TestJackhammerPrefabLibrary).Assembly.GetManifestResourceStream("Sledge.Formats.Map.Tests.Resources.jack_prefab.boxes.jol"); | ||
var library = new JackhammerPrefabLibrary(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); | ||
} | ||
|
||
// todo: writing not supported yet. need to know how the unknown fields are handled by the app to find out more | ||
[TestMethod] | ||
public void TestWritingPrefab() | ||
{ | ||
using var file = typeof(TestJackhammerPrefabLibrary).Assembly.GetManifestResourceStream("Sledge.Formats.Map.Tests.Resources.jack_prefab.boxes.jol"); | ||
var library = new JackhammerPrefabLibrary(file); | ||
|
||
var newLib = new JackhammerPrefabLibrary | ||
{ | ||
Description = "write test" | ||
}; | ||
|
||
var testMap = new MapFile(); | ||
testMap.Worldspawn.Children.Add(new Entity | ||
{ | ||
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 JackhammerPrefabLibrary(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); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
todo: replace this file with boxes.jol once you are able to create prefabs in free JACK |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
using Sledge.Formats.Map.Objects; | ||
using System; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Text; | ||
|
||
namespace Sledge.Formats.Map.Formats | ||
{ | ||
public class JackhammerPrefabLibrary | ||
{ | ||
private const int Version = 1; | ||
|
||
public string Description { get; set; } | ||
public List<Prefab> Prefabs { get; set; } | ||
|
||
public static JackhammerPrefabLibrary FromFile(string file) | ||
{ | ||
using (var stream = File.OpenRead(file)) | ||
{ | ||
return new JackhammerPrefabLibrary(stream); | ||
} | ||
} | ||
|
||
public JackhammerPrefabLibrary() | ||
{ | ||
Description = ""; | ||
Prefabs = new List<Prefab>(); | ||
} | ||
|
||
public JackhammerPrefabLibrary(Stream stream) | ||
{ | ||
Prefabs = new List<Prefab>(); | ||
using (var br = new BinaryReader(stream)) | ||
{ | ||
var header = br.ReadFixedLengthString(Encoding.ASCII, 4); | ||
Util.Assert(header == "JHOL", $"Incorrect prefab library header. Expected 'JHOL', got '{header}'."); | ||
|
||
var version = br.ReadInt32(); | ||
Util.Assert(version == Version, $"Unsupported prefab library version number. Expected {Version}, got {version}."); | ||
|
||
// read header data | ||
var descriptionStringNum = br.ReadInt32(); | ||
_ = br.ReadInt32(); // unknown | ||
_ = br.ReadInt32(); // unknown | ||
var singleEntryLength = br.ReadInt32(); | ||
var entryDataOffset = br.ReadInt32(); | ||
var entryDataLength = br.ReadInt32(); | ||
var mapDataOffset = br.ReadInt32(); | ||
var mapDataLength = br.ReadInt32(); | ||
var imageDataOffset = br.ReadInt32(); | ||
var imageDataLength = br.ReadInt32(); | ||
var stringDataOffset = br.ReadInt32(); | ||
var stringDataLength = br.ReadInt32(); | ||
|
||
// read strings | ||
br.BaseStream.Seek(stringDataOffset, SeekOrigin.Begin); | ||
|
||
var stringCount = br.ReadInt32(); | ||
|
||
// for whatever reason the offset to each string is stored next, we don't need those | ||
br.BaseStream.Seek(stringCount * 4, SeekOrigin.Current); | ||
|
||
var strings = new List<string> | ||
{ | ||
"" // string numbers seem to be indexed from 1 so add a blank string for index 0 | ||
}; | ||
for (var i = 0; i < stringCount; i++) | ||
{ | ||
var len = br.ReadInt32(); | ||
strings.Add(br.ReadFixedLengthString(Encoding.ASCII, len)); | ||
} | ||
|
||
Description = strings[descriptionStringNum]; | ||
|
||
// read entries | ||
br.BaseStream.Seek(entryDataOffset, SeekOrigin.Begin); | ||
|
||
var entries = new List<Entry>(); | ||
|
||
var numEntries = entryDataLength / singleEntryLength; | ||
for (var i = 0; i < numEntries; i++) | ||
{ | ||
var name = strings[br.ReadInt32()]; | ||
var desc = strings[br.ReadInt32()]; | ||
_ = br.ReadInt32(); // unknown | ||
_ = br.ReadInt32(); // unknown | ||
var entryMapOffset = br.ReadInt32(); | ||
var entryMapLength = br.ReadInt32(); | ||
var entryMapType = br.ReadFixedLengthString(Encoding.ASCII, 4); | ||
var entryImageOffset = br.ReadInt32(); | ||
var entryImageLength = br.ReadInt32(); | ||
var entryImageType = br.ReadFixedLengthString(Encoding.ASCII, 4); | ||
|
||
if (entryMapType != "JHMF") | ||
{ | ||
throw new NotSupportedException("Unexpected non-JMF format in Jackhammer prefab library."); | ||
} | ||
|
||
// i suspect this is an origin or bounding box, but not sure | ||
(_, _, _) = (br.ReadDouble(), br.ReadDouble(), br.ReadDouble()); | ||
|
||
var numWorldspawns = br.ReadInt32(); // ?? no idea, always seems to be 1 | ||
var numSolids = br.ReadInt32(); | ||
var numPointEnts = br.ReadInt32(); | ||
var numSolidEnts = br.ReadInt32(); | ||
_ = br.ReadInt32(); // unknown | ||
_ = br.ReadInt32(); // unknown | ||
_ = br.ReadInt32(); // unknown | ||
var numUniqueTextures = br.ReadInt32(); | ||
|
||
entries.Add(new Entry | ||
{ | ||
Name = name, | ||
Description = desc, | ||
MapOffset = entryMapOffset, | ||
MapLength = entryMapLength, | ||
MapType = entryMapType, | ||
ImageOffset = entryImageOffset, | ||
ImageLength = entryImageLength, | ||
ImageType = entryImageType | ||
}); | ||
} | ||
|
||
// read maps and preview images | ||
var jmf = new JackhammerJmfFormat(); | ||
|
||
foreach (var entry in entries) | ||
{ | ||
br.BaseStream.Seek(imageDataOffset + entry.ImageOffset, SeekOrigin.Begin); | ||
var img = br.ReadBytes(entry.ImageLength); | ||
using (var substream = new SubStream(br.BaseStream, mapDataOffset + entry.MapOffset, entry.MapLength)) | ||
{ | ||
Prefabs.Add(new Prefab(entry.Name, entry.Description, jmf.Read(substream)) | ||
{ | ||
PreviewImage = img | ||
}); | ||
} | ||
} | ||
} | ||
} | ||
|
||
public void WriteToFile(string file) | ||
{ | ||
using (var stream = File.OpenWrite(file)) | ||
{ | ||
Write(stream); | ||
} | ||
} | ||
|
||
public void Write(Stream stream) | ||
{ | ||
throw new NotImplementedException(); | ||
} | ||
|
||
// record class to hold an entry temporarily | ||
private class Entry | ||
{ | ||
public string Name { get; set; } | ||
public string Description { get; set; } | ||
public int MapOffset { get; set; } | ||
public int MapLength { get; set; } | ||
public string MapType { get; set; } | ||
public int ImageOffset { get; set; } | ||
public int ImageLength { get; set; } | ||
public string ImageType { get; set; } | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters