Skip to content

Commit

Permalink
feat(Dg): add Dungeon (.dg) support
Browse files Browse the repository at this point in the history
  • Loading branch information
matigramirez committed Nov 25, 2023
1 parent 3dc0eb5 commit a4b3d9c
Show file tree
Hide file tree
Showing 18 changed files with 336 additions and 0 deletions.
5 changes: 5 additions & 0 deletions src/Parsec/Serialization/SBinaryReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ public void ResetBuffer(byte[] buffer)
_binaryReader = new BinaryReader(memoryStream);
}

/// <summary>
/// Current position in the stream
/// </summary>
public long Position => _binaryReader.BaseStream.Position;

/// <summary>
/// Length of the stream
/// </summary>
Expand Down
56 changes: 56 additions & 0 deletions src/Parsec/Shaiya/Dg/Dg.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using Parsec.Extensions;
using Parsec.Serialization;
using Parsec.Shaiya.Common;
using Parsec.Shaiya.Core;

namespace Parsec.Shaiya.Dg;

public class Dg : FileBase
{
public BoundingBox BoundingBox { get; set; }

public List<String256> TextureNames { get; set; } = new();

public int UnknownInt32 { get; set; }

public List<DgNode> Nodes { get; set; } = new();

public override string Extension => "dg";

protected override void Read(SBinaryReader binaryReader)
{
BoundingBox = binaryReader.Read<BoundingBox>();
TextureNames = binaryReader.ReadList<String256>().ToList();
UnknownInt32 = binaryReader.ReadInt32();

while (true)
{
// When value is 1, node data follows, otherwise node reading must be skipped
var value = binaryReader.ReadInt32();

if (value > 0)
{
var node = binaryReader.Read<DgNode>();
Nodes.Add(node);
}

if (binaryReader.Position == binaryReader.StreamLength)
{
break;
}
}
}

protected override void Write(SBinaryWriter binaryWriter)
{
binaryWriter.Write(BoundingBox);
binaryWriter.Write(TextureNames.ToSerializable());
binaryWriter.Write(UnknownInt32);

foreach (var node in Nodes)
{
binaryWriter.Write(1);
binaryWriter.Write(node);
}
}
}
31 changes: 31 additions & 0 deletions src/Parsec/Shaiya/Dg/DgCollisionMesh.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Parsec.Extensions;
using Parsec.Serialization;
using Parsec.Shaiya.Common;
using Parsec.Shaiya.Core;

namespace Parsec.Shaiya.Dg;

public class DgCollisionMesh : ISerializable
{
/// <summary>
/// Vertices of the 3d object.
/// </summary>
public List<DgCollisionMeshVertex> Vertices { get; set; } = new();

/// <summary>
/// Triangular faces (polygons) of the 3d object.
/// </summary>
public List<MeshFace> Faces { get; set; } = new();

public void Read(SBinaryReader binaryReader)
{
Vertices = binaryReader.ReadList<DgCollisionMeshVertex>().ToList();
Faces = binaryReader.ReadList<MeshFace>().ToList();
}

public void Write(SBinaryWriter binaryWriter)
{
binaryWriter.Write(Vertices.ToSerializable());
binaryWriter.Write(Faces.ToSerializable());
}
}
26 changes: 26 additions & 0 deletions src/Parsec/Shaiya/Dg/DgCollisionMeshVertex.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using Parsec.Serialization;
using Parsec.Shaiya.Common;
using Parsec.Shaiya.Core;

namespace Parsec.Shaiya.Dg;

/// <summary>
/// Represents a vertex used in DG collision objects
/// </summary>
public sealed class DgCollisionMeshVertex : ISerializable
{
/// <summary>
/// Coordinates of the vertex in the 3D space.
/// </summary>
public Vector3 Coordinates { get; set; }

public void Read(SBinaryReader binaryReader)
{
Coordinates = binaryReader.Read<Vector3>();
}

public void Write(SBinaryWriter binaryWriter)
{
binaryWriter.Write(Coordinates);
}
}
29 changes: 29 additions & 0 deletions src/Parsec/Shaiya/Dg/DgMesh.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Parsec.Extensions;
using Parsec.Serialization;
using Parsec.Shaiya.Common;
using Parsec.Shaiya.Core;

