Correct UniqueId/FontFace implementations.
This commit is contained in:
@ -1,7 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
|
||||
@ -17,43 +17,46 @@ namespace RobloxFiles.BinaryFormat
|
||||
File = file;
|
||||
}
|
||||
|
||||
// Reads 'count * sizeof(T)' interleaved bytes and converts
|
||||
// them into an array of T[count] where each value in the
|
||||
// array has been decoded by the provided 'decode' function.
|
||||
public T[] ReadInterleaved<T>(int count, Func<byte[], int, T> decode) where T : struct
|
||||
// Reads 'count * sizeof(T)' interleaved bytes
|
||||
public T[] ReadInterleaved<T>(int count, Func<byte[], int, T> transform) where T : struct
|
||||
{
|
||||
int bufferSize = Marshal.SizeOf<T>();
|
||||
byte[] interleaved = ReadBytes(count * bufferSize);
|
||||
int sizeof_T = Marshal.SizeOf<T>();
|
||||
int blobSize = count * sizeof_T;
|
||||
|
||||
T[] values = new T[count];
|
||||
var blob = ReadBytes(blobSize);
|
||||
var work = new byte[sizeof_T];
|
||||
var values = new T[count];
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
for (int offset = 0; offset < count; offset++)
|
||||
{
|
||||
long buffer = 0;
|
||||
|
||||
for (int column = 0; column < bufferSize; column++)
|
||||
for (int i = 0; i < sizeof_T; i++)
|
||||
{
|
||||
long block = interleaved[(column * count) + i];
|
||||
int shift = (bufferSize - column - 1) * 8;
|
||||
buffer |= (block << shift);
|
||||
int index = (i * count) + offset;
|
||||
work[sizeof_T - i - 1] = blob[index];
|
||||
}
|
||||
|
||||
byte[] sequence = BitConverter.GetBytes(buffer);
|
||||
values[i] = decode(sequence, 0);
|
||||
values[offset] = transform(work, 0);
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
// Decodes an int from an interleaved buffer.
|
||||
private int DecodeInt(byte[] buffer, int startIndex)
|
||||
// Rotates the sign bit of an int32 buffer.
|
||||
public int RotateInt32(byte[] buffer, int startIndex)
|
||||
{
|
||||
int value = BitConverter.ToInt32(buffer, startIndex);
|
||||
return (value >> 1) ^ (-(value & 1));
|
||||
return (int)((uint)value >> 1) ^ (-(value & 1));
|
||||
}
|
||||
|
||||
// Decodes a float from an interleaved buffer.
|
||||
private float DecodeFloat(byte[] buffer, int startIndex)
|
||||
|
||||
// Rotates the sign bit of an int64 buffer.
|
||||
public long RotateInt64(byte[] buffer, int startIndex)
|
||||
{
|
||||
long value = BitConverter.ToInt64(buffer, startIndex);
|
||||
return (long)((ulong)value >> 1) ^ (-(value & 1));
|
||||
}
|
||||
|
||||
// Rotates the sign bit of a float buffer.
|
||||
public float RotateFloat(byte[] buffer, int startIndex)
|
||||
{
|
||||
uint u = BitConverter.ToUInt32(buffer, startIndex);
|
||||
uint i = (u >> 1) | (u << 31);
|
||||
@ -62,28 +65,10 @@ namespace RobloxFiles.BinaryFormat
|
||||
return BitConverter.ToSingle(b, 0);
|
||||
}
|
||||
|
||||
// Reads an interleaved buffer of integers.
|
||||
public int[] ReadInts(int count)
|
||||
{
|
||||
return ReadInterleaved(count, DecodeInt);
|
||||
}
|
||||
|
||||
// Reads an interleaved buffer of floats.
|
||||
public float[] ReadFloats(int count)
|
||||
{
|
||||
return ReadInterleaved(count, DecodeFloat);
|
||||
}
|
||||
|
||||
// Reads an interleaved buffer of unsigned integers.
|
||||
public uint[] ReadUInts(int count)
|
||||
{
|
||||
return ReadInterleaved(count, BitConverter.ToUInt32);
|
||||
}
|
||||
|
||||
// Reads and accumulates an interleaved buffer of integers.
|
||||
// Reads and accumulates an interleaved int32 buffer.
|
||||
public List<int> ReadInstanceIds(int count)
|
||||
{
|
||||
int[] values = ReadInts(count);
|
||||
int[] values = ReadInterleaved(count, RotateInt32);
|
||||
|
||||
for (int i = 1; i < count; ++i)
|
||||
values[i] += values[i - 1];
|
||||
|
@ -103,15 +103,21 @@ namespace RobloxFiles.BinaryFormat
|
||||
|
||||
Marshal.FreeHGlobal(converter);
|
||||
}
|
||||
|
||||
// Encodes an int for an interleaved buffer.
|
||||
private static int EncodeInt(int value)
|
||||
|
||||
// Rotates the sign bit of the provided int.
|
||||
public int RotateInt(int value)
|
||||
{
|
||||
return (value << 1) ^ (value >> 31);
|
||||
}
|
||||
|
||||
// Encodes a float for an interleaved buffer.
|
||||
private static float EncodeFloat(float value)
|
||||
|
||||
// Rotates the sign bit of the provided long.
|
||||
public long RotateLong(long value)
|
||||
{
|
||||
return (value << 1) ^ (value >> 63);
|
||||
}
|
||||
|
||||
// Rotates the sign bit of the provided float.
|
||||
public float RotateFloat(float value)
|
||||
{
|
||||
byte[] buffer = BitConverter.GetBytes(value);
|
||||
uint bits = BitConverter.ToUInt32(buffer, 0);
|
||||
@ -125,13 +131,19 @@ namespace RobloxFiles.BinaryFormat
|
||||
// Writes an interleaved list of integers.
|
||||
public void WriteInts(List<int> values)
|
||||
{
|
||||
WriteInterleaved(values, EncodeInt);
|
||||
WriteInterleaved(values, RotateInt);
|
||||
}
|
||||
|
||||
// Writes an interleaved list of longs
|
||||
public void WriteLongs(List<long> values)
|
||||
{
|
||||
WriteInterleaved(values, RotateLong);
|
||||
}
|
||||
|
||||
// Writes an interleaved list of floats
|
||||
public void WriteFloats(List<float> values)
|
||||
{
|
||||
WriteInterleaved(values, EncodeFloat);
|
||||
WriteInterleaved(values, RotateFloat);
|
||||
}
|
||||
|
||||
// Accumulatively writes an interleaved array of integers.
|
||||
|
@ -78,8 +78,8 @@ namespace RobloxFiles.BinaryFormat.Chunks
|
||||
}
|
||||
|
||||
// Setup some short-hand functions for actions used during the read procedure.
|
||||
var readInts = new Func<int[]>(() => reader.ReadInts(instCount));
|
||||
var readFloats = new Func<float[]>(() => reader.ReadFloats(instCount));
|
||||
var readInts = new Func<int[]>(() => reader.ReadInterleaved(instCount, reader.RotateInt32));
|
||||
var readFloats = new Func<float[]>(() => reader.ReadInterleaved(instCount, reader.RotateFloat));
|
||||
|
||||
var readProperties = new Action<Func<int, object>>(read =>
|
||||
{
|
||||
@ -434,7 +434,7 @@ namespace RobloxFiles.BinaryFormat.Chunks
|
||||
}
|
||||
case PropertyType.Enum:
|
||||
{
|
||||
uint[] enums = reader.ReadUInts(instCount);
|
||||
uint[] enums = reader.ReadInterleaved(instCount, BitConverter.ToUInt32);
|
||||
|
||||
readProperties(i =>
|
||||
{
|
||||
@ -619,22 +619,17 @@ namespace RobloxFiles.BinaryFormat.Chunks
|
||||
}
|
||||
case PropertyType.Int64:
|
||||
{
|
||||
long[] longs = reader.ReadInterleaved(instCount, (buffer, start) =>
|
||||
{
|
||||
long result = BitConverter.ToInt64(buffer, start);
|
||||
return (long)((ulong)result >> 1) ^ (-(result & 1));
|
||||
});
|
||||
|
||||
readProperties(i => longs[i]);
|
||||
var values = reader.ReadInterleaved(instCount, reader.RotateInt64);
|
||||
readProperties(i => values[i]);
|
||||
break;
|
||||
}
|
||||
case PropertyType.SharedString:
|
||||
{
|
||||
uint[] SharedKeys = reader.ReadUInts(instCount);
|
||||
var keys = reader.ReadInterleaved(instCount, BitConverter.ToUInt32);
|
||||
|
||||
readProperties(i =>
|
||||
{
|
||||
uint key = SharedKeys[i];
|
||||
uint key = keys[i];
|
||||
return File.SharedStrings[key];
|
||||
});
|
||||
|
||||
@ -654,14 +649,16 @@ namespace RobloxFiles.BinaryFormat.Chunks
|
||||
}
|
||||
case PropertyType.UniqueId:
|
||||
{
|
||||
readProperties(i =>
|
||||
var uniqueIds = reader.ReadInterleaved(instCount, (buffer, offset) =>
|
||||
{
|
||||
var index = reader.ReadUInt32();
|
||||
var time = reader.ReadUInt32();
|
||||
var random = reader.ReadUInt64();
|
||||
return new UniqueId(index, time, random);
|
||||
var random = reader.RotateInt64(buffer, 0);
|
||||
var time = BitConverter.ToUInt32(buffer, 8);
|
||||
var index = BitConverter.ToUInt32(buffer, 12);
|
||||
|
||||
return new UniqueId(random, time, index);
|
||||
});
|
||||
|
||||
readProperties(i => uniqueIds[i]);
|
||||
break;
|
||||
}
|
||||
case PropertyType.FontFace:
|
||||
@ -670,13 +667,11 @@ namespace RobloxFiles.BinaryFormat.Chunks
|
||||
{
|
||||
string family = reader.ReadString();
|
||||
|
||||
if (family.EndsWith(".otf") || family.EndsWith(".ttf"))
|
||||
return new FontFace(family);
|
||||
|
||||
var weight = (FontWeight)reader.ReadUInt16();
|
||||
var style = (FontStyle)reader.ReadByte();
|
||||
var cachedFaceId = reader.ReadString();
|
||||
|
||||
return new FontFace(family, weight, style);
|
||||
return new FontFace(family, weight, style, cachedFaceId);
|
||||
});
|
||||
|
||||
break;
|
||||
@ -1233,12 +1228,7 @@ namespace RobloxFiles.BinaryFormat.Chunks
|
||||
longs.Add(value);
|
||||
});
|
||||
|
||||
writer.WriteInterleaved(longs, value =>
|
||||
{
|
||||
// Move the sign bit to the front.
|
||||
return (value << 1) ^ (value >> 63);
|
||||
});
|
||||
|
||||
writer.WriteLongs(longs);
|
||||
break;
|
||||
}
|
||||
case PropertyType.SharedString:
|
||||
@ -1293,14 +1283,16 @@ namespace RobloxFiles.BinaryFormat.Chunks
|
||||
}
|
||||
case PropertyType.UniqueId:
|
||||
{
|
||||
var uniqueIds = new List<UniqueId>();
|
||||
|
||||
props.ForEach(prop =>
|
||||
{
|
||||
var uniqueId = prop.CastValue<UniqueId>();
|
||||
writer.Write(uniqueId.Index);
|
||||
writer.Write(uniqueId.Time);
|
||||
writer.Write(uniqueId.Random);
|
||||
var rotated = writer.RotateLong(uniqueId.Random);
|
||||
uniqueIds.Add(new UniqueId(rotated, uniqueId.Time, uniqueId.Index));
|
||||
});
|
||||
|
||||
writer.WriteInterleaved(uniqueIds);
|
||||
break;
|
||||
}
|
||||
case PropertyType.FontFace:
|
||||
@ -1310,16 +1302,16 @@ namespace RobloxFiles.BinaryFormat.Chunks
|
||||
var font = prop.CastValue<FontFace>();
|
||||
|
||||
string family = font.Family;
|
||||
writer.WriteString(font.Family);
|
||||
|
||||
if (family.EndsWith(".otf") || family.EndsWith(".ttf"))
|
||||
return;
|
||||
writer.WriteString(family);
|
||||
|
||||
var weight = (ushort)font.Weight;
|
||||
writer.Write(weight);
|
||||
|
||||
var style = (byte)font.Style;
|
||||
writer.Write(style);
|
||||
|
||||
var cachedFaceId = font.CachedFaceId;
|
||||
writer.WriteString(cachedFaceId);
|
||||
});
|
||||
|
||||
break;
|
||||
|
Reference in New Issue
Block a user