public void Save(BinaryRobloxFileWriter writer) { var file = writer.File; File = file; var postInstances = writer.PostInstances; var idCount = postInstances.Count; var childIds = new List <int>(); var parentIds = new List <int>(); foreach (Instance inst in writer.PostInstances) { Instance parent = inst.Parent; int childId = int.Parse(inst.Referent); int parentId = -1; if (parent != null) { parentId = int.Parse(parent.Referent); } childIds.Add(childId); parentIds.Add(parentId); } writer.Write(FORMAT); writer.Write(idCount); writer.WriteInstanceIds(childIds); writer.WriteInstanceIds(parentIds); }
public void Save(BinaryRobloxFileWriter writer) { writer.Write(Signatures.Length); for (int i = 0; i < Signatures.Length; i++) { var signature = Signatures[i]; signature.Length = signature.Data.Length; writer.Write(signature.Version); writer.Write(signature.Id); writer.Write(signature.Length); writer.Write(signature.Data); } }
public void Save(BinaryRobloxFileWriter writer) { writer.Write(Data.Count); foreach (var pair in Data) { writer.WriteString(pair.Key); writer.WriteString(pair.Value); } }
public void Save(BinaryRobloxFileWriter writer) { writer.Write(FORMAT); writer.Write(Lookup.Count); foreach (var pair in Lookup) { string key = pair.Key; byte[] hash = Convert.FromBase64String(key); writer.Write(hash); SharedString value = Strings[pair.Value]; byte[] buffer = SharedString.Find(value.Key); writer.Write(buffer.Length); writer.Write(buffer); } }
public void Save(BinaryRobloxFileWriter writer) { writer.Write(ClassIndex); writer.WriteString(ClassName); writer.Write(IsService); writer.Write(NumInstances); writer.WriteInstanceIds(InstanceIds); if (IsService) { var file = writer.File; foreach (int instId in InstanceIds) { Instance service = file.Instances[instId]; writer.Write(service.Parent == file); } } }
internal void WriteValue <T>() where T : struct { if (CurrentWriter == null) { throw new Exception("Property.CurrentWriter must be set to use WriteValue<T>"); } T value = CastValue <T>(); byte[] bytes = BinaryRobloxFileWriter.GetBytes(value); CurrentWriter.Write(bytes); }
public void Save(BinaryRobloxFileWriter writer) { BinaryRobloxFile file = writer.File; File = file; INST inst = file.Classes[ClassIndex]; var props = new List <Property>(); foreach (int instId in inst.InstanceIds) { Instance instance = file.Instances[instId]; var instProps = instance.Properties; if (!instProps.TryGetValue(Name, out Property prop)) { 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); } writer.Write(ClassIndex); writer.WriteString(Name); writer.Write(TypeId); switch (Type) { case PropertyType.String: props.ForEach(prop => { 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); }); break; case PropertyType.Bool: { props.ForEach(prop => { bool value = prop.CastValue <bool>(); writer.Write(value); }); break; } case PropertyType.Int: { var ints = props .Select(prop => prop.CastValue <int>()) .ToList(); writer.WriteInts(ints); break; } case PropertyType.Float: { var floats = props .Select(prop => prop.CastValue <float>()) .ToList(); writer.WriteFloats(floats); break; } case PropertyType.Double: { props.ForEach(prop => { double value = prop.CastValue <double>(); writer.Write(BinaryRobloxFileWriter.GetBytes(value)); }); break; } case PropertyType.UDim: { 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; } case PropertyType.UDim2: { 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; } case PropertyType.Ray: { 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; } case PropertyType.Faces: case PropertyType.Axes: { props.ForEach(prop => { byte value = prop.CastValue <byte>(); writer.Write(value); }); break; } case PropertyType.BrickColor: { var brickColorIds = props .Select(prop => prop.CastValue <BrickColor>()) .Select(value => value.Number) .ToList(); writer.WriteInts(brickColorIds); break; } case PropertyType.Color3: { 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; } case PropertyType.Vector2: { 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; } case PropertyType.Vector3: { 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; } case PropertyType.CFrame: case PropertyType.Quaternion: case PropertyType.OptionalCFrame: { var CFrame_X = new List <float>(); var CFrame_Y = new List <float>(); var CFrame_Z = new List <float>(); if (Type == PropertyType.OptionalCFrame) { writer.Write((byte)PropertyType.CFrame); } props.ForEach(prop => { CFrame value = null; if (prop.Value is Quaternion q) { value = q.ToCFrame(); } else { value = prop.CastValue <CFrame>(); } if (value == null) { value = new CFrame(); } 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); if (Type == PropertyType.OptionalCFrame) { writer.Write((byte)PropertyType.Bool); props.ForEach(prop => { if (prop.Value is null) { writer.Write(false); return; } if (prop.Value is Optional <CFrame> optional) { writer.Write(optional.HasValue); return; } var cf = prop.Value as CFrame; writer.Write(cf != null); }); } break; } case PropertyType.Enum: { var enums = new List <uint>(); props.ForEach(prop => { if (prop.Value is uint raw) { enums.Add(raw); return; } int signed = (int)prop.Value; uint value = (uint)signed; enums.Add(value); }); writer.WriteInterleaved(enums); break; } case PropertyType.Ref: { var InstanceIds = new List <int>(); props.ForEach(prop => { int referent = -1; if (prop.Value != null) { Instance value = prop.CastValue <Instance>(); if (value.IsDescendantOf(File)) { string refValue = value.Referent; int.TryParse(refValue, out referent); } } InstanceIds.Add(referent); }); writer.WriteInstanceIds(InstanceIds); break; } case PropertyType.Vector3int16: { props.ForEach(prop => { Vector3int16 value = prop.CastValue <Vector3int16>(); writer.Write(value.X); writer.Write(value.Y); writer.Write(value.Z); }); break; } case PropertyType.NumberSequence: { 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; } case PropertyType.ColorSequence: { 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); writer.Write(keyPoint.Envelope); } }); break; } case PropertyType.NumberRange: { props.ForEach(prop => { NumberRange value = prop.CastValue <NumberRange>(); writer.Write(value.Min); writer.Write(value.Max); }); break; } case PropertyType.Rect: { 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); break; } case PropertyType.PhysicalProperties: { 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; } case PropertyType.Color3uint8: { var Color3uint8_R = new List <byte>(); var Color3uint8_G = new List <byte>(); var Color3uint8_B = new List <byte>(); props.ForEach(prop => { Color3uint8 value = prop.CastValue <Color3uint8>(); Color3uint8_R.Add(value.R); Color3uint8_G.Add(value.G); Color3uint8_B.Add(value.B); }); byte[] rBuffer = Color3uint8_R.ToArray(); writer.Write(rBuffer); byte[] gBuffer = Color3uint8_G.ToArray(); writer.Write(gBuffer); byte[] bBuffer = Color3uint8_B.ToArray(); writer.Write(bBuffer); break; } case PropertyType.Int64: { var longs = new List <long>(); props.ForEach(prop => { long value = prop.CastValue <long>(); longs.Add(value); }); writer.WriteInterleaved(longs, value => { // Move the sign bit to the front. return((value << 1) ^ (value >> 63)); }); break; } case PropertyType.SharedString: { var sharedKeys = new List <uint>(); SSTR sstr = file.SSTR; if (sstr == null) { sstr = new SSTR(); file.SSTR = sstr; } props.ForEach(prop => { var shared = prop.CastValue <SharedString>(); if (shared == null) { byte[] empty = Array.Empty <byte>(); shared = SharedString.FromBuffer(empty); } string key = shared.Key; if (!sstr.Lookup.ContainsKey(key)) { uint id = (uint)sstr.Lookup.Count; sstr.Strings.Add(id, shared); sstr.Lookup.Add(key, id); } uint hashId = sstr.Lookup[key]; sharedKeys.Add(hashId); }); writer.WriteInterleaved(sharedKeys); break; } case PropertyType.ProtectedString: { props.ForEach(prop => { var protect = prop.CastValue <ProtectedString>(); byte[] buffer = protect.RawBuffer; writer.Write(buffer.Length); writer.Write(buffer); }); break; } case PropertyType.UniqueId: { props.ForEach(prop => { var guid = prop.CastValue <Guid>(); byte[] buffer = guid.ToByteArray(); writer.Write(buffer); }); break; } default: { RobloxFile.LogError($"Unhandled property type: {Type} in {this}!"); break; } } }