The DAT file reader had several bugs inherited from the old C++ reference code, which targeted an older format version. Verified and fixed against real client_portal.dat and client_cell_1.dat files: - Fix header offset: BTree root is at 0x160, not 0x148 (file size field) - Fix BTree entry size: 24 bytes (flags+id+offset+size+timestamp), not 12 - Fix sector-chain node reading: BTree nodes span multiple sectors via linked-list headers; must assemble node data across sector boundaries - Fix DatStreamImpl.Read() BSTR handling: use Buffer.BlockCopy to match C++ SysAllocStringByteLen instead of Marshal.PtrToStringAnsi - Fix DatStreamImpl.ReadBinary() pointer lifetime: inline fixed block to keep destination buffer pinned during Marshal.Copy - Document LoadFilters() dependency on parameterized COM properties in IDecalCore.Configuration that need IDispatch to call correctly Add smoke test project (13/13 tests pass against real DAT files). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
67 lines
1.8 KiB
C#
67 lines
1.8 KiB
C#
using System;
|
|
using System.Runtime.InteropServices;
|
|
using Decal.Interop.Dat;
|
|
|
|
namespace Decal.DecalDat
|
|
{
|
|
[ComVisible(true)]
|
|
[Guid("9F7F6CD9-D164-418D-8CB5-3B9ACD70BEAF")]
|
|
[ClassInterface(ClassInterfaceType.None)]
|
|
[ProgId("DecalDat.DatStream")]
|
|
public class DatStreamImpl : IDatStream
|
|
{
|
|
private DatFileEntry _file;
|
|
|
|
internal void Load(DatFileEntry file)
|
|
{
|
|
_file = file;
|
|
}
|
|
|
|
public int Size => _file?.Size ?? 0;
|
|
|
|
public int Tell => _file?.Tell ?? 0;
|
|
|
|
public void Skip(int Bytes)
|
|
{
|
|
_file?.Skip(Bytes);
|
|
}
|
|
|
|
public void Restart()
|
|
{
|
|
_file?.Reset();
|
|
}
|
|
|
|
public void ReadBinary(int Bytes, ref byte Buffer)
|
|
{
|
|
if (_file == null) return;
|
|
|
|
var buf = new byte[Bytes];
|
|
_file.Read(buf, 0, Bytes);
|
|
|
|
// Pin the destination buffer for the duration of the copy
|
|
unsafe
|
|
{
|
|
fixed (byte* p = &Buffer)
|
|
{
|
|
Marshal.Copy(buf, 0, new IntPtr(p), Bytes);
|
|
}
|
|
}
|
|
}
|
|
|
|
public string Read(int Bytes)
|
|
{
|
|
if (_file == null) return string.Empty;
|
|
|
|
var buf = new byte[Bytes];
|
|
int read = _file.Read(buf, 0, Bytes);
|
|
|
|
// Match C++ SysAllocStringByteLen: pack raw bytes into WCHAR pairs.
|
|
// When the CLR marshals this string to BSTR, the raw bytes are preserved
|
|
// byte-for-byte (each char = 2 raw bytes, little-endian).
|
|
int charCount = (read + 1) / 2;
|
|
var chars = new char[charCount];
|
|
Buffer.BlockCopy(buf, 0, chars, 0, read);
|
|
return new string(chars, 0, charCount);
|
|
}
|
|
}
|
|
}
|