2019-01-26 00:39:37 +00:00
|
|
|
|
using System;
|
2019-06-08 03:43:28 +00:00
|
|
|
|
using System.Collections.Generic;
|
2019-01-26 00:39:37 +00:00
|
|
|
|
using System.Linq;
|
2019-06-30 22:01:19 +00:00
|
|
|
|
using System.Text;
|
2019-01-26 00:39:37 +00:00
|
|
|
|
|
2019-02-01 17:19:20 +00:00
|
|
|
|
using RobloxFiles.Enums;
|
|
|
|
|
using RobloxFiles.DataTypes;
|
2020-09-10 05:08:12 +00:00
|
|
|
|
using RobloxFiles.Utility;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
using System.IO;
|
2019-01-26 00:39:37 +00:00
|
|
|
|
|
2019-02-01 17:19:20 +00:00
|
|
|
|
namespace RobloxFiles.BinaryFormat.Chunks
|
2019-01-26 00:39:37 +00:00
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
public class PROP : IBinaryFileChunk
|
2019-01-26 00:39:37 +00:00
|
|
|
|
{
|
2020-09-10 05:08:12 +00:00
|
|
|
|
private BinaryRobloxFile File;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
|
2019-06-08 03:43:28 +00:00
|
|
|
|
public string Name { get; internal set; }
|
2019-06-30 22:01:19 +00:00
|
|
|
|
public int ClassIndex { get; internal set; }
|
2021-05-01 22:40:09 +00:00
|
|
|
|
|
|
|
|
|
private INST Class => File.Classes[ClassIndex];
|
|
|
|
|
public string ClassName => Class?.ClassName;
|
2019-02-04 19:30:33 +00:00
|
|
|
|
|
2019-06-08 03:43:28 +00:00
|
|
|
|
public PropertyType Type { get; internal set; }
|
2019-06-30 22:01:19 +00:00
|
|
|
|
|
2021-05-01 22:40:09 +00:00
|
|
|
|
internal byte TypeId
|
2019-06-18 20:49:41 +00:00
|
|
|
|
{
|
|
|
|
|
get { return (byte)Type; }
|
2021-05-01 22:40:09 +00:00
|
|
|
|
set { Type = (PropertyType)value; }
|
2019-06-18 20:49:41 +00:00
|
|
|
|
}
|
2019-06-30 22:01:19 +00:00
|
|
|
|
|
|
|
|
|
public override string ToString()
|
|
|
|
|
{
|
|
|
|
|
return $"{Type} {ClassName}.{Name}";
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-13 01:19:30 +00:00
|
|
|
|
public void Load(BinaryRobloxFileReader reader)
|
2019-01-26 00:39:37 +00:00
|
|
|
|
{
|
2021-05-01 22:40:09 +00:00
|
|
|
|
File = reader.File;
|
2019-06-30 22:01:19 +00:00
|
|
|
|
ClassIndex = reader.ReadInt32();
|
2019-06-08 03:43:28 +00:00
|
|
|
|
Name = reader.ReadString();
|
|
|
|
|
|
2021-05-01 22:40:09 +00:00
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
byte propType = reader.ReadByte();
|
|
|
|
|
Type = (PropertyType)propType;
|
|
|
|
|
}
|
|
|
|
|
catch (EndOfStreamException)
|
|
|
|
|
{
|
|
|
|
|
RobloxFile.LogError($"Got corrupted PROP chunk (@ {this})!");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-01-26 00:39:37 +00:00
|
|
|
|
|
2021-08-09 17:22:26 +00:00
|
|
|
|
if (Class == null)
|
|
|
|
|
{
|
|
|
|
|
RobloxFile.LogError($"Unknown class index {ClassIndex} (@ {this})!");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-01 22:40:09 +00:00
|
|
|
|
var ids = Class.InstanceIds;
|
|
|
|
|
int instCount = Class.NumInstances;
|
|
|
|
|
var props = new Property[instCount];
|
2019-01-26 00:39:37 +00:00
|
|
|
|
|
|
|
|
|
for (int i = 0; i < instCount; i++)
|
|
|
|
|
{
|
2019-02-04 19:30:33 +00:00
|
|
|
|
int id = ids[i];
|
2021-05-01 22:40:09 +00:00
|
|
|
|
Instance instance = File.Instances[id];
|
2019-01-30 06:36:56 +00:00
|
|
|
|
|
2020-10-30 20:07:11 +00:00
|
|
|
|
if (instance == null)
|
|
|
|
|
{
|
2021-01-20 20:45:58 +00:00
|
|
|
|
RobloxFile.LogError($"PROP: No instance @{id} for property {ClassName}.{Name}");
|
2020-10-30 20:07:11 +00:00
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-01 22:40:09 +00:00
|
|
|
|
var prop = new Property(instance, this);
|
2019-01-29 09:50:55 +00:00
|
|
|
|
props[i] = prop;
|
2019-05-17 06:14:04 +00:00
|
|
|
|
|
2019-06-30 22:01:19 +00:00
|
|
|
|
instance.AddProperty(ref prop);
|
2019-01-26 00:39:37 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-17 06:14:04 +00:00
|
|
|
|
// Setup some short-hand functions for actions used during the read procedure.
|
2022-10-13 02:19:43 +00:00
|
|
|
|
var readInts = new Func<int[]>(() => reader.ReadInterleaved(instCount, reader.RotateInt32));
|
|
|
|
|
var readFloats = new Func<float[]>(() => reader.ReadInterleaved(instCount, reader.RotateFloat));
|
2019-05-17 06:14:04 +00:00
|
|
|
|
|
2019-06-08 03:43:28 +00:00
|
|
|
|
var readProperties = new Action<Func<int, object>>(read =>
|
2019-01-26 00:39:37 +00:00
|
|
|
|
{
|
|
|
|
|
for (int i = 0; i < instCount; i++)
|
|
|
|
|
{
|
2020-09-14 16:20:34 +00:00
|
|
|
|
var prop = props[i];
|
2020-10-30 20:07:11 +00:00
|
|
|
|
|
|
|
|
|
if (prop == null)
|
|
|
|
|
continue;
|
|
|
|
|
|
2020-09-14 16:20:34 +00:00
|
|
|
|
prop.Value = read(i);
|
2019-01-26 00:39:37 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
2019-01-29 09:50:55 +00:00
|
|
|
|
|
2019-02-04 19:30:33 +00:00
|
|
|
|
switch (Type)
|
2019-01-26 00:39:37 +00:00
|
|
|
|
{
|
|
|
|
|
case PropertyType.String:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
readProperties(i =>
|
2019-01-29 09:50:55 +00:00
|
|
|
|
{
|
2019-06-30 22:01:19 +00:00
|
|
|
|
string value = reader.ReadString();
|
2021-05-01 22:40:09 +00:00
|
|
|
|
|
2019-01-29 09:50:55 +00:00
|
|
|
|
// Leave an access point for the original byte sequence, in case this is a BinaryString.
|
|
|
|
|
// This will allow the developer to read the sequence without any mangling from C# strings.
|
2019-06-08 03:43:28 +00:00
|
|
|
|
byte[] buffer = reader.GetLastStringBuffer();
|
2019-05-19 04:44:51 +00:00
|
|
|
|
props[i].RawBuffer = buffer;
|
2019-01-29 09:50:55 +00:00
|
|
|
|
|
2019-06-30 22:01:19 +00:00
|
|
|
|
// Check if this is going to be casted as a BinaryString.
|
|
|
|
|
// BinaryStrings should use a type of byte[] instead.
|
|
|
|
|
|
2020-09-10 05:08:12 +00:00
|
|
|
|
switch (Name)
|
|
|
|
|
{
|
|
|
|
|
case "Tags":
|
|
|
|
|
case "AttributesSerialize":
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2020-09-10 05:08:12 +00:00
|
|
|
|
return buffer;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2020-09-10 05:08:12 +00:00
|
|
|
|
default:
|
|
|
|
|
{
|
|
|
|
|
Property prop = props[i];
|
|
|
|
|
Instance instance = prop.Instance;
|
2019-11-01 02:40:31 +00:00
|
|
|
|
|
2020-09-10 05:08:12 +00:00
|
|
|
|
Type instType = instance.GetType();
|
|
|
|
|
var member = ImplicitMember.Get(instType, Name);
|
2019-06-30 22:01:19 +00:00
|
|
|
|
|
2020-09-10 05:08:12 +00:00
|
|
|
|
if (member != null)
|
|
|
|
|
{
|
|
|
|
|
object result = value;
|
|
|
|
|
Type memberType = member.MemberType;
|
2019-06-30 22:01:19 +00:00
|
|
|
|
|
2020-09-10 05:08:12 +00:00
|
|
|
|
if (memberType == typeof(byte[]))
|
|
|
|
|
result = buffer;
|
2019-06-30 22:01:19 +00:00
|
|
|
|
|
2020-09-10 05:08:12 +00:00
|
|
|
|
return result;
|
|
|
|
|
}
|
2019-06-30 22:01:19 +00:00
|
|
|
|
|
2020-09-10 05:08:12 +00:00
|
|
|
|
return value;
|
|
|
|
|
}
|
2019-06-30 22:01:19 +00:00
|
|
|
|
}
|
2019-01-29 09:50:55 +00:00
|
|
|
|
});
|
|
|
|
|
|
2019-01-26 00:39:37 +00:00
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-01-26 00:39:37 +00:00
|
|
|
|
case PropertyType.Bool:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
readProperties(i => reader.ReadBoolean());
|
2019-01-26 00:39:37 +00:00
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-01-26 00:39:37 +00:00
|
|
|
|
case PropertyType.Int:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2019-01-29 09:50:55 +00:00
|
|
|
|
int[] ints = readInts();
|
2019-06-08 03:43:28 +00:00
|
|
|
|
readProperties(i => ints[i]);
|
2019-01-26 00:39:37 +00:00
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-01-26 00:39:37 +00:00
|
|
|
|
case PropertyType.Float:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2019-01-29 09:50:55 +00:00
|
|
|
|
float[] floats = readFloats();
|
2019-06-08 03:43:28 +00:00
|
|
|
|
readProperties(i => floats[i]);
|
2019-01-26 00:39:37 +00:00
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-01-26 00:39:37 +00:00
|
|
|
|
case PropertyType.Double:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
readProperties(i => reader.ReadDouble());
|
2019-01-26 00:39:37 +00:00
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-01-26 00:39:37 +00:00
|
|
|
|
case PropertyType.UDim:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2019-01-29 09:50:55 +00:00
|
|
|
|
float[] UDim_Scales = readFloats();
|
|
|
|
|
int[] UDim_Offsets = readInts();
|
2019-01-26 00:39:37 +00:00
|
|
|
|
|
2019-06-08 03:43:28 +00:00
|
|
|
|
readProperties(i =>
|
2019-01-26 00:39:37 +00:00
|
|
|
|
{
|
2019-01-29 09:50:55 +00:00
|
|
|
|
float scale = UDim_Scales[i];
|
|
|
|
|
int offset = UDim_Offsets[i];
|
2019-01-26 00:39:37 +00:00
|
|
|
|
return new UDim(scale, offset);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-01-26 00:39:37 +00:00
|
|
|
|
case PropertyType.UDim2:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
|
|
|
|
float[] UDim2_Scales_X = readFloats(),
|
2019-01-29 09:50:55 +00:00
|
|
|
|
UDim2_Scales_Y = readFloats();
|
|
|
|
|
|
2021-05-01 22:40:09 +00:00
|
|
|
|
int[] UDim2_Offsets_X = readInts(),
|
2019-01-29 09:50:55 +00:00
|
|
|
|
UDim2_Offsets_Y = readInts();
|
2019-01-26 00:39:37 +00:00
|
|
|
|
|
2019-06-08 03:43:28 +00:00
|
|
|
|
readProperties(i =>
|
2019-01-26 00:39:37 +00:00
|
|
|
|
{
|
2019-01-29 09:50:55 +00:00
|
|
|
|
float scaleX = UDim2_Scales_X[i],
|
|
|
|
|
scaleY = UDim2_Scales_Y[i];
|
|
|
|
|
|
|
|
|
|
int offsetX = UDim2_Offsets_X[i],
|
|
|
|
|
offsetY = UDim2_Offsets_Y[i];
|
|
|
|
|
|
2019-01-26 00:39:37 +00:00
|
|
|
|
return new UDim2(scaleX, offsetX, scaleY, offsetY);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-01-26 00:39:37 +00:00
|
|
|
|
case PropertyType.Ray:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
readProperties(i =>
|
2019-01-26 00:39:37 +00:00
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
float posX = reader.ReadFloat(),
|
|
|
|
|
posY = reader.ReadFloat(),
|
|
|
|
|
posZ = reader.ReadFloat();
|
2019-01-26 00:39:37 +00:00
|
|
|
|
|
2019-06-08 03:43:28 +00:00
|
|
|
|
float dirX = reader.ReadFloat(),
|
|
|
|
|
dirY = reader.ReadFloat(),
|
|
|
|
|
dirZ = reader.ReadFloat();
|
|
|
|
|
|
2021-05-01 22:40:09 +00:00
|
|
|
|
var origin = new Vector3(posX, posY, posZ);
|
|
|
|
|
var direction = new Vector3(dirX, dirY, dirZ);
|
2019-01-26 00:39:37 +00:00
|
|
|
|
|
|
|
|
|
return new Ray(origin, direction);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-01-26 00:39:37 +00:00
|
|
|
|
case PropertyType.Faces:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
readProperties(i =>
|
2019-01-26 00:39:37 +00:00
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
byte faces = reader.ReadByte();
|
2019-01-26 00:39:37 +00:00
|
|
|
|
return (Faces)faces;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-01-26 00:39:37 +00:00
|
|
|
|
case PropertyType.Axes:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
readProperties(i =>
|
2019-01-26 00:39:37 +00:00
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
byte axes = reader.ReadByte();
|
2019-01-26 00:39:37 +00:00
|
|
|
|
return (Axes)axes;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-01-26 00:39:37 +00:00
|
|
|
|
case PropertyType.BrickColor:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2019-02-25 03:17:53 +00:00
|
|
|
|
int[] BrickColorIds = readInts();
|
2019-01-26 00:39:37 +00:00
|
|
|
|
|
2019-06-08 03:43:28 +00:00
|
|
|
|
readProperties(i =>
|
2019-01-26 00:39:37 +00:00
|
|
|
|
{
|
2021-05-01 22:40:09 +00:00
|
|
|
|
BrickColor color = BrickColorIds[i];
|
|
|
|
|
return color;
|
2019-01-26 00:39:37 +00:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-01-26 00:39:37 +00:00
|
|
|
|
case PropertyType.Color3:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2019-03-31 07:07:15 +00:00
|
|
|
|
float[] Color3_R = readFloats(),
|
|
|
|
|
Color3_G = readFloats(),
|
|
|
|
|
Color3_B = readFloats();
|
2019-01-26 00:39:37 +00:00
|
|
|
|
|
2019-06-08 03:43:28 +00:00
|
|
|
|
readProperties(i =>
|
2019-01-26 00:39:37 +00:00
|
|
|
|
{
|
2019-03-31 07:07:15 +00:00
|
|
|
|
float r = Color3_R[i],
|
|
|
|
|
g = Color3_G[i],
|
|
|
|
|
b = Color3_B[i];
|
2019-01-26 00:39:37 +00:00
|
|
|
|
|
|
|
|
|
return new Color3(r, g, b);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-01-26 00:39:37 +00:00
|
|
|
|
case PropertyType.Vector2:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2019-02-25 03:17:53 +00:00
|
|
|
|
float[] Vector2_X = readFloats(),
|
|
|
|
|
Vector2_Y = readFloats();
|
2019-01-26 00:39:37 +00:00
|
|
|
|
|
2019-06-08 03:43:28 +00:00
|
|
|
|
readProperties(i =>
|
2019-01-26 00:39:37 +00:00
|
|
|
|
{
|
2019-02-25 03:17:53 +00:00
|
|
|
|
float x = Vector2_X[i],
|
|
|
|
|
y = Vector2_Y[i];
|
2019-01-26 00:39:37 +00:00
|
|
|
|
|
|
|
|
|
return new Vector2(x, y);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-01-26 00:39:37 +00:00
|
|
|
|
case PropertyType.Vector3:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2019-02-25 03:17:53 +00:00
|
|
|
|
float[] Vector3_X = readFloats(),
|
|
|
|
|
Vector3_Y = readFloats(),
|
|
|
|
|
Vector3_Z = readFloats();
|
2019-01-26 00:39:37 +00:00
|
|
|
|
|
2019-06-08 03:43:28 +00:00
|
|
|
|
readProperties(i =>
|
2019-01-26 00:39:37 +00:00
|
|
|
|
{
|
2019-02-25 03:17:53 +00:00
|
|
|
|
float x = Vector3_X[i],
|
|
|
|
|
y = Vector3_Y[i],
|
|
|
|
|
z = Vector3_Z[i];
|
2019-01-26 00:39:37 +00:00
|
|
|
|
|
|
|
|
|
return new Vector3(x, y, z);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-01-26 00:39:37 +00:00
|
|
|
|
case PropertyType.CFrame:
|
|
|
|
|
case PropertyType.Quaternion:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
case PropertyType.OptionalCFrame:
|
|
|
|
|
{
|
2019-06-30 22:01:19 +00:00
|
|
|
|
float[][] matrices = new float[instCount][];
|
2019-01-26 00:39:37 +00:00
|
|
|
|
|
2021-05-01 22:40:09 +00:00
|
|
|
|
if (Type == PropertyType.OptionalCFrame)
|
|
|
|
|
{
|
|
|
|
|
byte cframeType = (byte)PropertyType.CFrame;
|
|
|
|
|
byte readType = reader.ReadByte();
|
|
|
|
|
|
|
|
|
|
if (readType != cframeType)
|
|
|
|
|
{
|
|
|
|
|
RobloxFile.LogError($"Unexpected property type in OptionalCFrame (expected {cframeType}, got {readType})");
|
|
|
|
|
readProperties(i => null);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-30 22:01:19 +00:00
|
|
|
|
for (int i = 0; i < instCount; i++)
|
2019-01-26 00:39:37 +00:00
|
|
|
|
{
|
2019-06-30 22:01:19 +00:00
|
|
|
|
byte rawOrientId = reader.ReadByte();
|
2021-05-01 22:40:09 +00:00
|
|
|
|
|
2019-06-30 22:01:19 +00:00
|
|
|
|
if (rawOrientId > 0)
|
2019-01-26 00:39:37 +00:00
|
|
|
|
{
|
2019-01-29 09:50:55 +00:00
|
|
|
|
// Make sure this value is in a safe range.
|
2019-06-30 22:01:19 +00:00
|
|
|
|
int orientId = (rawOrientId - 1) % 36;
|
2022-12-27 12:36:50 +00:00
|
|
|
|
var cf = CFrame.FromOrientId(orientId);
|
|
|
|
|
matrices[i] = cf.GetComponents();
|
2019-01-26 00:39:37 +00:00
|
|
|
|
}
|
2019-02-04 19:30:33 +00:00
|
|
|
|
else if (Type == PropertyType.Quaternion)
|
2019-01-26 00:39:37 +00:00
|
|
|
|
{
|
2021-05-01 22:40:09 +00:00
|
|
|
|
float qx = reader.ReadFloat(),
|
|
|
|
|
qy = reader.ReadFloat(),
|
|
|
|
|
qz = reader.ReadFloat(),
|
|
|
|
|
qw = reader.ReadFloat();
|
2019-01-26 00:39:37 +00:00
|
|
|
|
|
2021-05-01 22:40:09 +00:00
|
|
|
|
var quaternion = new Quaternion(qx, qy, qz, qw);
|
2019-02-04 19:30:33 +00:00
|
|
|
|
var rotation = quaternion.ToCFrame();
|
2021-05-01 22:40:09 +00:00
|
|
|
|
|
2019-06-30 22:01:19 +00:00
|
|
|
|
matrices[i] = rotation.GetComponents();
|
2019-01-26 00:39:37 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
float[] matrix = new float[9];
|
|
|
|
|
|
|
|
|
|
for (int m = 0; m < 9; m++)
|
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
float value = reader.ReadFloat();
|
2019-01-26 00:39:37 +00:00
|
|
|
|
matrix[m] = value;
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-30 22:01:19 +00:00
|
|
|
|
matrices[i] = matrix;
|
2019-01-26 00:39:37 +00:00
|
|
|
|
}
|
2019-06-30 22:01:19 +00:00
|
|
|
|
}
|
2019-01-26 00:39:37 +00:00
|
|
|
|
|
2019-02-25 03:17:53 +00:00
|
|
|
|
float[] CFrame_X = readFloats(),
|
|
|
|
|
CFrame_Y = readFloats(),
|
|
|
|
|
CFrame_Z = readFloats();
|
2019-01-26 00:39:37 +00:00
|
|
|
|
|
2021-05-01 22:40:09 +00:00
|
|
|
|
var CFrames = new CFrame[instCount];
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < instCount; i++)
|
2019-01-26 00:39:37 +00:00
|
|
|
|
{
|
2019-06-30 22:01:19 +00:00
|
|
|
|
float[] matrix = matrices[i];
|
2019-01-26 00:39:37 +00:00
|
|
|
|
|
2019-02-25 03:17:53 +00:00
|
|
|
|
float x = CFrame_X[i],
|
|
|
|
|
y = CFrame_Y[i],
|
|
|
|
|
z = CFrame_Z[i];
|
2019-01-26 00:39:37 +00:00
|
|
|
|
|
2019-06-30 22:01:19 +00:00
|
|
|
|
float[] components;
|
|
|
|
|
|
|
|
|
|
if (matrix.Length == 12)
|
|
|
|
|
{
|
|
|
|
|
matrix[0] = x;
|
|
|
|
|
matrix[1] = y;
|
|
|
|
|
matrix[2] = z;
|
|
|
|
|
|
|
|
|
|
components = matrix;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
float[] position = new float[3] { x, y, z };
|
|
|
|
|
components = position.Concat(matrix).ToArray();
|
|
|
|
|
}
|
2019-01-26 00:39:37 +00:00
|
|
|
|
|
2021-05-01 22:40:09 +00:00
|
|
|
|
CFrames[i] = new CFrame(components);
|
|
|
|
|
}
|
2019-01-26 00:39:37 +00:00
|
|
|
|
|
2021-05-01 22:40:09 +00:00
|
|
|
|
if (Type == PropertyType.OptionalCFrame)
|
|
|
|
|
{
|
|
|
|
|
byte boolType = (byte)PropertyType.Bool;
|
|
|
|
|
byte readType = reader.ReadByte();
|
|
|
|
|
|
|
|
|
|
if (readType != boolType)
|
|
|
|
|
{
|
|
|
|
|
RobloxFile.LogError($"Unexpected property type in OptionalCFrame (expected {boolType}, got {readType})");
|
|
|
|
|
readProperties(i => null);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < instCount; i++)
|
|
|
|
|
{
|
2021-05-05 18:06:20 +00:00
|
|
|
|
CFrame cf = CFrames[i];
|
|
|
|
|
bool archivable = reader.ReadBoolean();
|
|
|
|
|
|
|
|
|
|
if (!archivable)
|
|
|
|
|
cf = null;
|
|
|
|
|
|
|
|
|
|
CFrames[i] = new Optional<CFrame>(cf);
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
readProperties(i => CFrames[i]);
|
2019-01-26 00:39:37 +00:00
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-01-26 00:39:37 +00:00
|
|
|
|
case PropertyType.Enum:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2022-10-13 02:19:43 +00:00
|
|
|
|
uint[] enums = reader.ReadInterleaved(instCount, BitConverter.ToUInt32);
|
2019-06-30 22:01:19 +00:00
|
|
|
|
|
|
|
|
|
readProperties(i =>
|
|
|
|
|
{
|
|
|
|
|
Property prop = props[i];
|
|
|
|
|
Instance instance = prop.Instance;
|
|
|
|
|
|
|
|
|
|
Type instType = instance.GetType();
|
|
|
|
|
uint value = enums[i];
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
2020-09-10 05:08:12 +00:00
|
|
|
|
var info = ImplicitMember.Get(instType, Name);
|
2020-09-21 18:29:31 +00:00
|
|
|
|
|
|
|
|
|
if (info == null)
|
|
|
|
|
{
|
2021-05-01 22:40:09 +00:00
|
|
|
|
RobloxFile.LogError($"Enum cast failed for {ClassName}.{Name} using value {value}!");
|
2020-09-21 18:29:31 +00:00
|
|
|
|
return value;
|
|
|
|
|
}
|
2021-05-01 22:40:09 +00:00
|
|
|
|
|
2020-09-10 05:08:12 +00:00
|
|
|
|
return Enum.Parse(info.MemberType, value.ToInvariantString());
|
2019-06-30 22:01:19 +00:00
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
2021-05-01 22:40:09 +00:00
|
|
|
|
RobloxFile.LogError($"Enum cast failed for {ClassName}.{Name} using value {value}!");
|
2019-06-30 22:01:19 +00:00
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
});
|
2019-01-29 09:50:55 +00:00
|
|
|
|
|
2019-01-26 00:39:37 +00:00
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-01-26 00:39:37 +00:00
|
|
|
|
case PropertyType.Ref:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
var instIds = reader.ReadInstanceIds(instCount);
|
2019-01-26 00:39:37 +00:00
|
|
|
|
|
2019-06-08 03:43:28 +00:00
|
|
|
|
readProperties(i =>
|
2019-01-26 00:39:37 +00:00
|
|
|
|
{
|
|
|
|
|
int instId = instIds[i];
|
2021-05-05 00:45:00 +00:00
|
|
|
|
|
|
|
|
|
if (instId >= File.NumInstances)
|
|
|
|
|
{
|
|
|
|
|
RobloxFile.LogError($"Got out of bounds referent index in {ClassName}.{Name}!");
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-01 22:40:09 +00:00
|
|
|
|
return instId >= 0 ? File.Instances[instId] : null;
|
2019-01-26 00:39:37 +00:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-01-26 00:39:37 +00:00
|
|
|
|
case PropertyType.Vector3int16:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
readProperties(i =>
|
2019-01-26 00:39:37 +00:00
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
short x = reader.ReadInt16(),
|
|
|
|
|
y = reader.ReadInt16(),
|
|
|
|
|
z = reader.ReadInt16();
|
2019-01-26 00:39:37 +00:00
|
|
|
|
|
|
|
|
|
return new Vector3int16(x, y, z);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-01-26 00:39:37 +00:00
|
|
|
|
case PropertyType.NumberSequence:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
readProperties(i =>
|
2019-01-26 00:39:37 +00:00
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
int numKeys = reader.ReadInt32();
|
2019-01-29 09:50:55 +00:00
|
|
|
|
var keypoints = new NumberSequenceKeypoint[numKeys];
|
2019-01-26 00:39:37 +00:00
|
|
|
|
|
2019-01-29 09:50:55 +00:00
|
|
|
|
for (int key = 0; key < numKeys; key++)
|
2019-01-26 00:39:37 +00:00
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
float Time = reader.ReadFloat(),
|
|
|
|
|
Value = reader.ReadFloat(),
|
|
|
|
|
Envelope = reader.ReadFloat();
|
2019-01-26 00:39:37 +00:00
|
|
|
|
|
2019-01-29 09:50:55 +00:00
|
|
|
|
keypoints[key] = new NumberSequenceKeypoint(Time, Value, Envelope);
|
2019-01-26 00:39:37 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return new NumberSequence(keypoints);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-01-26 00:39:37 +00:00
|
|
|
|
case PropertyType.ColorSequence:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
readProperties(i =>
|
2019-01-26 00:39:37 +00:00
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
int numKeys = reader.ReadInt32();
|
2019-01-29 09:50:55 +00:00
|
|
|
|
var keypoints = new ColorSequenceKeypoint[numKeys];
|
2019-01-26 00:39:37 +00:00
|
|
|
|
|
2019-01-29 09:50:55 +00:00
|
|
|
|
for (int key = 0; key < numKeys; key++)
|
2019-01-26 00:39:37 +00:00
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
float Time = reader.ReadFloat(),
|
|
|
|
|
R = reader.ReadFloat(),
|
|
|
|
|
G = reader.ReadFloat(),
|
|
|
|
|
B = reader.ReadFloat();
|
2019-02-04 19:30:33 +00:00
|
|
|
|
|
|
|
|
|
Color3 Value = new Color3(R, G, B);
|
2019-06-30 22:01:19 +00:00
|
|
|
|
int Envelope = reader.ReadInt32();
|
2019-02-04 19:30:33 +00:00
|
|
|
|
|
2019-06-30 22:01:19 +00:00
|
|
|
|
keypoints[key] = new ColorSequenceKeypoint(Time, Value, Envelope);
|
2019-01-26 00:39:37 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return new ColorSequence(keypoints);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-01-26 00:39:37 +00:00
|
|
|
|
case PropertyType.NumberRange:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
readProperties(i =>
|
2019-01-26 00:39:37 +00:00
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
float min = reader.ReadFloat();
|
|
|
|
|
float max = reader.ReadFloat();
|
2019-01-26 00:39:37 +00:00
|
|
|
|
|
|
|
|
|
return new NumberRange(min, max);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-01-26 00:39:37 +00:00
|
|
|
|
case PropertyType.Rect:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2019-01-29 09:50:55 +00:00
|
|
|
|
float[] Rect_X0 = readFloats(), Rect_Y0 = readFloats(),
|
|
|
|
|
Rect_X1 = readFloats(), Rect_Y1 = readFloats();
|
2019-01-26 00:39:37 +00:00
|
|
|
|
|
2019-06-08 03:43:28 +00:00
|
|
|
|
readProperties(i =>
|
2019-01-26 00:39:37 +00:00
|
|
|
|
{
|
2019-01-29 09:50:55 +00:00
|
|
|
|
float x0 = Rect_X0[i], y0 = Rect_Y0[i],
|
|
|
|
|
x1 = Rect_X1[i], y1 = Rect_Y1[i];
|
2019-01-26 00:39:37 +00:00
|
|
|
|
|
|
|
|
|
return new Rect(x0, y0, x1, y1);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-01-26 00:39:37 +00:00
|
|
|
|
case PropertyType.PhysicalProperties:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
readProperties(i =>
|
2019-01-26 00:39:37 +00:00
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
bool custom = reader.ReadBoolean();
|
2021-05-01 22:40:09 +00:00
|
|
|
|
|
2019-01-26 00:39:37 +00:00
|
|
|
|
if (custom)
|
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
float Density = reader.ReadFloat(),
|
|
|
|
|
Friction = reader.ReadFloat(),
|
|
|
|
|
Elasticity = reader.ReadFloat(),
|
|
|
|
|
FrictionWeight = reader.ReadFloat(),
|
|
|
|
|
ElasticityWeight = reader.ReadFloat();
|
2019-01-26 00:39:37 +00:00
|
|
|
|
|
|
|
|
|
return new PhysicalProperties
|
|
|
|
|
(
|
2019-01-29 09:50:55 +00:00
|
|
|
|
Density,
|
|
|
|
|
Friction,
|
|
|
|
|
Elasticity,
|
|
|
|
|
FrictionWeight,
|
|
|
|
|
ElasticityWeight
|
2019-01-26 00:39:37 +00:00
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-01-26 00:39:37 +00:00
|
|
|
|
case PropertyType.Color3uint8:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
byte[] Color3uint8_R = reader.ReadBytes(instCount),
|
|
|
|
|
Color3uint8_G = reader.ReadBytes(instCount),
|
|
|
|
|
Color3uint8_B = reader.ReadBytes(instCount);
|
2021-05-01 22:40:09 +00:00
|
|
|
|
|
2019-06-08 03:43:28 +00:00
|
|
|
|
readProperties(i =>
|
2019-01-26 00:39:37 +00:00
|
|
|
|
{
|
2019-02-25 03:17:53 +00:00
|
|
|
|
byte r = Color3uint8_R[i],
|
|
|
|
|
g = Color3uint8_G[i],
|
|
|
|
|
b = Color3uint8_B[i];
|
2019-01-26 00:39:37 +00:00
|
|
|
|
|
2019-06-30 22:01:19 +00:00
|
|
|
|
Color3uint8 result = Color3.FromRGB(r, g, b);
|
|
|
|
|
return result;
|
2019-01-26 00:39:37 +00:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-01-26 00:39:37 +00:00
|
|
|
|
case PropertyType.Int64:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2022-10-13 02:19:43 +00:00
|
|
|
|
var values = reader.ReadInterleaved(instCount, reader.RotateInt64);
|
|
|
|
|
readProperties(i => values[i]);
|
2019-01-26 00:39:37 +00:00
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-05-17 06:14:04 +00:00
|
|
|
|
case PropertyType.SharedString:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2022-10-13 02:19:43 +00:00
|
|
|
|
var keys = reader.ReadInterleaved(instCount, BitConverter.ToUInt32);
|
2019-05-17 06:14:04 +00:00
|
|
|
|
|
2019-06-08 03:43:28 +00:00
|
|
|
|
readProperties(i =>
|
2019-05-17 06:14:04 +00:00
|
|
|
|
{
|
2022-10-13 02:19:43 +00:00
|
|
|
|
uint key = keys[i];
|
2021-05-01 22:40:09 +00:00
|
|
|
|
return File.SharedStrings[key];
|
2019-05-17 06:14:04 +00:00
|
|
|
|
});
|
|
|
|
|
|
2020-07-13 01:19:30 +00:00
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2020-07-13 01:19:30 +00:00
|
|
|
|
case PropertyType.ProtectedString:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2020-07-13 01:19:30 +00:00
|
|
|
|
readProperties(i =>
|
|
|
|
|
{
|
|
|
|
|
int length = reader.ReadInt32();
|
|
|
|
|
byte[] buffer = reader.ReadBytes(length);
|
|
|
|
|
|
|
|
|
|
return new ProtectedString(buffer);
|
|
|
|
|
});
|
|
|
|
|
|
2019-05-17 06:14:04 +00:00
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2022-02-10 21:22:44 +00:00
|
|
|
|
case PropertyType.UniqueId:
|
|
|
|
|
{
|
2022-10-13 02:19:43 +00:00
|
|
|
|
var uniqueIds = reader.ReadInterleaved(instCount, (buffer, offset) =>
|
2022-02-10 21:22:44 +00:00
|
|
|
|
{
|
2022-10-13 02:19:43 +00:00
|
|
|
|
var random = reader.RotateInt64(buffer, 0);
|
|
|
|
|
var time = BitConverter.ToUInt32(buffer, 8);
|
|
|
|
|
var index = BitConverter.ToUInt32(buffer, 12);
|
|
|
|
|
|
|
|
|
|
return new UniqueId(random, time, index);
|
2022-02-10 21:22:44 +00:00
|
|
|
|
});
|
|
|
|
|
|
2022-10-13 02:19:43 +00:00
|
|
|
|
readProperties(i => uniqueIds[i]);
|
2022-02-10 21:22:44 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
2022-04-15 01:52:05 +00:00
|
|
|
|
case PropertyType.FontFace:
|
|
|
|
|
{
|
|
|
|
|
readProperties(i =>
|
|
|
|
|
{
|
|
|
|
|
string family = reader.ReadString();
|
|
|
|
|
|
|
|
|
|
var weight = (FontWeight)reader.ReadUInt16();
|
|
|
|
|
var style = (FontStyle)reader.ReadByte();
|
2022-10-13 02:19:43 +00:00
|
|
|
|
var cachedFaceId = reader.ReadString();
|
2022-04-15 01:52:05 +00:00
|
|
|
|
|
2022-10-13 02:19:43 +00:00
|
|
|
|
return new FontFace(family, weight, style, cachedFaceId);
|
2022-04-15 01:52:05 +00:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
2019-05-17 06:14:04 +00:00
|
|
|
|
default:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
|
|
|
|
RobloxFile.LogError($"Unhandled property type: {Type} in {this}!");
|
2019-05-17 06:14:04 +00:00
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-06-08 03:43:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
reader.Dispose();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal static Dictionary<string, PROP> CollectProperties(BinaryRobloxFileWriter writer, INST inst)
|
|
|
|
|
{
|
|
|
|
|
BinaryRobloxFile file = writer.File;
|
|
|
|
|
var propMap = new Dictionary<string, PROP>();
|
|
|
|
|
|
|
|
|
|
foreach (int instId in inst.InstanceIds)
|
|
|
|
|
{
|
|
|
|
|
Instance instance = file.Instances[instId];
|
2019-06-30 22:01:19 +00:00
|
|
|
|
var props = instance.RefreshProperties();
|
2019-06-08 03:43:28 +00:00
|
|
|
|
|
2019-06-30 22:01:19 +00:00
|
|
|
|
foreach (string propName in props.Keys)
|
2019-06-08 03:43:28 +00:00
|
|
|
|
{
|
2020-09-10 05:08:12 +00:00
|
|
|
|
if (propName == "Archivable")
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (propName.Contains("__"))
|
|
|
|
|
continue;
|
|
|
|
|
|
2019-06-08 03:43:28 +00:00
|
|
|
|
if (!propMap.ContainsKey(propName))
|
|
|
|
|
{
|
|
|
|
|
Property prop = props[propName];
|
|
|
|
|
|
|
|
|
|
PROP propChunk = new PROP()
|
|
|
|
|
{
|
|
|
|
|
Name = prop.Name,
|
|
|
|
|
Type = prop.Type,
|
2019-06-30 22:01:19 +00:00
|
|
|
|
|
2021-05-01 22:40:09 +00:00
|
|
|
|
File = writer.File,
|
2019-06-30 22:01:19 +00:00
|
|
|
|
ClassIndex = inst.ClassIndex
|
2019-06-08 03:43:28 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
propMap.Add(propName, propChunk);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return propMap;
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-13 01:19:30 +00:00
|
|
|
|
public void Save(BinaryRobloxFileWriter writer)
|
2019-06-08 03:43:28 +00:00
|
|
|
|
{
|
|
|
|
|
BinaryRobloxFile file = writer.File;
|
2020-09-10 05:08:12 +00:00
|
|
|
|
File = file;
|
2019-06-08 03:43:28 +00:00
|
|
|
|
|
2019-06-30 22:01:19 +00:00
|
|
|
|
INST inst = file.Classes[ClassIndex];
|
2019-06-08 03:43:28 +00:00
|
|
|
|
var props = new List<Property>();
|
|
|
|
|
|
|
|
|
|
foreach (int instId in inst.InstanceIds)
|
|
|
|
|
{
|
|
|
|
|
Instance instance = file.Instances[instId];
|
2020-10-30 20:07:11 +00:00
|
|
|
|
var instProps = instance.Properties;
|
2019-06-08 03:43:28 +00:00
|
|
|
|
|
2020-10-30 20:07:11 +00:00
|
|
|
|
if (!instProps.TryGetValue(Name, out Property prop))
|
2019-06-08 03:43:28 +00:00
|
|
|
|
throw new Exception($"Property {Name} must be defined in {instance.GetFullName()}!");
|
|
|
|
|
else if (prop.Type != Type)
|
|
|
|
|
throw new Exception($"Property {Name} is not using the correct type in {instance.GetFullName()}!");
|
|
|
|
|
|
|
|
|
|
props.Add(prop);
|
2019-01-26 00:39:37 +00:00
|
|
|
|
}
|
2019-01-29 09:50:55 +00:00
|
|
|
|
|
2019-06-30 22:01:19 +00:00
|
|
|
|
writer.Write(ClassIndex);
|
2019-06-08 03:43:28 +00:00
|
|
|
|
writer.WriteString(Name);
|
|
|
|
|
writer.Write(TypeId);
|
|
|
|
|
|
|
|
|
|
switch (Type)
|
|
|
|
|
{
|
|
|
|
|
case PropertyType.String:
|
|
|
|
|
props.ForEach(prop =>
|
|
|
|
|
{
|
2019-06-11 01:27:57 +00:00
|
|
|
|
byte[] buffer = prop.HasRawBuffer ? prop.RawBuffer : null;
|
|
|
|
|
|
|
|
|
|
if (buffer == null)
|
|
|
|
|
{
|
|
|
|
|
string value = prop.CastValue<string>();
|
|
|
|
|
buffer = Encoding.UTF8.GetBytes(value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
writer.Write(buffer.Length);
|
|
|
|
|
writer.Write(buffer);
|
2019-06-08 03:43:28 +00:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
case PropertyType.Bool:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2019-06-30 22:01:19 +00:00
|
|
|
|
props.ForEach(prop =>
|
|
|
|
|
{
|
|
|
|
|
bool value = prop.CastValue<bool>();
|
|
|
|
|
writer.Write(value);
|
|
|
|
|
});
|
|
|
|
|
|
2019-06-08 03:43:28 +00:00
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-06-08 03:43:28 +00:00
|
|
|
|
case PropertyType.Int:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
|
|
|
|
var ints = props
|
|
|
|
|
.Select(prop => prop.CastValue<int>())
|
|
|
|
|
.ToList();
|
2019-06-08 03:43:28 +00:00
|
|
|
|
|
|
|
|
|
writer.WriteInts(ints);
|
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-06-08 03:43:28 +00:00
|
|
|
|
case PropertyType.Float:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
|
|
|
|
var floats = props
|
|
|
|
|
.Select(prop => prop.CastValue<float>())
|
|
|
|
|
.ToList();
|
2019-06-08 03:43:28 +00:00
|
|
|
|
|
|
|
|
|
writer.WriteFloats(floats);
|
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-06-08 03:43:28 +00:00
|
|
|
|
case PropertyType.Double:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2020-08-20 07:03:05 +00:00
|
|
|
|
props.ForEach(prop =>
|
|
|
|
|
{
|
|
|
|
|
double value = prop.CastValue<double>();
|
|
|
|
|
writer.Write(BinaryRobloxFileWriter.GetBytes(value));
|
|
|
|
|
});
|
|
|
|
|
|
2019-06-08 03:43:28 +00:00
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-06-08 03:43:28 +00:00
|
|
|
|
case PropertyType.UDim:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
var UDim_Scales = new List<float>();
|
|
|
|
|
var UDim_Offsets = new List<int>();
|
|
|
|
|
|
|
|
|
|
props.ForEach(prop =>
|
|
|
|
|
{
|
|
|
|
|
UDim value = prop.CastValue<UDim>();
|
|
|
|
|
UDim_Scales.Add(value.Scale);
|
|
|
|
|
UDim_Offsets.Add(value.Offset);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
writer.WriteFloats(UDim_Scales);
|
|
|
|
|
writer.WriteInts(UDim_Offsets);
|
|
|
|
|
|
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-06-08 03:43:28 +00:00
|
|
|
|
case PropertyType.UDim2:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
var UDim2_Scales_X = new List<float>();
|
|
|
|
|
var UDim2_Scales_Y = new List<float>();
|
|
|
|
|
|
|
|
|
|
var UDim2_Offsets_X = new List<int>();
|
|
|
|
|
var UDim2_Offsets_Y = new List<int>();
|
|
|
|
|
|
|
|
|
|
props.ForEach(prop =>
|
|
|
|
|
{
|
|
|
|
|
UDim2 value = prop.CastValue<UDim2>();
|
|
|
|
|
|
|
|
|
|
UDim2_Scales_X.Add(value.X.Scale);
|
|
|
|
|
UDim2_Scales_Y.Add(value.Y.Scale);
|
|
|
|
|
|
|
|
|
|
UDim2_Offsets_X.Add(value.X.Offset);
|
|
|
|
|
UDim2_Offsets_Y.Add(value.Y.Offset);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
writer.WriteFloats(UDim2_Scales_X);
|
|
|
|
|
writer.WriteFloats(UDim2_Scales_Y);
|
|
|
|
|
|
|
|
|
|
writer.WriteInts(UDim2_Offsets_X);
|
|
|
|
|
writer.WriteInts(UDim2_Offsets_Y);
|
|
|
|
|
|
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-06-08 03:43:28 +00:00
|
|
|
|
case PropertyType.Ray:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
props.ForEach(prop =>
|
|
|
|
|
{
|
|
|
|
|
Ray ray = prop.CastValue<Ray>();
|
|
|
|
|
|
|
|
|
|
Vector3 pos = ray.Origin;
|
|
|
|
|
writer.Write(pos.X);
|
|
|
|
|
writer.Write(pos.Y);
|
|
|
|
|
writer.Write(pos.Z);
|
|
|
|
|
|
|
|
|
|
Vector3 dir = ray.Direction;
|
|
|
|
|
writer.Write(dir.X);
|
|
|
|
|
writer.Write(dir.Y);
|
|
|
|
|
writer.Write(dir.Z);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-06-08 03:43:28 +00:00
|
|
|
|
case PropertyType.Faces:
|
|
|
|
|
case PropertyType.Axes:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2020-08-20 07:03:05 +00:00
|
|
|
|
props.ForEach(prop =>
|
|
|
|
|
{
|
|
|
|
|
byte value = prop.CastValue<byte>();
|
|
|
|
|
writer.Write(value);
|
|
|
|
|
});
|
2021-05-01 22:40:09 +00:00
|
|
|
|
|
2019-06-08 03:43:28 +00:00
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-06-08 03:43:28 +00:00
|
|
|
|
case PropertyType.BrickColor:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
|
|
|
|
var brickColorIds = props
|
|
|
|
|
.Select(prop => prop.CastValue<BrickColor>())
|
|
|
|
|
.Select(value => value.Number)
|
|
|
|
|
.ToList();
|
2019-06-08 03:43:28 +00:00
|
|
|
|
|
2021-05-01 22:40:09 +00:00
|
|
|
|
writer.WriteInts(brickColorIds);
|
2019-06-08 03:43:28 +00:00
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-06-08 03:43:28 +00:00
|
|
|
|
case PropertyType.Color3:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
var Color3_R = new List<float>();
|
|
|
|
|
var Color3_G = new List<float>();
|
|
|
|
|
var Color3_B = new List<float>();
|
|
|
|
|
|
|
|
|
|
props.ForEach(prop =>
|
|
|
|
|
{
|
|
|
|
|
Color3 value = prop.CastValue<Color3>();
|
|
|
|
|
Color3_R.Add(value.R);
|
|
|
|
|
Color3_G.Add(value.G);
|
|
|
|
|
Color3_B.Add(value.B);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
writer.WriteFloats(Color3_R);
|
|
|
|
|
writer.WriteFloats(Color3_G);
|
|
|
|
|
writer.WriteFloats(Color3_B);
|
|
|
|
|
|
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-06-08 03:43:28 +00:00
|
|
|
|
case PropertyType.Vector2:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
var Vector2_X = new List<float>();
|
|
|
|
|
var Vector2_Y = new List<float>();
|
|
|
|
|
|
|
|
|
|
props.ForEach(prop =>
|
|
|
|
|
{
|
|
|
|
|
Vector2 value = prop.CastValue<Vector2>();
|
|
|
|
|
Vector2_X.Add(value.X);
|
|
|
|
|
Vector2_Y.Add(value.Y);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
writer.WriteFloats(Vector2_X);
|
|
|
|
|
writer.WriteFloats(Vector2_Y);
|
|
|
|
|
|
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-06-08 03:43:28 +00:00
|
|
|
|
case PropertyType.Vector3:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
var Vector3_X = new List<float>();
|
|
|
|
|
var Vector3_Y = new List<float>();
|
|
|
|
|
var Vector3_Z = new List<float>();
|
|
|
|
|
|
|
|
|
|
props.ForEach(prop =>
|
|
|
|
|
{
|
|
|
|
|
Vector3 value = prop.CastValue<Vector3>();
|
|
|
|
|
Vector3_X.Add(value.X);
|
|
|
|
|
Vector3_Y.Add(value.Y);
|
|
|
|
|
Vector3_Z.Add(value.Z);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
writer.WriteFloats(Vector3_X);
|
|
|
|
|
writer.WriteFloats(Vector3_Y);
|
|
|
|
|
writer.WriteFloats(Vector3_Z);
|
|
|
|
|
|
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-06-08 03:43:28 +00:00
|
|
|
|
case PropertyType.CFrame:
|
|
|
|
|
case PropertyType.Quaternion:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
case PropertyType.OptionalCFrame:
|
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
var CFrame_X = new List<float>();
|
|
|
|
|
var CFrame_Y = new List<float>();
|
|
|
|
|
var CFrame_Z = new List<float>();
|
|
|
|
|
|
2021-05-01 22:40:09 +00:00
|
|
|
|
if (Type == PropertyType.OptionalCFrame)
|
|
|
|
|
writer.Write((byte)PropertyType.CFrame);
|
|
|
|
|
|
2019-06-08 03:43:28 +00:00
|
|
|
|
props.ForEach(prop =>
|
|
|
|
|
{
|
|
|
|
|
CFrame value = null;
|
|
|
|
|
|
2021-02-18 19:15:08 +00:00
|
|
|
|
if (prop.Value is Quaternion q)
|
2019-06-08 03:43:28 +00:00
|
|
|
|
value = q.ToCFrame();
|
|
|
|
|
else
|
|
|
|
|
value = prop.CastValue<CFrame>();
|
2021-05-01 22:40:09 +00:00
|
|
|
|
|
|
|
|
|
if (value == null)
|
|
|
|
|
value = new CFrame();
|
|
|
|
|
|
2019-06-08 03:43:28 +00:00
|
|
|
|
Vector3 pos = value.Position;
|
|
|
|
|
CFrame_X.Add(pos.X);
|
|
|
|
|
CFrame_Y.Add(pos.Y);
|
|
|
|
|
CFrame_Z.Add(pos.Z);
|
|
|
|
|
|
|
|
|
|
int orientId = value.GetOrientId();
|
|
|
|
|
writer.Write((byte)(orientId + 1));
|
|
|
|
|
|
|
|
|
|
if (orientId == -1)
|
|
|
|
|
{
|
|
|
|
|
if (Type == PropertyType.Quaternion)
|
|
|
|
|
{
|
|
|
|
|
Quaternion quat = new Quaternion(value);
|
|
|
|
|
writer.Write(quat.X);
|
|
|
|
|
writer.Write(quat.Y);
|
|
|
|
|
writer.Write(quat.Z);
|
|
|
|
|
writer.Write(quat.W);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
float[] components = value.GetComponents();
|
|
|
|
|
|
|
|
|
|
for (int i = 3; i < 12; i++)
|
|
|
|
|
{
|
|
|
|
|
float component = components[i];
|
|
|
|
|
writer.Write(component);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
writer.WriteFloats(CFrame_X);
|
|
|
|
|
writer.WriteFloats(CFrame_Y);
|
|
|
|
|
writer.WriteFloats(CFrame_Z);
|
|
|
|
|
|
2021-05-01 22:40:09 +00:00
|
|
|
|
if (Type == PropertyType.OptionalCFrame)
|
|
|
|
|
{
|
|
|
|
|
writer.Write((byte)PropertyType.Bool);
|
|
|
|
|
|
|
|
|
|
props.ForEach(prop =>
|
|
|
|
|
{
|
|
|
|
|
if (prop.Value is null)
|
|
|
|
|
{
|
|
|
|
|
writer.Write(false);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-05 18:06:20 +00:00
|
|
|
|
if (prop.Value is Optional<CFrame> optional)
|
|
|
|
|
{
|
|
|
|
|
writer.Write(optional.HasValue);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var cf = prop.Value as CFrame;
|
|
|
|
|
writer.Write(cf != null);
|
2021-05-01 22:40:09 +00:00
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-08 03:43:28 +00:00
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-06-08 03:43:28 +00:00
|
|
|
|
case PropertyType.Enum:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
|
|
|
|
var enums = new List<uint>();
|
2019-06-08 03:43:28 +00:00
|
|
|
|
|
|
|
|
|
props.ForEach(prop =>
|
|
|
|
|
{
|
2021-02-18 19:15:08 +00:00
|
|
|
|
if (prop.Value is uint raw)
|
2019-06-30 22:01:19 +00:00
|
|
|
|
{
|
2021-05-01 22:40:09 +00:00
|
|
|
|
enums.Add(raw);
|
2019-06-30 22:01:19 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int signed = (int)prop.Value;
|
|
|
|
|
uint value = (uint)signed;
|
2021-02-18 19:15:08 +00:00
|
|
|
|
|
2021-05-01 22:40:09 +00:00
|
|
|
|
enums.Add(value);
|
2019-06-08 03:43:28 +00:00
|
|
|
|
});
|
|
|
|
|
|
2021-05-01 22:40:09 +00:00
|
|
|
|
writer.WriteInterleaved(enums);
|
2019-06-08 03:43:28 +00:00
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-06-08 03:43:28 +00:00
|
|
|
|
case PropertyType.Ref:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
var InstanceIds = new List<int>();
|
|
|
|
|
|
|
|
|
|
props.ForEach(prop =>
|
|
|
|
|
{
|
|
|
|
|
int referent = -1;
|
|
|
|
|
|
|
|
|
|
if (prop.Value != null)
|
|
|
|
|
{
|
|
|
|
|
Instance value = prop.CastValue<Instance>();
|
2021-05-05 00:45:00 +00:00
|
|
|
|
|
|
|
|
|
if (value.IsDescendantOf(File))
|
|
|
|
|
{
|
|
|
|
|
string refValue = value.Referent;
|
|
|
|
|
int.TryParse(refValue, out referent);
|
|
|
|
|
}
|
2019-06-08 03:43:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
InstanceIds.Add(referent);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
writer.WriteInstanceIds(InstanceIds);
|
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-06-08 03:43:28 +00:00
|
|
|
|
case PropertyType.Vector3int16:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
props.ForEach(prop =>
|
|
|
|
|
{
|
|
|
|
|
Vector3int16 value = prop.CastValue<Vector3int16>();
|
|
|
|
|
writer.Write(value.X);
|
|
|
|
|
writer.Write(value.Y);
|
|
|
|
|
writer.Write(value.Z);
|
|
|
|
|
});
|
2021-05-01 22:40:09 +00:00
|
|
|
|
|
2019-06-08 03:43:28 +00:00
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-06-08 03:43:28 +00:00
|
|
|
|
case PropertyType.NumberSequence:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
props.ForEach(prop =>
|
|
|
|
|
{
|
|
|
|
|
NumberSequence value = prop.CastValue<NumberSequence>();
|
|
|
|
|
|
|
|
|
|
var keyPoints = value.Keypoints;
|
|
|
|
|
writer.Write(keyPoints.Length);
|
|
|
|
|
|
|
|
|
|
foreach (var keyPoint in keyPoints)
|
|
|
|
|
{
|
|
|
|
|
writer.Write(keyPoint.Time);
|
|
|
|
|
writer.Write(keyPoint.Value);
|
|
|
|
|
writer.Write(keyPoint.Envelope);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-06-08 03:43:28 +00:00
|
|
|
|
case PropertyType.ColorSequence:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
props.ForEach(prop =>
|
|
|
|
|
{
|
|
|
|
|
ColorSequence value = prop.CastValue<ColorSequence>();
|
|
|
|
|
|
|
|
|
|
var keyPoints = value.Keypoints;
|
|
|
|
|
writer.Write(keyPoints.Length);
|
|
|
|
|
|
|
|
|
|
foreach (var keyPoint in keyPoints)
|
|
|
|
|
{
|
|
|
|
|
Color3 color = keyPoint.Value;
|
|
|
|
|
writer.Write(keyPoint.Time);
|
|
|
|
|
|
|
|
|
|
writer.Write(color.R);
|
|
|
|
|
writer.Write(color.G);
|
|
|
|
|
writer.Write(color.B);
|
|
|
|
|
|
2019-06-30 22:01:19 +00:00
|
|
|
|
writer.Write(keyPoint.Envelope);
|
2019-06-08 03:43:28 +00:00
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-06-08 03:43:28 +00:00
|
|
|
|
case PropertyType.NumberRange:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
props.ForEach(prop =>
|
|
|
|
|
{
|
|
|
|
|
NumberRange value = prop.CastValue<NumberRange>();
|
|
|
|
|
writer.Write(value.Min);
|
|
|
|
|
writer.Write(value.Max);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-06-08 03:43:28 +00:00
|
|
|
|
case PropertyType.Rect:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
var Rect_X0 = new List<float>();
|
|
|
|
|
var Rect_Y0 = new List<float>();
|
|
|
|
|
|
|
|
|
|
var Rect_X1 = new List<float>();
|
|
|
|
|
var Rect_Y1 = new List<float>();
|
|
|
|
|
|
|
|
|
|
props.ForEach(prop =>
|
|
|
|
|
{
|
|
|
|
|
Rect value = prop.CastValue<Rect>();
|
|
|
|
|
|
|
|
|
|
Vector2 min = value.Min;
|
|
|
|
|
Rect_X0.Add(min.X);
|
|
|
|
|
Rect_Y0.Add(min.Y);
|
|
|
|
|
|
|
|
|
|
Vector2 max = value.Max;
|
|
|
|
|
Rect_X1.Add(max.X);
|
|
|
|
|
Rect_Y1.Add(max.Y);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
writer.WriteFloats(Rect_X0);
|
|
|
|
|
writer.WriteFloats(Rect_Y0);
|
|
|
|
|
|
|
|
|
|
writer.WriteFloats(Rect_X1);
|
|
|
|
|
writer.WriteFloats(Rect_Y1);
|
2021-05-01 22:40:09 +00:00
|
|
|
|
|
2019-06-08 03:43:28 +00:00
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-06-08 03:43:28 +00:00
|
|
|
|
case PropertyType.PhysicalProperties:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
props.ForEach(prop =>
|
|
|
|
|
{
|
|
|
|
|
bool custom = (prop.Value != null);
|
|
|
|
|
writer.Write(custom);
|
|
|
|
|
|
|
|
|
|
if (custom)
|
|
|
|
|
{
|
|
|
|
|
PhysicalProperties value = prop.CastValue<PhysicalProperties>();
|
|
|
|
|
|
|
|
|
|
writer.Write(value.Density);
|
|
|
|
|
writer.Write(value.Friction);
|
|
|
|
|
writer.Write(value.Elasticity);
|
|
|
|
|
|
|
|
|
|
writer.Write(value.FrictionWeight);
|
|
|
|
|
writer.Write(value.ElasticityWeight);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-06-08 03:43:28 +00:00
|
|
|
|
case PropertyType.Color3uint8:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
var Color3uint8_R = new List<byte>();
|
|
|
|
|
var Color3uint8_G = new List<byte>();
|
|
|
|
|
var Color3uint8_B = new List<byte>();
|
|
|
|
|
|
|
|
|
|
props.ForEach(prop =>
|
|
|
|
|
{
|
2019-06-30 22:01:19 +00:00
|
|
|
|
Color3uint8 value = prop.CastValue<Color3uint8>();
|
|
|
|
|
Color3uint8_R.Add(value.R);
|
|
|
|
|
Color3uint8_G.Add(value.G);
|
|
|
|
|
Color3uint8_B.Add(value.B);
|
|
|
|
|
});
|
2019-06-08 03:43:28 +00:00
|
|
|
|
|
2019-06-30 22:01:19 +00:00
|
|
|
|
byte[] rBuffer = Color3uint8_R.ToArray();
|
|
|
|
|
writer.Write(rBuffer);
|
2019-06-08 03:43:28 +00:00
|
|
|
|
|
2019-06-30 22:01:19 +00:00
|
|
|
|
byte[] gBuffer = Color3uint8_G.ToArray();
|
|
|
|
|
writer.Write(gBuffer);
|
2019-06-08 03:43:28 +00:00
|
|
|
|
|
2019-06-30 22:01:19 +00:00
|
|
|
|
byte[] bBuffer = Color3uint8_B.ToArray();
|
|
|
|
|
writer.Write(bBuffer);
|
2019-06-08 03:43:28 +00:00
|
|
|
|
|
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-06-08 03:43:28 +00:00
|
|
|
|
case PropertyType.Int64:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
|
|
|
|
var longs = new List<long>();
|
2019-06-08 03:43:28 +00:00
|
|
|
|
|
|
|
|
|
props.ForEach(prop =>
|
|
|
|
|
{
|
|
|
|
|
long value = prop.CastValue<long>();
|
2021-05-01 22:40:09 +00:00
|
|
|
|
longs.Add(value);
|
2019-06-08 03:43:28 +00:00
|
|
|
|
});
|
|
|
|
|
|
2022-10-13 02:19:43 +00:00
|
|
|
|
writer.WriteLongs(longs);
|
2019-06-08 03:43:28 +00:00
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2019-06-08 03:43:28 +00:00
|
|
|
|
case PropertyType.SharedString:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2019-06-08 03:43:28 +00:00
|
|
|
|
var sharedKeys = new List<uint>();
|
|
|
|
|
SSTR sstr = file.SSTR;
|
|
|
|
|
|
|
|
|
|
if (sstr == null)
|
|
|
|
|
{
|
|
|
|
|
sstr = new SSTR();
|
|
|
|
|
file.SSTR = sstr;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
props.ForEach(prop =>
|
|
|
|
|
{
|
2020-07-13 01:19:30 +00:00
|
|
|
|
var shared = prop.CastValue<SharedString>();
|
2020-08-20 07:03:05 +00:00
|
|
|
|
|
|
|
|
|
if (shared == null)
|
|
|
|
|
{
|
|
|
|
|
byte[] empty = Array.Empty<byte>();
|
|
|
|
|
shared = SharedString.FromBuffer(empty);
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-13 01:19:30 +00:00
|
|
|
|
string key = shared.Key;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
|
2019-06-30 22:01:19 +00:00
|
|
|
|
if (!sstr.Lookup.ContainsKey(key))
|
2019-06-08 03:43:28 +00:00
|
|
|
|
{
|
2021-05-01 22:40:09 +00:00
|
|
|
|
uint id = (uint)sstr.Lookup.Count;
|
2019-06-30 22:01:19 +00:00
|
|
|
|
sstr.Strings.Add(id, shared);
|
|
|
|
|
sstr.Lookup.Add(key, id);
|
2019-06-08 03:43:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
2019-06-30 22:01:19 +00:00
|
|
|
|
uint hashId = sstr.Lookup[key];
|
|
|
|
|
sharedKeys.Add(hashId);
|
2019-06-08 03:43:28 +00:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
writer.WriteInterleaved(sharedKeys);
|
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2020-07-13 01:19:30 +00:00
|
|
|
|
case PropertyType.ProtectedString:
|
2021-05-01 22:40:09 +00:00
|
|
|
|
{
|
2020-07-13 01:19:30 +00:00
|
|
|
|
props.ForEach(prop =>
|
|
|
|
|
{
|
|
|
|
|
var protect = prop.CastValue<ProtectedString>();
|
|
|
|
|
byte[] buffer = protect.RawBuffer;
|
|
|
|
|
|
|
|
|
|
writer.Write(buffer.Length);
|
|
|
|
|
writer.Write(buffer);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
break;
|
2021-05-01 22:40:09 +00:00
|
|
|
|
}
|
2022-02-10 21:22:44 +00:00
|
|
|
|
case PropertyType.UniqueId:
|
|
|
|
|
{
|
2022-10-13 02:19:43 +00:00
|
|
|
|
var uniqueIds = new List<UniqueId>();
|
|
|
|
|
|
2022-02-10 21:22:44 +00:00
|
|
|
|
props.ForEach(prop =>
|
|
|
|
|
{
|
2022-04-25 03:28:42 +00:00
|
|
|
|
var uniqueId = prop.CastValue<UniqueId>();
|
2022-10-13 02:19:43 +00:00
|
|
|
|
var rotated = writer.RotateLong(uniqueId.Random);
|
|
|
|
|
uniqueIds.Add(new UniqueId(rotated, uniqueId.Time, uniqueId.Index));
|
2022-02-10 21:22:44 +00:00
|
|
|
|
});
|
|
|
|
|
|
2022-10-13 02:19:43 +00:00
|
|
|
|
writer.WriteInterleaved(uniqueIds);
|
2022-02-10 21:22:44 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
2022-04-15 01:52:05 +00:00
|
|
|
|
case PropertyType.FontFace:
|
|
|
|
|
{
|
|
|
|
|
props.ForEach(prop =>
|
|
|
|
|
{
|
|
|
|
|
var font = prop.CastValue<FontFace>();
|
|
|
|
|
|
|
|
|
|
string family = font.Family;
|
2022-10-13 02:19:43 +00:00
|
|
|
|
writer.WriteString(family);
|
2022-04-15 01:52:05 +00:00
|
|
|
|
|
|
|
|
|
var weight = (ushort)font.Weight;
|
|
|
|
|
writer.Write(weight);
|
|
|
|
|
|
|
|
|
|
var style = (byte)font.Style;
|
|
|
|
|
writer.Write(style);
|
2022-10-13 02:19:43 +00:00
|
|
|
|
|
|
|
|
|
var cachedFaceId = font.CachedFaceId;
|
|
|
|
|
writer.WriteString(cachedFaceId);
|
2022-04-15 01:52:05 +00:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
2021-05-01 22:40:09 +00:00
|
|
|
|
default:
|
|
|
|
|
{
|
|
|
|
|
RobloxFile.LogError($"Unhandled property type: {Type} in {this}!");
|
|
|
|
|
break;
|
|
|
|
|
}
|
2019-06-08 03:43:28 +00:00
|
|
|
|
}
|
2019-01-26 00:39:37 +00:00
|
|
|
|
}
|
2020-09-10 05:08:12 +00:00
|
|
|
|
|
|
|
|
|
public void WriteInfo(StringBuilder builder)
|
|
|
|
|
{
|
|
|
|
|
builder.AppendLine($"- Name: {Name}");
|
|
|
|
|
builder.AppendLine($"- Type: {Type}");
|
|
|
|
|
builder.AppendLine($"- TypeId: {TypeId}");
|
|
|
|
|
builder.AppendLine($"- ClassName: {ClassName}");
|
|
|
|
|
builder.AppendLine($"- ClassIndex: {ClassIndex}");
|
|
|
|
|
|
|
|
|
|
builder.AppendLine($"| InstanceId | Value |");
|
|
|
|
|
builder.AppendLine($"|-----------:|---------------------------|");
|
|
|
|
|
|
|
|
|
|
INST inst = File.Classes[ClassIndex];
|
|
|
|
|
|
|
|
|
|
foreach (var instId in inst.InstanceIds)
|
|
|
|
|
{
|
|
|
|
|
Instance instance = File.Instances[instId];
|
|
|
|
|
Property prop = instance?.GetProperty(Name);
|
|
|
|
|
|
|
|
|
|
object value = prop?.Value;
|
|
|
|
|
string str = value?.ToInvariantString() ?? "null";
|
|
|
|
|
|
2021-02-18 19:15:08 +00:00
|
|
|
|
if (value is byte[] buffer)
|
|
|
|
|
str = Convert.ToBase64String(buffer);
|
2020-09-10 05:08:12 +00:00
|
|
|
|
|
|
|
|
|
if (str.Length > 25)
|
|
|
|
|
str = str.Substring(0, 22) + "...";
|
|
|
|
|
|
|
|
|
|
str = str.Replace('\r', ' ');
|
|
|
|
|
str = str.Replace('\n', ' ');
|
|
|
|
|
|
|
|
|
|
string row = string.Format("| {0, 10} | {1, -25} |", instId, str);
|
|
|
|
|
builder.AppendLine(row);
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-01-26 00:39:37 +00:00
|
|
|
|
}
|
2019-01-29 10:25:33 +00:00
|
|
|
|
}
|