Added support for SharedStrings and SSTR chunk type

This commit is contained in:
CloneTrooper1019
2019-05-17 01:14:04 -05:00
parent 32e80bdd9a
commit 45a84e34d0
15 changed files with 266 additions and 91 deletions

View File

@ -9,11 +9,11 @@ namespace RobloxFiles.BinaryFormat
{
public BinaryRobloxReader(Stream stream) : base(stream) { }
private byte[] lastStringBuffer = new byte[0] { };
// Reads 'count * sizeof(T)' interleaved bytes and converts them
// into an array of T[count] where each value in the array has
// been transformed by the provided 'transform' function.
public T[] ReadInterleaved<T>(int count, Func<byte[], int, T> transform) where T : struct
// 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
{
int bufferSize = Marshal.SizeOf<T>();
byte[] interleaved = ReadBytes(count * bufferSize);
@ -32,21 +32,21 @@ namespace RobloxFiles.BinaryFormat
}
byte[] sequence = BitConverter.GetBytes(buffer);
values[i] = transform(sequence, 0);
values[i] = decode(sequence, 0);
}
return values;
}
// Transforms an int from an interleaved buffer.
private int TransformInt(byte[] buffer, int startIndex)
// Decodes an int from an interleaved buffer.
private int DecodeInt(byte[] buffer, int startIndex)
{
int value = BitConverter.ToInt32(buffer, startIndex);
return (value >> 1) ^ (-(value & 1));
}
// Transforms a float from an interleaved buffer.
private float TransformFloat(byte[] buffer, int startIndex)
// Decodes a float from an interleaved buffer.
private float DecodeFloat(byte[] buffer, int startIndex)
{
uint u = BitConverter.ToUInt32(buffer, startIndex);
uint i = (u >> 1) | (u << 31);
@ -58,13 +58,19 @@ namespace RobloxFiles.BinaryFormat
// Reads an interleaved buffer of integers.
public int[] ReadInts(int count)
{
return ReadInterleaved(count, TransformInt);
return ReadInterleaved(count, DecodeInt);
}
// Reads an interleaved buffer of floats.
public float[] ReadFloats(int count)
{
return ReadInterleaved(count, TransformFloat);
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.

View File

@ -26,8 +26,10 @@ namespace RobloxFiles.BinaryFormat
public override string ToString() => GetType().Name;
public Instance[] Instances;
public META Metadata;
public INST[] Types;
public Dictionary<string, string> Metadata;
public Dictionary<uint, string> SharedStrings;
public void ReadFile(byte[] contents)
{
@ -75,12 +77,18 @@ namespace RobloxFiles.BinaryFormat
hierarchy.Assemble(this);
break;
case "META":
Metadata = new META(chunk);
META meta = new META(chunk);
Metadata = meta.Data;
break;
case "SSTR":
SSTR shared = new SSTR(chunk);
SharedStrings = shared.Strings;
break;
case "END\0":
reading = false;
break;
default:
Console.WriteLine("Unhandled chunk type: {0}!", chunk.ChunkType);
Chunks.Remove(chunk);
break;
}

View File

@ -5,20 +5,19 @@ namespace RobloxFiles.BinaryFormat.Chunks
public class META
{
public int NumEntries;
public Dictionary<string, string> Entries;
public Dictionary<string, string> Data = new Dictionary<string, string>();
public META(BinaryRobloxChunk chunk)
{
using (BinaryRobloxReader reader = chunk.GetReader("META"))
{
NumEntries = reader.ReadInt32();
Entries = new Dictionary<string, string>(NumEntries);
for (int i = 0; i < NumEntries; i++)
{
string key = reader.ReadString();
string value = reader.ReadString();
Entries.Add(key, value);
Data.Add(key, value);
}
}
}

View File

@ -1,9 +1,10 @@
using System;
using System.IO;
using System.Linq;
using RobloxFiles.Enums;
using RobloxFiles.DataTypes;
using RobloxFiles.DataTypes.Utility;
using RobloxFiles.Utility;
namespace RobloxFiles.BinaryFormat.Chunks
{
@ -44,21 +45,19 @@ namespace RobloxFiles.BinaryFormat.Chunks
for (int i = 0; i < instCount; i++)
{
int id = ids[i];
Instance instance = file.Instances[id];
Property prop = new Property();
prop.Name = Name;
prop.Type = Type;
prop.Instance = instance;
Instance inst = file.Instances[id];
Property prop = new Property(inst, this);
props[i] = prop;
instance.AddProperty(ref prop);
inst.AddProperty(ref prop);
}
// Setup some short-hand functions for actions frequently used during the read procedure.
// 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 loadProperties = new Action<Func<int, object>>(read =>
{
for (int i = 0; i < instCount; i++)
@ -290,7 +289,7 @@ namespace RobloxFiles.BinaryFormat.Chunks
// TODO: I want to map these values to actual Roblox enums, but I'll have to add an
// interpreter for the JSON API Dump to do it properly.
uint[] enums = Reader.ReadInterleaved(instCount, BitConverter.ToUInt32);
uint[] enums = Reader.ReadUInts(instCount);
loadProperties(i => enums[i]);
break;
@ -431,6 +430,19 @@ namespace RobloxFiles.BinaryFormat.Chunks
loadProperties(i => int64s[i]);
break;
case PropertyType.SharedString:
uint[] sharedKeys = Reader.ReadUInts(instCount);
loadProperties(i =>
{
uint key = sharedKeys[i];
return file.SharedStrings[key];
});
break;
default:
Console.WriteLine("Unhandled property type: {0}!", Type);
break;
}
Reader.Dispose();

View File

@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
namespace RobloxFiles.BinaryFormat.Chunks
{
public class SSTR
{
public int Version;
public int NumHashes;
public Dictionary<string, uint> Lookup = new Dictionary<string, uint>();
public Dictionary<uint, string> Strings = new Dictionary<uint, string>();
public SSTR(BinaryRobloxChunk chunk)
{
using (BinaryRobloxReader reader = chunk.GetReader("SSTR"))
{
Version = reader.ReadInt32();
NumHashes = reader.ReadInt32();
for (uint id = 0; id < NumHashes; id++)
{
byte[] md5 = reader.ReadBytes(16);
int length = reader.ReadInt32();
byte[] data = reader.ReadBytes(length);
string key = Convert.ToBase64String(md5);
string value = Convert.ToBase64String(data);
Lookup.Add(key, id);
Strings.Add(id, value);
}
}
}
}
}