namespace Parsec.Shaiya.Dg;

public class DgMesh : ISerializable
{
public AlphaBlendingMode AlphaBlendingMode { get; set; }

public List<DgSegmentVertex> Vertices { get; set; } = new();

public List<MeshFace> Faces { get; set; } = new();

public void Read(SBinaryReader binaryReader)
{
AlphaBlendingMode = (AlphaBlendingMode)binaryReader.ReadInt32();
Vertices = binaryReader.ReadList<DgSegmentVertex>().ToList();
Faces = binaryReader.ReadList<MeshFace>().ToList();
}

public void Write(SBinaryWriter binaryWriter)
{
binaryWriter.Write((int)AlphaBlendingMode);
binaryWriter.Write(Vertices.ToSerializable());
binaryWriter.Write(Faces.ToSerializable());
}
}
7 changes: 7 additions & 0 deletions src/Parsec/Shaiya/Dg/DgMeshCollisionType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace Parsec.Shaiya.Dg;

public enum DgMeshCollisionType
{
Transparent = 0,
Collision = 1
}
55 changes: 55 additions & 0 deletions src/Parsec/Shaiya/Dg/DgNode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using Parsec.Extensions;
using Parsec.Serialization;
using Parsec.Shaiya.Common;
using Parsec.Shaiya.Core;

namespace Parsec.Shaiya.Dg;

public class DgNode : ISerializable
{
// TODO: Check
public Vector3 Center { get; set; }

// TODO: Check
public BoundingBox ViewBox { get; set; }

// TODO: Check
public BoundingBox CollisionBox { get; set; }

public List<DgObject> Objects { get; set; } = new();

public DgMeshCollisionType CollisionType { get; set; }

public DgCollisionMesh CollisionMesh { get; set; } = new();

public void Read(SBinaryReader binaryReader)
{
Center = binaryReader.Read<Vector3>();
ViewBox = binaryReader.Read<BoundingBox>();
CollisionBox = binaryReader.Read<BoundingBox>();

Objects = binaryReader.ReadList<DgObject>().ToList();

CollisionType = (DgMeshCollisionType)binaryReader.ReadInt32();

if (CollisionType == DgMeshCollisionType.Collision)
{
// Read extra node info
CollisionMesh = binaryReader.Read<DgCollisionMesh>();
}
}

public void Write(SBinaryWriter binaryWriter)
{
binaryWriter.Write(Center);
binaryWriter.Write(ViewBox);
binaryWriter.Write(CollisionBox);
binaryWriter.Write(Objects.ToSerializable());
binaryWriter.Write((int)CollisionType);

if (CollisionType == DgMeshCollisionType.Collision)
{
binaryWriter.Write(CollisionMesh);
}
}
}
24 changes: 24 additions & 0 deletions src/Parsec/Shaiya/Dg/DgObject.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using Parsec.Extensions;
using Parsec.Serialization;
using Parsec.Shaiya.Core;

namespace Parsec.Shaiya.Dg;

public class DgObject : ISerializable
{
public int TextureIndex { get; set; }

public List<DgMesh> Meshes { get; set; } = new();

public void Read(SBinaryReader binaryReader)
{
TextureIndex = binaryReader.ReadInt32();
Meshes = binaryReader.ReadList<DgMesh>().ToList();
}

public void Write(SBinaryWriter binaryWriter)
{
binaryWriter.Write(TextureIndex);
binaryWriter.Write(Meshes.ToSerializable());
}
}
52 changes: 52 additions & 0 deletions src/Parsec/Shaiya/Dg/DgSegmentVertex.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using Parsec.Serialization;
using Parsec.Shaiya.Common;
using Parsec.Shaiya.Core;

namespace Parsec.Shaiya.Dg;

