Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 103 additions & 0 deletions Source/ACE.DatLoader.Tests/SurfaceTextureTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
using System.IO;
using System.Linq;
using ACE.DatLoader.FileTypes;
using ACE.Entity.Enum;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace ACE.DatLoader.Tests
{
/// <summary>
/// Tests the SurfaceTexture header and texture-reference layout without
/// depending on installed DAT archives or the DatManager singleton.
/// </summary>
[TestClass]
public class SurfaceTextureTests
{
[TestMethod]
public void Unpack_TwoTextureReferences_PreservesRetailLayout()
{
// The IDs mirror an observed HighRes/Portal pair. Header bytes are
// constructed in-memory so the test can run without client DAT files.
var data = BuildRecord(0x0500000C, 0, TextureType.Texture2D,
0x06003B9D, 0x06003B9E);

var result = UnpackCompletely<SurfaceTexture>(data);

Assert.AreEqual(0x0500000Cu, result.Id);
Assert.AreEqual(0u, result.DataCategory);
Assert.AreEqual(TextureType.Texture2D, result.TextureType);
CollectionAssert.AreEqual(new uint[] { 0x06003B9D, 0x06003B9E }, result.Textures.ToArray());
}

[TestMethod]
public void Unpack_NonzeroDataCategory_RetainsUnsignedBits()
{
// A synthetic boundary case: retail SurfaceTexture categories
// observed so far were all zero, but the field is a UInt32 on disk.
var data = BuildRecord(0x05000001, 0xF0000001u, TextureType.Cube,
0x06000001);

var result = UnpackCompletely<SurfaceTexture>(data);

Assert.AreEqual(0xF0000001u, result.DataCategory);
Assert.AreEqual(TextureType.Cube, result.TextureType);
CollectionAssert.AreEqual(new uint[] { 0x06000001 }, result.Textures.ToArray());
}

[TestMethod]
public void Unpack_ZeroTextureReferences_ConsumesCompleteRecord()
{
var data = BuildRecord(0x05000002, 0, TextureType.Texture2D);

var result = UnpackCompletely<SurfaceTexture>(data);

Assert.AreEqual(0u, result.DataCategory);
Assert.AreEqual(TextureType.Texture2D, result.TextureType);
Assert.AreEqual(0, result.Textures.Count);
}

[TestMethod]
public void Unpack_HeaderMatchesExistingRenderTextureLayout()
{
// RenderTexture is an independently implemented reader for the
// same binary field layout. Compare their decoded values.
var data = BuildRecord(0x05000003, 0x80000000u, TextureType.Movie2D,
0x06000010, 0x06000011);

var surface = UnpackCompletely<SurfaceTexture>(data);
var render = UnpackCompletely<RenderTexture>(data);

Assert.AreEqual(render.Id, surface.Id);
Assert.AreEqual(render.DataCategory, surface.DataCategory);
Assert.AreEqual(render.TextureType, surface.TextureType);
CollectionAssert.AreEqual(render.Textures.ToArray(), surface.Textures.ToArray());
}

private static T UnpackCompletely<T>(byte[] bytes) where T : FileType, new()
{
using var stream = new MemoryStream(bytes, writable: false);
using var reader = new BinaryReader(stream);
var value = new T();
value.Unpack(reader);
Assert.AreEqual(stream.Length, stream.Position,
"The parser must consume exactly the bytes in the record.");
return value;
}

private static byte[] BuildRecord(uint id, uint category,
TextureType textureType, params uint[] textureIds)
{
using var stream = new MemoryStream();
using (var writer = new BinaryWriter(stream, System.Text.Encoding.UTF8, leaveOpen: true))
{
writer.Write(id); // UInt32 resource ID
writer.Write(category); // UInt32 DataCategory
writer.Write((byte)textureType); // Byte TextureType
writer.Write(textureIds.Length); // Int32 List<uint> count
foreach (var textureId in textureIds)
writer.Write(textureId);
}
return stream.ToArray();
}
}
}
22 changes: 15 additions & 7 deletions Source/ACE.DatLoader/FileTypes/SurfaceTexture.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using ACE.Common;
using ACE.DatLoader.Entity;
using ACE.Entity.Enum;
using System;
using System.Collections.Generic;
using System.IO;
Expand All @@ -10,17 +11,24 @@ namespace ACE.DatLoader.FileTypes
[DatFileType(DatFileType.SurfaceTexture)]
public class SurfaceTexture : FileType
{
// public int Id { get; private set; }
public int Unknown { get; private set; }
public byte UnknownByte { get; private set; }
// public int TextureCount { get; private set; }
public List<uint> Textures { get; private set; } = new List<uint>(); // These values correspond to a Surface (0x06) entry
// The original client identifies ImgTex as SURFACETEXTURE (DB type 0x0B).
// DBObj::Serialize reads/writes this field for categorized resource types.
// All examined retail SurfaceTexture records have DataCategory == 0.
public uint DataCategory { get; private set; }

// ImgTex::Serialize writes a fixed byte value of 2.
// RenderTexture uses TextureType.Texture2D for this value.
// On read, the original ImgTex serializer skips the byte without interpreting it.
public TextureType TextureType { get; private set; }

// References to Texture resources (IDs in the 0x06 and 0x07 ranges).
public List<uint> Textures { get; private set; } = new List<uint>(); // References to Texture (0x06) entries.

public override void Unpack(BinaryReader reader)
{
Id = reader.ReadUInt32();
Unknown = reader.ReadInt32();
UnknownByte = reader.ReadByte();
DataCategory = reader.ReadUInt32();
TextureType = (TextureType)reader.ReadByte();
Textures.Unpack(reader);
}
}
Expand Down