Roblox-File-Format/BinaryFormat/BinaryFileChunk.cs
CloneTrooper1019 de8df15d3f Large scale refactor to add class support!
Instance classes are now strongly typed with real property fields that
are derived from the JSON API Dump! This required a lot of reworking
across the board:

- Classes and Enums are auto-generated in the 'Generated' folder now.
This is done using a custom built-in plugin, which can be found in
the Plugins folder of this project.
- Property objects are now tied to .NET's reflection system. Reading
and writing from them will try to redirect into a field of the
Instance they are bound to.
- Property types that were loosely defined now have proper data types
(such as Color3uint8, Content, ProtectedString, SharedString, etc)
- Fixed an error with the CFrame directional vectors.
- The binary PRNT chunk now writes instances in child->parent order.
- Enums are now generated correctly, with up-to-date values.
- INST chunks are now referred to as 'Classes' instead of 'Types'.
- Unary operator added to Vector2 and Vector3.
- CollectionService tags can now be manipulated per-instance using
the Instance.Tags member.
- The Instance.Archivable property now works correctly.
- XML files now save/load metadata correctly.
- Cleaned up the property tokens directory.

I probably missed a few things, but that's a general overview of
everything that changed.
2019-06-30 17:01:19 -05:00

125 lines
3.8 KiB
C#

using System;
using System.IO;
using System.Text;
using LZ4;
namespace RobloxFiles.BinaryFormat
{
/// <summary>
/// BinaryRobloxFileChunk represents a generic LZ4-compressed chunk
/// of data in Roblox's Binary File Format.
/// </summary>
public class BinaryRobloxFileChunk
{
public readonly string ChunkType;
public readonly int Reserved;
public readonly int CompressedSize;
public readonly int Size;
public readonly byte[] CompressedData;
public readonly byte[] Data;
public bool HasCompressedData => (CompressedSize > 0);
public IBinaryFileChunk Handler { get; internal set; }
public bool HasWriteBuffer { get; private set; }
public byte[] WriteBuffer { get; private set; }
public BinaryRobloxFileReader GetDataReader(BinaryRobloxFile file)
{
MemoryStream buffer = new MemoryStream(Data);
return new BinaryRobloxFileReader(file, buffer);
}
public override string ToString()
{
string chunkType = ChunkType.Replace('\0', ' ');
return $"'{chunkType}' Chunk ({Size} bytes) [{Handler?.ToString()}]";
}
public BinaryRobloxFileChunk(BinaryRobloxFileReader reader)
{
byte[] rawChunkType = reader.ReadBytes(4);
ChunkType = Encoding.ASCII.GetString(rawChunkType);
CompressedSize = reader.ReadInt32();
Size = reader.ReadInt32();
Reserved = reader.ReadInt32();
if (HasCompressedData)
{
CompressedData = reader.ReadBytes(CompressedSize);
Data = LZ4Codec.Decode(CompressedData, 0, CompressedSize, Size);
}
else
{
Data = reader.ReadBytes(Size);
}
}
public BinaryRobloxFileChunk(BinaryRobloxFileWriter writer, bool compress = true)
{
if (!writer.WritingChunk)
throw new Exception("BinaryRobloxFileChunk: Supplied writer must have WritingChunk set to true.");
Stream stream = writer.BaseStream;
using (BinaryReader reader = new BinaryReader(stream, Encoding.UTF8, true))
{
long length = (stream.Position - writer.ChunkStart);
stream.Position = writer.ChunkStart;
Size = (int)length;
Data = reader.ReadBytes(Size);
}
CompressedData = LZ4Codec.Encode(Data, 0, Size);
CompressedSize = CompressedData.Length;
if (!compress || CompressedSize > Size)
{
CompressedSize = 0;
CompressedData = new byte[0];
}
ChunkType = writer.ChunkType;
Reserved = 0;
}
public void WriteChunk(BinaryRobloxFileWriter writer)
{
// Record where we are when we start writing.
var stream = writer.BaseStream;
long startPos = stream.Position;
// Write the chunk's data.
writer.WriteString(ChunkType, true);
writer.Write(CompressedSize);
writer.Write(Size);
writer.Write(Reserved);
if (CompressedSize > 0)
writer.Write(CompressedData);
else
writer.Write(Data);
// Capture the data we wrote into a byte[] array.
long endPos = stream.Position;
int length = (int)(endPos - startPos);
using (MemoryStream buffer = new MemoryStream())
{
stream.Position = startPos;
stream.CopyTo(buffer, length);
WriteBuffer = buffer.ToArray();
HasWriteBuffer = true;
}
}
}
}