Added support for SharedStrings and SSTR chunk type
This commit is contained in:
@ -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.
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
37
BinaryFormat/ChunkTypes/SSTR.cs
Normal file
37
BinaryFormat/ChunkTypes/SSTR.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user