public class DgSegmentVertex : ISerializable
{
/// <summary>
/// Vertex coordinates in the 3D space
/// </summary>
public Vector3 Coordinates { get; set; }

/// <summary>
/// Vertex normal used for lighting
/// </summary>
public Vector3 Normal { get; set; }

/// <summary>
/// SMODs don't have bones, that's why this value is always -1.
/// </summary>
public int BoneId { get; set; } = -1;

/// <summary>
/// Texture mapping
/// </summary>
public Vector2 UV { get; set; }

public float Unknown1 { get; set; }

public float Unknown2 { get; set; }

public void Read(SBinaryReader binaryReader)
{
Coordinates = binaryReader.Read<Vector3>();
Normal = binaryReader.Read<Vector3>();
BoneId = binaryReader.ReadInt32();
UV = binaryReader.Read<Vector2>();
Unknown1 = binaryReader.ReadSingle();
Unknown2 = binaryReader.ReadSingle();
}

public void Write(SBinaryWriter binaryWriter)
{
binaryWriter.Write(Coordinates);
binaryWriter.Write(Normal);
binaryWriter.Write(BoneId);
binaryWriter.Write(UV);
binaryWriter.Write(Unknown1);
binaryWriter.Write(Unknown2);
}
}
21 changes: 21 additions & 0 deletions tests/Parsec.Tests/Parsec.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -794,5 +794,26 @@
<None Update="Shaiya\Cloak\EmblemList.dat">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Shaiya\Dg\R1_Dun1_2F.dg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Shaiya\Dg\r1_dun3.dg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Shaiya\Dg\r1_dun3_01.dg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Shaiya\Dg\r1_dun3_02.dg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Shaiya\Dg\r1_dun3_boss.dg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Shaiya\Dg\R1_Trade.dg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="Shaiya\Dg\R2_Dun1.dg">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>
30 changes: 30 additions & 0 deletions tests/Parsec.Tests/Shaiya/Dg/DgTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
namespace Parsec.Tests.Shaiya.Dg;

public class DgTests
{
[Theory]
[InlineData("R1_Dun1_2F.dg")]
[InlineData("r1_dun3.dg")]
[InlineData("r1_dun3_01.dg")]
[InlineData("r1_dun3_02.dg")]
[InlineData("r1_dun3_boss.dg")]
[InlineData("R1_Trade.dg")]
[InlineData("R2_Dun1.dg")]
public void DgMultipleReadWriteTest(string fileName)
{
var filePath = $"Shaiya/Dg/{fileName}";
var outputPath = $"Shaiya/Dg/output_{fileName}";
var jsonPath = $"Shaiya/Dg/{fileName}.json";

var dg = ParsecReader.FromFile<Parsec.Shaiya.Dg.Dg>(filePath);
dg.Write(outputPath);
dg.WriteJson(jsonPath);

var outputDg = ParsecReader.FromFile<Parsec.Shaiya.Dg.Dg>(outputPath);
var dgFromJson = ParsecReader.FromJsonFile<Parsec.Shaiya.Dg.Dg>(jsonPath);

// Check bytes
Assert.Equal(dg.GetBytes(), outputDg.GetBytes());
Assert.Equal(dg.GetBytes(), dgFromJson.GetBytes());
}
}
Binary file added tests/Parsec.Tests/Shaiya/Dg/R1_Dun1_2F.dg
Binary file not shown.
Binary file added tests/Parsec.Tests/Shaiya/Dg/R1_Trade.dg
Binary file not shown.
Binary file added tests/Parsec.Tests/Shaiya/Dg/R2_Dun1.dg
Binary file not shown.
Binary file added tests/Parsec.Tests/Shaiya/Dg/r1_dun3.dg
Binary file not shown.
Binary file added tests/Parsec.Tests/Shaiya/Dg/r1_dun3_01.dg
Binary file not shown.
Binary file added tests/Parsec.Tests/Shaiya/Dg/r1_dun3_02.dg
Binary file not shown.
Binary file added tests/Parsec.Tests/Shaiya/Dg/r1_dun3_boss.dg
Binary file not shown.

0 comments on commit a4b3d9c

Please sign in to comment.