public override void Save(Stream fileStream) { var writer = new BINAWriter(fileStream, Header); char[] WTXTMagic = { 'W', 'T', 'X', 'T' }; writer.Write(WTXTMagic); writer.AddString($"mstName", $"{Name}"); writer.Write(entries.Count); for (int i = 0; i < entries.Count; i++) { writer.AddString($"nameOffset{i}", $"{entries[i].Name}"); writer.AddOffset($"textOffset{i}"); writer.AddOffset($"placeholderOffset{i}"); } for (int i = 0; i < entries.Count; i++) { writer.FillInOffset($"textOffset{i}", false); writer.WriteNullTerminatedStringUTF16(entries[i].Text); } for (int i = 0; i < entries.Count; i++) { writer.FillInOffset($"placeholderOffset{i}", false); writer.WriteNullTerminatedString(entries[i].Placeholder); } writer.FinishWrite(Header); }
public override void Save(Stream fileStream) { var writer = new BINAWriter(fileStream, Header); writer.AddOffset("VertexCountOffsetOffset"); writer.Write(0); //PostFaceOffset (Not needed???) writer.FillInOffset("VertexCountOffsetOffset", false); writer.AddOffset("VertexCountOffset"); writer.AddOffset("FaceCountOffset"); writer.FillInOffset("VertexCountOffset", false); writer.Write(Vertices.Count); for (int i = 0; i < Vertices.Count; i++) { writer.Write(Vertices[i]); } writer.FillInOffset("FaceCountOffset", false); writer.Write(Faces.Count); for (int i = 0; i < Faces.Count; i++) { writer.Write(Faces[i].Vertex1); writer.Write(Faces[i].Vertex2); writer.Write(Faces[i].Vertex3); writer.WriteNulls(2); writer.Write(Faces[i].Flags); } writer.FinishWrite(Header); }
public override void Save(Stream fileStream) { // Header var writer = new BINAWriter(fileStream, Header); writer.AddString("gismOffset", Signature); writer.Write(UnknownBoolean1); writer.Write((uint)Gismos.Length); // Containers writer.AddOffset("containerOffset"); writer.FillInOffset("containerOffset", false); // Container 1 for (int i = 0; i < Gismos.Length; ++i) { var gismo = Gismos[i]; writer.AddString($"fileNameOffset_{i}", gismo.FileName); writer.AddString($"fileNameOffset2_{i}", gismo.FileName); writer.AddString($"unknownOffset1_{i}", gismo.FileName); // TODO writer.Write(gismo.Unknown1); writer.Write(gismo.Unknown2); writer.Write(gismo.Unknown3); writer.Write((gismo.DoesAnimate) ? 1u : 0u); writer.AddOffset($"havokOffset_{i}"); writer.Write((gismo.UnknownBoolean1) ? 1u : 0u); writer.AddOffset($"containerTwoOffset_{i}"); } // Havok Array for (int i = 0; i < Gismos.Length; ++i) { writer.FillInOffset($"havokOffset_{i}", false); writer.WriteNulls(4); // TODO: Figure out what this is writer.AddString($"havokNameOffset_{i}", Gismos[i].HavokName); } // Container 2 for (int i = 0; i < Gismos.Length; ++i) { var gismo = Gismos[i]; writer.FillInOffset($"containerTwoOffset_{i}", false); writer.Write((gismo.UnknownBoolean2) ? 1u : 0u); writer.Write((gismo.UnknownBoolean3) ? 1u : 0u); writer.Write(gismo.Unknown5); writer.Write(gismo.Unknown6); writer.Write(gismo.Unknown7); writer.Write(gismo.Unknown8); writer.Write(gismo.RotationAmount); writer.Write(gismo.Unknown9); } writer.FinishWrite(Header); }
public void Write(BINAWriter writer, string prefix) { writer.Write(Nodes.Count); writer.Write(DataNodeIndices.Count); writer.AddOffset($"{prefix}nodesOffset", 8); writer.AddOffset($"{prefix}dataNodeIndicesOffset", 8); // Nodes writer.FillInOffsetLong($"{prefix}nodesOffset", true, false); foreach (var node in Nodes) { node.Write(writer, this, prefix); } }
public override void Save(Stream fileStream) { // Header var header = new BINAv1Header(); var writer = new BINAWriter(fileStream, header); writer.WriteSignature(Signature); // Texture writer.AddString("textureName", Texture); // Placeholders writer.Write((uint)Entries.Count); writer.AddOffset("placeholderEntriesPos"); writer.FillInOffset("placeholderEntriesPos", false); for (int i = 0; i < Entries.Count; i++) { writer.AddString($"placeholderName{i}", Entries[i].Placeholder); writer.Write(Entries[i].X); writer.Write(Entries[i].Y); writer.Write(Entries[i].Width); writer.Write(Entries[i].Height); } writer.FinishWrite(header); }
public override void Save(Stream fileStream) { // Header var header = new BINAv1Header(); var writer = new BINAWriter(fileStream, header); uint filesCount = 0; for (int i = 0; i < Types.Count; i++) { for (int c = 0; c < Types[i].Files.Count; c++) { filesCount++; } } writer.Write(filesCount); writer.AddOffset("fileEntriesPos"); writer.Write(Types.Count); writer.AddOffset("typeEntriesPos"); writer.FillInOffset("typeEntriesPos", false); for (int i = 0; i < Types.Count; i++) { writer.AddString($"typeName{i}", Types[i].TypeName); writer.Write(Types[i].Files.Count); writer.AddOffset($"typeFilesOffset{i}"); } writer.FillInOffset("fileEntriesPos", false); int objectNum = 0; for (int i = 0; i < Types.Count; i++) { writer.FillInOffset($"typeFilesOffset{i}", false); for (int f = 0; f < Types[i].Files.Count; f++) { writer.AddString($"friendlyName{objectNum}", Types[i].Files[f].FriendlyName); writer.AddString($"filePath{objectNum}", Types[i].Files[f].FilePath); objectNum++; } } writer.FinishWrite(header); }
public override void Save(Stream fileStream) { // Header var header = new BINAv1Header(); var writer = new BINAWriter(fileStream, header); writer.Write((uint)Files.Count); writer.AddOffset("fileEntriesPos"); writer.Write((uint)Types.Count); writer.AddOffset("typeEntriesPos"); // Types writer.FillInOffset("typeEntriesPos", false); for (int i = 0; i < Types.Count; i++) { writer.AddString($"typeName{i}", Types[i].TypeName); writer.Write(Types[i].FileCount); writer.AddOffset($"typeFilesOffset{i}"); } // Files uint fileEntriesPos = (uint)writer.BaseStream.Position; writer.FillInOffset("fileEntriesPos", false); for (int i = 0; i < Files.Count; i++) { writer.AddString($"friendlyName{i}", Files[i].FriendlyName); writer.AddString($"filePath{i}", Files[i].FilePath); } // Fill-in type file offsets for (int i = 0; i < Types.Count; i++) { writer.FillInOffset($"typeFilesOffset{i}", (fileEntriesPos + (Types[i].FileStartIndex * 8U)), false, false); } writer.FinishWrite(header); }
public void Write(BINAWriter writer, string extension, uint id, int index) { writer.Write(id); writer.Write((ulong)Data.Length); writer.Write(0U); if (DataType == DataEntryTypes.NotHere) { writer.Write(0UL); } else { writer.AddOffset($"{extension}fileDataOffset{index}", 8); } writer.Write(0UL); writer.AddString($"{extension}extDataOffset{index}", extension, 8); writer.Write((ulong)DataType); }
public override void Save(Stream fileStream) { // Header var writer = new BINAWriter(fileStream, Header); writer.WriteSignature(Signature); writer.Write(1u); // TODO: Figure out what this value is. writer.Write(SoundEntries.Length); writer.AddOffset("soundEntriesOffset"); // Data writer.FillInOffset("soundEntriesOffset", false); for (uint i = 0; i < SoundEntries.Length; ++i) { writer.Write(i); writer.AddString($"soundEntry_{i}", SoundEntries[i]); } writer.FinishWrite(Header); }
public override void Save(Stream fileStream) { //The file this produces needs the footer hex edited in some way to correct it (usually changing it to DBVBVB etc etc) var writer = new BINAWriter(fileStream, Header); writer.WriteSignature(Signature); writer.Write(1); //Unknown 1 writer.Write(Convert.ToInt64(SvShapes.Count)); writer.Write(24L); //Unknown 2 for (int i = 0; i < SvShapes.Count; i++) { writer.AddString($"ShapeName{i}", SvShapes[i].Name, 8u); writer.Write(SvShapes[i].Unknown1); writer.Write(SvShapes[i].Size); writer.Write(SvShapes[i].Position); writer.Write(SvShapes[i].Rotation); writer.Write(SvShapes[i].BoundingBox.Minimum); writer.Write(SvShapes[i].BoundingBox.Maximum); writer.Write(SvShapes[i].Unknown2); writer.Write(Convert.ToInt64(SvShapes[i].Sectors.Count)); writer.AddOffset($"SectorsOffset{i}", 8u); } for (int i = 0; i < SvShapes.Count; i++) { writer.FillInOffset($"SectorsOffset{i}", false); for (int s = 0; s < SvShapes[i].Sectors.Count; s++) { writer.Write(Convert.ToByte(SvShapes[i].Sectors[s].SectorIndex)); writer.Write(SvShapes[i].Sectors[s].Visible); } } writer.WriteNulls(0x30); writer.FinishWrite(Header); }
public override void Save(Stream fileStream) { // Header var writer = new BINAWriter(fileStream, Header); writer.Write(0UL); writer.Write(0UL); writer.AddOffset("objectTableOffset", 8); writer.Write((ulong)Objects.Count); writer.Write((ulong)Objects.Count); writer.Write(0UL); // Objects writer.FillInOffsetLong("objectTableOffset", false, false); writer.AddOffsetTable("objectOffset", (uint)Objects.Count, 8); writer.FixPadding(16); for (int i = 0; i < Objects.Count; ++i) { var obj = Objects[i]; writer.FillInOffsetLong($"objectOffset_{i}", false, false); WriteObject(writer, obj, i); } writer.FixPadding(16); // Object Parameters for (int i = 0; i < Objects.Count; ++i) { writer.FixPadding(16); WriteObjectParameters(writer, Objects[i], i); } writer.FinishWrite(Header); }
public override void Save(Stream fileStream) { var writer = new BINAWriter(fileStream, Header); var typeCounts = new Dictionary <string, int>(); var groupNameCounts = new Dictionary <string, int>(); var groupTypeCounts = new Dictionary <string, int>(); uint stringParamCount = 0; // Header writer.WriteNulls(0xC); writer.WriteNullTerminatedString("test"); writer.WriteNulls(0x1B); writer.Write(Objects.Count); writer.AddOffset("objectOffset"); // TODO: Write group count writer.Write(groupNames.Count); writer.AddOffset("groupOffset"); // Data writer.FillInOffset("objectOffset", false); for (int i = 0; i < Objects.Count; ++i) { WriteObject(i); } for (int i = 0; i < Objects.Count; ++i) { WriteObjectParams(i); } // Write Groups writer.FillInOffset("groupOffset", false); for (int i = 0; i < groupNameCounts.Count; ++i) { WriteGroup(i); } for (int i = 0; i < groupNameCounts.Count; ++i) { WriteGroupCounts(i); } // Write Footer writer.FinishWrite(Header); // Sub-Methods void WriteObject(int id) { // Object Entry var obj = Objects[id]; string type = obj.ObjectType; if (!typeCounts.ContainsKey(type)) { typeCounts.Add(type, 1); } else { ++typeCounts[type]; } if (obj.CustomData["GroupName"].Data.ToString() != string.Empty) { if (!groupNameCounts.ContainsKey(obj.CustomData["GroupName"].Data.ToString())) { groupNameCounts.Add(obj.CustomData["GroupName"].Data.ToString(), 1); } else { ++groupNameCounts[obj.CustomData["GroupName"].Data.ToString()]; } Console.WriteLine(obj.CustomData["GroupName"].Data); } if (obj.CustomData["GroupType"].Data.ToString() != string.Empty) { if (!groupTypeCounts.ContainsKey(obj.CustomData["GroupType"].Data.ToString())) { groupTypeCounts.Add(obj.CustomData["GroupType"].Data.ToString(), 1); } else { ++groupTypeCounts[obj.CustomData["GroupType"].Data.ToString()]; } Console.WriteLine(obj.CustomData["GroupType"].Data); } string name = ""; if (obj.CustomData.ContainsKey("Name")) { name = (obj.CustomData["Name"].Data as string); } if (string.IsNullOrEmpty(name)) { writer.AddString($"nameOffset{id}", $"{type}{typeCounts[obj.ObjectType]}"); } else { writer.AddString($"nameOffset{id}", $"{name}"); } writer.AddString($"typeOffset{id}", type); writer.WriteNulls(16); writer.Write(obj.Transform.Position); writer.Write(0); writer.Write(obj.Transform.Rotation); writer.Write(obj.Parameters.Count); writer.AddOffset($"paramOffset{id}"); } void WriteGroup(int id) { writer.AddString($"groupNameOffset{id}", $"{groupNames[id]}"); writer.AddString($"groupTypeOffset{id}", $"{groupTypes[id]}"); writer.Write(3); //Should actually be the number of objects writer.AddOffset($"groupObjectCount{id}"); } void WriteGroupCounts(int id) { writer.FillInOffset($"groupObjectCount{id}", false); //Should actually be the object IDs writer.Write(0); //Not sure how to properly write eight bytes rather than four... writer.Write(4); } void WriteObjectParams(int id) { var obj = Objects[id]; writer.FillInOffset($"paramOffset{id}", false); foreach (var param in obj.Parameters) { WriteParam(param); writer.FixPadding(0x14); // TODO: Make sure this works right } } void WriteParam(SetObjectParam param) { Console.WriteLine(param.DataType); if (param.DataType == typeof(bool)) { writer.Write(0); writer.Write(((bool)param.Data) ? 1 : 0); } else if (param.DataType == typeof(int)) { writer.Write(1); writer.Write((int)param.Data); } else if (param.DataType == typeof(float)) { writer.Write(2); writer.Write((float)param.Data); } else if (param.DataType == typeof(string)) { writer.Write(3); writer.AddString($"offset{stringParamCount}", (string)param.Data); writer.Write(1); ++stringParamCount; } else if (param.DataType == typeof(Vector3)) { writer.Write(4); writer.Write((Vector3)param.Data); } else if (param.DataType == typeof(uint)) { writer.Write(6); writer.Write((uint)param.Data); } else { Console.WriteLine( "WARNING: '06 sets do not support object param type {0}!", param.DataType); writer.Write(0L); } } }
public override void Save(Stream fileStream) { // BINA Header var writer = new BINAWriter(fileStream, Header); var rand = new Random(); // Header writer.Write((byte)3); // TODO: Figure out what this is lol writer.Write((byte)Sheets.Count); writer.Write((byte)0); writer.Write((byte)0); writer.Write(0); writer.AddOffset("sheetsOffset", 8); // Sheets writer.FillInOffsetLong("sheetsOffset", false, false); for (int i = 0; i < Sheets.Count; ++i) { var sheet = Sheets[i]; writer.AddString($"sheetNameOffset{i}", sheet.Name, 8); writer.Write((ulong)sheet.Cells.Count); writer.AddOffset($"cellsOffset{i}", 8); } // Cells for (int i = 0; i < Sheets.Count; ++i) { var sheet = Sheets[i]; writer.FillInOffsetLong($"cellsOffset{i}", false, false); for (int i2 = 0; i2 < sheet.Cells.Count; ++i2) { var cell = sheet.Cells[i2]; if (!cell.UUID.HasValue) { cell.UUID = (ulong)rand.Next(); } writer.Write(cell.UUID.Value); writer.AddString($"cellNameOffset{i}{i2}", cell.Name, 8); writer.AddOffset($"secondEntryOffset{i}{i2}", 8); writer.AddOffset($"dataOffset{i}{i2}", 8); } } // Data for (int i = 0; i < Sheets.Count; ++i) { var sheet = Sheets[i]; for (int i2 = 0; i2 < sheet.Cells.Count; ++i2) { var cell = sheet.Cells[i2]; writer.FillInOffsetLong($"dataOffset{i}{i2}", false, false); var sb = new StringBuilder(cell.Data); sb.Replace(NullReplaceChar, '\0'); sb.Replace("\r\n", "\n"); var bytes = Encoding.Unicode.GetBytes(sb.ToString()); writer.Write(bytes); writer.Write((ushort)0); writer.FixPadding(8); } } // Second Entries for (int i = 0; i < Sheets.Count; ++i) { var sheet = Sheets[i]; for (int i2 = 0; i2 < sheet.Cells.Count; ++i2) { var cell = sheet.Cells[i2]; writer.FillInOffsetLong($"secondEntryOffset{i}{i2}", false, false); writer.AddString($"secondEntryNameOffset{i}{i2}", cell.Name, 8); if (!string.IsNullOrEmpty(cell.TypeName)) { writer.AddOffset($"typeOffset{i}{i2}", 8); } else { writer.Write(0UL); } writer.AddOffset($"layoutOffset{i}{i2}", 8); } } // Cell Types foreach (var type in Types) { // Fill-in Second Entry Type Offset var t = type.Value; for (int i = 0; i < Sheets.Count; ++i) { var sheet = Sheets[i]; for (int i2 = 0; i2 < sheet.Cells.Count; ++i2) { var cell = sheet.Cells[i2]; if (string.IsNullOrEmpty(cell.TypeName) || cell.TypeName != type.Key) { continue; } writer.FillInOffsetLong($"typeOffset{i}{i2}", false, false); } } // Offsets if (!string.IsNullOrEmpty(type.Key)) { writer.AddString($"typeName{type.Key}", type.Key, 8); } else { writer.Write(0UL); } if (!string.IsNullOrEmpty(t.Namespace)) { writer.AddString($"typeNamespace{type.Key}", t.Namespace, 8); } else { writer.Write(0UL); } WriteOptOffset($"typeUnknownFloat1Offset{type.Key}", t.UnknownFloat1); WriteOptOffset($"typeUnknownFloat2Offset{type.Key}", t.UnknownFloat2); WriteOptOffset($"typeUnknownFloat3Offset{type.Key}", t.UnknownFloat3); WriteOptOffset($"typeUnknownInt1Offset{type.Key}", t.UnknownInt1); WriteOptOffset($"typeUnknownInt2Offset{type.Key}", t.UnknownInt2); writer.Write(0UL); // unknownOffset1 WriteOptOffset($"typeUnknownULong2Offset{type.Key}", t.UnknownULong2); writer.Write(0UL); // unknownOffset2 writer.Write(0UL); // unknownOffset3 writer.Write(0UL); // unknownOffset4 WriteOptOffset($"typeUnknownULong1Offset{type.Key}", t.UnknownULong1); writer.Write(0UL); // unknownOffset5 // UnknownFloat1 if (t.UnknownFloat1.HasValue) { writer.FillInOffsetLong($"typeUnknownFloat1Offset{type.Key}", false, false); writer.Write(t.UnknownFloat1.Value); writer.Write(0); } // UnknownFloat2 if (t.UnknownFloat2.HasValue) { writer.FillInOffsetLong($"typeUnknownFloat2Offset{type.Key}", false, false); writer.Write(t.UnknownFloat2.Value); writer.Write(0); } // UnknownFloat3 if (t.UnknownFloat3.HasValue) { writer.FillInOffsetLong($"typeUnknownFloat3Offset{type.Key}", false, false); writer.Write(t.UnknownFloat3.Value); writer.Write(0); } // UnknownInt1 if (t.UnknownInt1.HasValue) { writer.FillInOffsetLong($"typeUnknownInt1Offset{type.Key}", false, false); writer.Write(t.UnknownInt1.Value); writer.Write(0); } // UnknownInt2 if (t.UnknownInt2.HasValue) { writer.FillInOffsetLong($"typeUnknownInt2Offset{type.Key}", false, false); writer.Write(t.UnknownInt2.Value); writer.Write(0); } // UnknownULong1 if (t.UnknownULong1.HasValue) { writer.FillInOffsetLong($"typeUnknownULong1Offset{type.Key}", false, false); writer.Write(t.UnknownULong1.Value); } // UnknownULong2 if (t.UnknownULong2.HasValue) { writer.FillInOffsetLong($"typeUnknownULong2Offset{type.Key}", false, false); writer.Write(t.UnknownULong2.Value); } } // Layouts for (int i = 0; i < Layouts.Count; ++i) { writer.FixPadding(8); // Fill-In Cell Offsets var cat = Layouts[i]; for (int sheetIndex = 0; sheetIndex < Sheets.Count; ++sheetIndex) { var sheet = Sheets[sheetIndex]; for (int i2 = 0; i2 < sheet.Cells.Count; ++i2) { var cell = sheet.Cells[i2]; if (cell.LayoutIndex != i) { continue; } writer.FillInOffsetLong($"layoutOffset{sheetIndex}{i2}", false, false); } } // Write Layout if (!string.IsNullOrEmpty(cat.Name)) { writer.AddString($"layoutName{i}", cat.Name, 8); } else { writer.Write(0UL); } WriteOptOffset($"catUnknownData1{i}", cat.UnknownData1); WriteOptOffset($"catUnknownData2{i}", cat.UnknownData2); WriteOptOffset($"catUnknownData3{i}", cat.UnknownData3); WriteOptOffset($"catUnknownData4{i}", cat.UnknownData4); WriteOptOffset($"catUnknownData5{i}", cat.UnknownData5); WriteOptOffset($"catUnknownData6{i}", cat.UnknownData6); WriteOptOffset($"catUnknownData7{i}", cat.UnknownData7); writer.Write(cat.UnknownData8); // UnknownData1 if (cat.UnknownData1.HasValue) { writer.FixPadding(8); writer.FillInOffsetLong($"catUnknownData1{i}", false, false); writer.Write(cat.UnknownData1.Value); } // UnknownData2 if (cat.UnknownData2.HasValue) { writer.FixPadding(8); writer.FillInOffsetLong($"catUnknownData2{i}", false, false); writer.Write(cat.UnknownData2.Value); } // UnknownData3 if (cat.UnknownData3.HasValue) { writer.FixPadding(8); writer.FillInOffsetLong($"catUnknownData3{i}", false, false); writer.Write(cat.UnknownData3.Value); } // UnknownData4 if (cat.UnknownData4.HasValue) { writer.FixPadding(8); writer.FillInOffsetLong($"catUnknownData4{i}", false, false); writer.Write(cat.UnknownData4.Value); } // UnknownData5 if (cat.UnknownData5.HasValue) { writer.FixPadding(8); writer.FillInOffsetLong($"catUnknownData5{i}", false, false); writer.Write(cat.UnknownData5.Value); } // UnknownData6 if (cat.UnknownData6.HasValue) { writer.FixPadding(8); writer.FillInOffsetLong($"catUnknownData6{i}", false, false); writer.Write(cat.UnknownData6.Value); } // UnknownData7 if (cat.UnknownData7.HasValue) { writer.FixPadding(8); writer.FillInOffsetLong($"catUnknownData7{i}", false, false); writer.Write(cat.UnknownData7.Value); } } // Footer writer.FinishWrite(Header); // Sub-Methods void WriteOptOffset <T>(string offsetName, T?value) where T : struct { if (value.HasValue) { writer.AddOffset(offsetName, 8); } else { writer.Write(0UL); } } }
protected void WriteObjectParameters(BINAWriter writer, SetObject obj, int objID) { uint arrIndex = 0, strIndex = 0; uint paramStartPos = (uint)writer.BaseStream.Position; writer.FillInOffsetLong($"objParamsOffset{objID}", false, false); // Write Normal Parameters foreach (var param in obj.Parameters) { WriteParameter(param); } // Padding uint rawLength = obj.GetCustomDataValue <uint>("RawByteLength"); uint len = (uint)(writer.BaseStream.Position - paramStartPos); if (rawLength > len) { writer.WriteNulls(rawLength - len); } // Write Arrays if (arrIndex < 1) { return; // Don't bother if there's not even any arrays } writer.FixPadding(8); arrIndex = 0; foreach (var param in obj.Parameters) { WriteArray(param); } // Sub-Methods void WriteParameter(SetObjectParam param) { FixPadding(writer, param.DataType); // Special Param Types if (param is SetObjectParamGroup group) { foreach (var p in group.Parameters) { WriteParameter(p); } writer.FixPadding(group.Padding ?? 16); return; } else if (param.DataType == typeof(ObjectReference[])) { var arr = (param.Data as ObjectReference[]); ulong arrLength = (ulong)arr.LongLength; if (arrLength < 1) { writer.WriteNulls(24); return; } writer.AddOffset($"obj{objID}ArrOffset{arrIndex}", 8); writer.Write(arrLength); writer.Write(arrLength); ++arrIndex; return; } else if (param.DataType == typeof(ObjectReference)) { var reference = (param.Data as ObjectReference); if (reference == null) { writer.Write(0U); return; } reference.Write(writer); return; } else if (param.DataType == typeof(string)) { string str = (param.Data as string); if (string.IsNullOrEmpty(str)) { writer.Write(0UL); writer.Write(0UL); return; } writer.AddString($"obj{objID}StrOffset{strIndex}", str, 8); writer.Write(0UL); ++strIndex; return; } // Data writer.WriteByType(param.DataType, param.Data); // Post-Param Padding if (param.DataType == typeof(Vector3)) { writer.Write(0U); } } void WriteArray(SetObjectParam param) { // Groups if (param is SetObjectParamGroup group) { foreach (var p in group.Parameters) { WriteArray(p); } return; } // Array Values if (param.DataType == typeof(ObjectReference[])) { var arr = (param.Data as ObjectReference[]); if (arr == null || arr.Length < 1) { return; } writer.FillInOffsetLong($"obj{objID}ArrOffset{arrIndex}", false, false); for (uint i = 0; i < arr.Length; ++i) { if (arr[i] == null) { writer.Write(0U); } else { arr[i].Write(writer); } } ++arrIndex; } } }
public override void Save(Stream fileStream) { var writer = new BINAWriter(fileStream, Header); var typeCounts = new Dictionary <string, int>(); uint stringParamCount = 0; // Header writer.WriteNulls(0xC); writer.WriteNullTerminatedString("test"); writer.WriteNulls(0x1B); writer.Write(Objects.Count); writer.AddOffset("objectOffset"); // TODO: Write group count writer.Write(0); writer.Write(0); // Data writer.FillInOffset("objectOffset", false); for (int i = 0; i < Objects.Count; ++i) { WriteObject(i); } for (int i = 0; i < Objects.Count; ++i) { WriteObjectParams(i); } // Write Groups // TODO: Write groups properly // Write Footer writer.FinishWrite(Header); // Sub-Methods void WriteObject(int id) { // Object Entry var obj = Objects[id]; string type = obj.ObjectType; if (!typeCounts.ContainsKey(type)) { typeCounts.Add(type, 1); } else { ++typeCounts[type]; } writer.AddString($"nameOffset{id}", $"{type}{typeCounts[obj.ObjectType]}"); writer.AddString($"typeOffset{id}", type); writer.WriteNulls(16); writer.Write(obj.Transform.Position); writer.Write(0); writer.Write(obj.Transform.Rotation); writer.Write(obj.Parameters.Count); writer.AddOffset($"paramOffset{id}"); } void WriteObjectParams(int id) { var obj = Objects[id]; writer.FillInOffset($"paramOffset{id}", false); foreach (var param in obj.Parameters) { WriteParam(param); writer.FixPadding(0x14); // TODO: Make sure this works right } } void WriteParam(SetObjectParam param) { if (param.DataType == typeof(bool)) { writer.Write(0); writer.Write(((bool)param.Data) ? 1 : 0); } else if (param.DataType == typeof(int)) { writer.Write(1); writer.Write((int)param.Data); } else if (param.DataType == typeof(float)) { writer.Write(2); writer.Write((float)param.Data); } else if (param.DataType == typeof(string)) { writer.Write(3); writer.AddString($"offset{stringParamCount}", (string)param.Data); writer.Write(1); ++stringParamCount; } else if (param.DataType == typeof(Vector3)) { writer.Write(4); writer.Write((Vector3)param.Data); } else if (param.DataType == typeof(uint)) { writer.Write(6); writer.Write((uint)param.Data); } else { Console.WriteLine( "WARNING: '06 sets do not support object param type {0}!", param.DataType); writer.Write(0L); } } }
protected void WriteObject(BINAWriter writer, SetObject obj, int objID) { writer.Write(0UL); // Object Type writer.AddString($"objTypeOffset{objID}", obj.ObjectType, 8); // Object Name string name = ""; if (obj.CustomData.ContainsKey("Name")) { name = (obj.CustomData["Name"].Data as string); } if (string.IsNullOrEmpty(name)) { name = $"{obj.ObjectType}{objID}"; } writer.AddString($"objNameOffset{objID}", name, 8); // Object Entry writer.Write((ushort)obj.ObjectID); writer.Write((obj.CustomData.ContainsKey("GroupID")) ? (ushort)obj.CustomData["GroupID"].Data : obj.GetCustomDataValue <ushort>("Unknown1")); writer.Write(obj.GetCustomDataValue <ushort>("ParentID")); writer.Write((obj.CustomData.ContainsKey("ParentGroupID")) ? (ushort)obj.CustomData["ParentGroupID"].Data : obj.GetCustomDataValue <ushort>("ParentUnknown1")); writer.Write(obj.Transform.Position); writer.Write(obj.Transform.Rotation.ToEulerAngles(true)); writer.Write((obj.CustomData.ContainsKey("ChildPosOffset")) ? (Vector3)obj.CustomData["ChildPosOffset"].Data : obj.Transform.Position); writer.Write((obj.CustomData.ContainsKey("ChildRotOffset")) ? (Vector3)obj.CustomData["ChildRotOffset"].Data : obj.Transform.Rotation.ToEulerAngles(true)); // Extra Parameter Entries uint extraParamCounts = (uint)obj.CustomData.Count; if (obj.CustomData.ContainsKey("Name")) { extraParamCounts -= 1; } if (obj.CustomData.ContainsKey("RangeOut")) { extraParamCounts -= 1; } if (obj.CustomData.ContainsKey("GroupID") || obj.CustomData.ContainsKey("Unknown1")) { extraParamCounts -= 1; } if (obj.CustomData.ContainsKey("ParentID")) { extraParamCounts -= 1; } if (obj.CustomData.ContainsKey("ParentGroupID") || obj.CustomData.ContainsKey("ParentUnknown1")) { extraParamCounts -= 1; } if (obj.CustomData.ContainsKey("ChildPosOffset")) { extraParamCounts -= 1; } if (obj.CustomData.ContainsKey("ChildRotOffset")) { extraParamCounts -= 1; } if (obj.CustomData.ContainsKey("RawByteLength")) { extraParamCounts -= 1; } writer.AddOffset($"extraParamsOffset{objID}", 8); writer.Write((ulong)extraParamCounts); // TODO writer.Write((ulong)extraParamCounts); // TODO writer.Write(0UL); writer.AddOffset($"objParamsOffset{objID}", 8); writer.FixPadding(16); writer.FillInOffsetLong($"extraParamsOffset{objID}", false, false); writer.AddOffsetTable($"extraParamOffset{objID}", extraParamCounts, 8); writer.FixPadding(16); // TODO: Make sure this is correct int i = -1; foreach (var customData in obj.CustomData) { if (customData.Key == "Name" || customData.Key == "GroupID" || customData.Key == "Unknown1" || customData.Key == "ParentID" || customData.Key == "ParentGroupID" || customData.Key == "ParentUnknown1" || customData.Key == "RangeOut" || customData.Key == "ChildPosOffset" || customData.Key == "ChildRotOffset" || customData.Key == "RawByteLength") { continue; } writer.FillInOffsetLong( $"extraParamOffset{objID}_{++i}", false, false); writer.Write(0UL); writer.AddString($"extraParamNameOffset{objID}{i}", (customData.Key == "RangeIn") ? "RangeSpawning" : customData.Key, 8); if (!WriteExtraParamLength(writer, customData)) { writer.Write(0); Console.WriteLine( $"WARNING: CustomData {customData.Key} skipped; Unknown Type!"); } writer.AddOffset($"extraParamDataOffset{objID}{i}", 8); } // Extra Parameter Data foreach (var customData in obj.CustomData) { if (customData.Key == "Name" || customData.Key == "GroupID" || customData.Key == "Unknown1" || customData.Key == "ParentID" || customData.Key == "ParentGroupID" || customData.Key == "ParentUnknown1" || customData.Key == "RangeOut" || customData.Key == "ChildPosOffset" || customData.Key == "ChildRotOffset" || customData.Key == "RawByteLength") { continue; } writer.FillInOffsetLong( $"extraParamDataOffset{objID}{i}", false, false); if (!WriteExtraParamData(writer, obj, customData)) { writer.Write(0UL); } } }
protected void WriteNodeTree(BINAWriter writer, NodeTree tree, string prefix) { writer.Write(tree.Nodes.Count); writer.Write(tree.DataNodeCount); writer.AddOffset($"{prefix}nodesOffset", 8); writer.AddOffset($"{prefix}dataNodeIndicesOffset", 8); // Nodes int dataIndex = -1; tree.DataNodeIndices = new List <int>(); writer.FillInOffsetLong($"{prefix}nodesOffset", true, false); for (int i = 0; i < tree.Nodes.Count; ++i) { // Name var node = tree.Nodes[i]; if (!string.IsNullOrEmpty(node.Name)) { writer.AddString($"{prefix}nodeNameOffset{i}", node.Name, 8); } else { writer.Write(0UL); } // Data if (node.HasData) { node.DataIndex = ++dataIndex; writer.AddOffset($"{prefix}nodeDataOffset{dataIndex}", 8); tree.DataNodeIndices.Add(i); } else { writer.Write(0UL); } // Child Index Table Offset if (node.ChildCount > 0) { writer.AddOffset($"{prefix}nodeChildIDTableOffset{i}", 8); } else { writer.Write(0UL); } // Populate Child IDs if (node.ParentIndex >= 0) { tree.Nodes[node.ParentIndex].ChildIDs.Add(i); } // Indices writer.Write(node.ParentIndex); writer.Write(i); // Global Index writer.Write(node.DataIndex); // Other writer.Write(node.ChildCount); writer.Write(node.HasData); writer.Write(node.FullPathSize); } }
public void Write(BINAWriter writer, NodeTree tree, string prefix) { // Name if (!string.IsNullOrEmpty(Name)) { writer.AddString($"{prefix}nodeNameOffset{Index}", Name, 8); } else { writer.Write(0UL); } // Data bool hasData = (Data != null); if (hasData) { writer.AddOffset($"{prefix}nodeDataOffset{Index}", 8); } else { writer.Write(0UL); } // Child Index Table Offset if (ChildIndices.Count > 0) { writer.AddOffset($"{prefix}nodeChildIDTableOffset{Index}", 8); } else { writer.Write(0UL); } // Indices writer.Write(ParentIndex); writer.Write(Index); writer.Write(DataIndex); // Other writer.Write((ushort)ChildIndices.Count); writer.Write(hasData); // Full Path Size string pth; int fullPathSize = 0; for (int i = ParentIndex; i > 0;) { pth = tree.Nodes[i].Name; i = tree.Nodes[i].ParentIndex; if (string.IsNullOrEmpty(pth)) { continue; } fullPathSize += pth.Length; if (fullPathSize > byte.MaxValue) { throw new NotSupportedException( "Forces cannot have file names > 255 characters in length!"); } } writer.Write((byte)fullPathSize); }
public int Save(Stream fileStream, uint?sizeLimit, int startIndex = 0, List <string> splitList = null) { uint size = 0; int endIndex = -1; bool isRootPAC = !sizeLimit.HasValue; // BINA Header var writer = new BINAWriter(fileStream, Header); if (Header.ID == 0) { // Get a random non-zero value between 1 and 0xFFFFFFFF // because I care too much™ var rand = new Random(); do { Header.ID = unchecked ((uint)rand.Next( int.MinValue, int.MaxValue)); }while (Header.ID == 0); } // Generate list of files to pack, categorized by extension var filesByExt = new Dictionary <string, List <DataContainer> >(); for (int i = startIndex; i < Data.Count; ++i) { var file = (Data[i] as ArchiveFile); if (file == null) { continue; } int extIndex = file.Name.IndexOf('.'); if (extIndex == -1) { Console.WriteLine( "WARNING: Skipped {0} as it has no extension!", file.Name); continue; } string ext = file.Name.Substring(extIndex); if (!Types.ContainsKey(ext)) { Console.WriteLine( "WARNING: Skipped {0} as its extension ({1}) is unsupported!", file.Name, ext); continue; } // Root-Exclusive Type Check var dataEntry = new DataEntry(file.Data); if (isRootPAC) { dataEntry.DataType = (!RootExclusiveTypes.Contains(ext)) ? DataEntryTypes.NotHere : DataEntryTypes.Regular;; } else if (RootExclusiveTypes.Contains(ext)) { continue; } // BINA Header Check if (file.Data != null && file.Data.Length > 3 && file.Data[0] == 0x42 && file.Data[1] == 0x49 && file.Data[2] == 0x4E && file.Data[3] == 0x41) { dataEntry.DataType = DataEntryTypes.BINAFile; } // Split if you exceed the sizeLimit if (!isRootPAC && sizeLimit.HasValue) { // Not very accurate but close enough™ size += (uint)file.Data.Length; if (size >= sizeLimit.Value) { endIndex = i; break; } } // Add Node to list, making the list first if necessary List <DataContainer> files; if (!filesByExt.ContainsKey(ext)) { files = new List <DataContainer>(); filesByExt.Add(ext, files); } else { files = filesByExt[ext]; } // We use substring instead of Path.GetFileNameWithoutExtension // to support files with multiple extensions (e.g. *.grass.bin) string shortName = file.Name.Substring(0, extIndex); files.Add(new DataContainer(shortName, dataEntry)); } // Pack file list into Node Trees and generate types list //var fileTrees = new Dictionary<string, NodeTree>(); var types = new List <DataContainer>(); foreach (var fileType in filesByExt) { var fileTree = new NodeTree(fileType.Value) { CustomData = fileType.Key.Substring(1) }; types.Add(new DataContainer( Types[fileType.Key], fileTree)); } // Pack types list into Node Tree var typeTree = new NodeTree(types); // Write types Node Tree const string typeTreePrefix = "typeTree"; typeTree.Write(writer, typeTreePrefix); // Write file Node Trees and generate an array of trees in the // same order as they'll be in the file for easier writing var fileTrees = new NodeTree[typeTree.DataNodeIndices.Count]; for (int i = 0; i < fileTrees.Length; ++i) { int dataNodeIndex = typeTree.DataNodeIndices[i]; var tree = (NodeTree)typeTree.Nodes[dataNodeIndex].Data; writer.FillInOffsetLong( $"{typeTreePrefix}nodeDataOffset{dataNodeIndex}", true, false); fileTrees[i] = tree; tree.Write(writer, $"fileTree{i}"); } // Write Data Node Indices typeTree.WriteDataIndices(writer, typeTreePrefix); for (int i = 0; i < fileTrees.Length; ++i) { fileTrees[i].WriteDataIndices(writer, $"fileTree{i}"); } // Write Child Node Indices typeTree.WriteChildIndices(writer, typeTreePrefix); for (int i = 0; i < fileTrees.Length; ++i) { fileTrees[i].WriteChildIndices(writer, $"fileTree{i}"); } // Write Split PACs section if (isRootPAC && splitList != null) { writer.Write((ulong)splitList.Count); writer.AddOffset($"splitPACsOffset", 8); writer.FillInOffsetLong($"splitPACsOffset", true, false); Header.SplitListLength += 16; for (int i = 0; i < splitList.Count; ++i) { writer.AddString($"splitPACName{i}", splitList[i], 8); Header.SplitListLength += 8; } } // Write File Entries long fileEntriesOffset = fileStream.Position; for (int i = 0; i < fileTrees.Length; ++i) { fileTrees[i].WriteDataEntries(writer, $"fileTree{i}", (string)fileTrees[i].CustomData, Header.ID); } // Write String Table uint stringTablePos = (uint)fileStream.Position; writer.WriteStringTable(Header); writer.FixPadding(8); // Write File Data long fileDataOffset = fileStream.Position; for (int i = 0; i < fileTrees.Length; ++i) { fileTrees[i].WriteData(writer, $"fileTree{i}", (string)fileTrees[i].CustomData); } // Write Offset Table writer.FixPadding(8); uint footerPos = writer.WriteFooter(Header); // Fill-In Header uint fileSize = (uint)fileStream.Position; writer.BaseStream.Position = 0; Header.NodeTreeLength = (uint)(fileEntriesOffset - PACxHeader.Length) - Header.SplitListLength; Header.FileEntriesLength = stringTablePos - (uint)fileEntriesOffset; Header.StringTableLength = (uint)fileDataOffset - stringTablePos; Header.DataLength = (footerPos - (uint)fileDataOffset); // 5 if there are splits and this is the root, 2 if this is a split, 1 if no splits Header.PacType = (sizeLimit.HasValue) ? PACxHeader.PACTypes.IsSplit : (splitList != null) ? PACxHeader.PACTypes.HasSplits : PACxHeader.PACTypes.HasNoSplits; Header.SplitCount = (splitList == null) ? 0U : (uint)splitList.Count; Header.FinishWrite(writer); return(endIndex); }
public override void Save(Stream fileStream) { var writer = new BINAWriter(fileStream, Header); uint stringParamCount = 0; // Header writer.WriteNulls(0xC); if (Name != null) { writer.WriteNullTerminatedString(Name); } else { writer.WriteNullTerminatedString("test"); } writer.WriteNulls(0x1B); writer.Write(Objects.Count); writer.AddOffset("objectTableOffset"); writer.Write(Groups.Count); if (Groups.Count != 0) { writer.AddOffset("groupTableOffset"); } else { writer.Write(0); } //Objects writer.FillInOffset("objectTableOffset", false); for (int i = 0; i < Objects.Count; i++) { //Object Values writer.AddString($"objectNameOffset{i}", $"{Objects[i].ObjectName}"); writer.AddString($"objectTypeOffset{i}", $"{Objects[i].ObjectType}"); writer.Write(Objects[i].UnknownBytes); writer.Write(Objects[i].Transform.Position); writer.Write(Objects[i].DrawDistance); writer.Write(Objects[i].Transform.Rotation); writer.Write(Objects[i].Parameters.Count); if (Objects[i].Parameters.Count != 0) { writer.AddOffset($"parameterOffset{i}"); } else { writer.Write(0); } } for (int i = 0; i < Objects.Count; i++) { if (Objects[i].Parameters.Count != 0) { writer.FillInOffset($"parameterOffset{i}", false); foreach (var parameter in Objects[i].Parameters) { switch (parameter.DataType.ToString()) { case "System.Boolean": writer.Write(0); writer.Write(((bool)parameter.Data) ? 1 : 0); writer.Write(parameter.Unknown1); writer.Write(parameter.Unknown2); break; case "System.Int32": writer.Write(1); writer.Write((int)parameter.Data); writer.Write(parameter.Unknown1); writer.Write(parameter.Unknown2); break; case "System.Single": writer.Write(2); writer.Write((float)parameter.Data); writer.Write(parameter.Unknown1); writer.Write(parameter.Unknown2); break; case "System.String": writer.Write(3); writer.AddString($"stringParamOffset{stringParamCount}", (string)parameter.Data); writer.Write(parameter.Unknown1); writer.Write(parameter.Unknown2); stringParamCount++; break; case "HedgeLib.Vector3": writer.Write(4); writer.Write((Vector3)parameter.Data); break; case "System.UInt32": writer.Write(6); writer.Write((uint)parameter.Data); writer.Write(parameter.Unknown1); writer.Write(parameter.Unknown2); break; default: Console.WriteLine(parameter.DataType.ToString()); writer.Write(0); writer.Write(0); writer.Write(0); writer.Write(0); break; } writer.Write(parameter.Unknown3); } } /* * //Object Parameters * if (Objects[i].Parameters.Count != 0) * { * Console.WriteLine($"Object {i}"); * writer.FillInOffset($"parameterOffset{i}", false); * foreach (var parameter in Objects[i].Parameters) * { * if (parameter.DataType.ToString() == "System.Boolean") * { * Console.WriteLine("Boolean"); * writer.Write(66); * writer.Write(((bool)parameter.Data) ? 1 : 0); * writer.Write(parameter.Unknown1); * writer.Write(parameter.Unknown2); * writer.Write(parameter.Unknown3); * } * else if (parameter.DataType.ToString() == "System.Int32") * { * Console.WriteLine("Int32"); * writer.Write(77); * writer.Write((int)parameter.Data); * writer.Write(parameter.Unknown1); * writer.Write(parameter.Unknown2); * writer.Write(parameter.Unknown3); * } * else if (parameter.DataType.ToString() == "System.Single") * { * Console.WriteLine("Single"); * writer.Write(88); * writer.Write((float)parameter.Data); * writer.Write(parameter.Unknown1); * writer.Write(parameter.Unknown2); * writer.Write(parameter.Unknown3); * } * else if (parameter.DataType.ToString() == "System.String") * { * Console.WriteLine("String"); * writer.Write(99); * writer.AddString($"offset{stringParamCount}", (string)parameter.Data); * writer.Write(parameter.Unknown1); * writer.Write(parameter.Unknown2); * writer.Write(parameter.Unknown3); * ++stringParamCount; * } * else if (parameter.DataType.ToString() == "HedgeLib.Vector3") * { * Console.WriteLine("Vector3"); * writer.Write(55); * writer.Write((Vector3)parameter.Data); * writer.Write(parameter.Unknown3); * } * else if (parameter.DataType.ToString() == "System.UInt32") * { * Console.WriteLine("UInt32"); * writer.Write(44); * writer.Write((uint)parameter.Data); * writer.Write(parameter.Unknown1); * writer.Write(parameter.Unknown2); * writer.Write(parameter.Unknown3); * } * else * { * Console.WriteLine("Unhandled Data Type!"); * writer.Write(0L); * } * writer.FixPadding(0x14); * } * }*/ } //Groups if (Groups.Count != 0) { writer.FillInOffset("groupTableOffset", false); for (int i = 0; i < Groups.Count; i++) { writer.AddString($"groupNameOffset{i}", $"{Groups[i].GroupName}"); writer.AddString($"groupTypeOffset{i}", $"{Groups[i].GroupType}"); writer.Write(Groups[i].GroupObjectCount); writer.AddOffset($"groupObjectList{i}"); } for (int i = 0; i < Groups.Count; i++) { writer.FillInOffset($"groupObjectList{i}", false); for (int c = 0; c < Groups[i].ObjectIDs.Count; c++) { writer.Write(0); writer.Write(Groups[i].ObjectIDs[c]); } } } //writer.WriteNulls(572); // Write Footer writer.FinishWrite(Header); }
public int Save(Stream fileStream, uint?sizeLimit, int startIndex = 0, List <string> splitList = null) { uint size = 0; int endIndex = -1; bool isRootPAC = (!sizeLimit.HasValue && splitList != null); // BINA Header var writer = new BINAWriter(fileStream, Header); if (Header.ID == 0) { // Get a random non-zero value between 1 and 0xFFFFFFFF // because I care too much™ var rand = new Random(); do { Header.ID = unchecked ((uint)rand.Next( int.MinValue, int.MaxValue)); }while (Header.ID == 0); } // Generate Node Trees var typeTree = new NodeTree(); typeTree.Nodes.Add(new Node() { ChildCount = 1, }); typeTree.Nodes.Add(new Node() { Name = "Res", ParentIndex = 0, }); var fileTrees = new Dictionary <string, NodeTree>(); for (int i = startIndex; i < Data.Count; ++i) { var file = (Data[i] as ArchiveFile); if (file == null) { continue; } string ext = file.Name.Substring(file.Name.IndexOf('.')); string shortName = file.Name.Substring(0, file.Name.Length - ext.Length); if (!Types.ContainsKey(ext)) { Console.WriteLine( "WARNING: Skipped {0} as its extension ({1}) is unsupported!", file.Name, ext); continue; } // Root-Exclusive Type Check ulong fileDataType = 0; if (isRootPAC) { fileDataType = (RootExclusiveTypes.Contains(ext)) ? 0UL : 1UL; } // BINA Header Check if (file.Data[0] == 0x42 && file.Data[1] == 0x49 && file.Data[2] == 0x4E && file.Data[3] == 0x41) { fileDataType = 2; } if (!isRootPAC && RootExclusiveTypes.Contains(ext)) { continue; } NodeTree fileTree; string pacType = Types[ext]; // TODO: Do node name splitting more like the game does it if (!fileTrees.ContainsKey(ext)) { ++typeTree.Nodes[1].ChildCount; ++typeTree.DataNodeCount; typeTree.Nodes.Add(new Node() { FullPathSize = 3, Name = pacType.Substring(3), ParentIndex = 1, ChildCount = 1 }); typeTree.Nodes.Add(new Node() { FullPathSize = (byte)pacType.Length, ParentIndex = typeTree.Nodes.Count - 1, DataOffset = -1, HasData = true }); fileTree = new NodeTree(); fileTree.Nodes.Add(new Node()); fileTrees.Add(ext, fileTree); } else { fileTree = fileTrees[ext]; } ++fileTree.Nodes[0].ChildCount; ++fileTree.DataNodeCount; fileTree.Nodes.Add(new Node() { Name = shortName, ParentIndex = 0, ChildCount = 1 }); fileTree.Nodes.Add(new Node() { ArchiveFileIndex = i, DataOffset = -1, ParentIndex = fileTree.Nodes.Count - 1, FullPathSize = (byte)shortName.Length, HasData = true, DataType = fileDataType }); // Split if you exceed the sizeLimit if (sizeLimit.HasValue && i < Data.Count - 1) { // Not very accurate but close enough™ size += (uint)(0x40 + file.Data.Length); if (size >= sizeLimit.Value) { endIndex = i + 1; break; } } } // Write Node Trees WriteNodeTree(writer, typeTree, "typeTree"); int nodeTreeIndex = -1; foreach (var fileTree in fileTrees) { writer.FillInOffsetLong( $"typeTreenodeDataOffset{++nodeTreeIndex}", true, false); WriteNodeTree(writer, fileTree.Value, Types[fileTree.Key]); } // Write Data Node Indices WriteTreeDataIndices(writer, typeTree, "typeTree"); foreach (var fileTree in fileTrees) { WriteTreeDataIndices(writer, fileTree.Value, Types[fileTree.Key]); } // Write Child Node Indices for (int i = 0; i < typeTree.Nodes.Count; ++i) { var node = typeTree.Nodes[i]; if (node.ChildCount < 1) { continue; } writer.FillInOffsetLong( $"typeTreenodeChildIDTableOffset{i}", true, false); for (int i2 = 0; i2 < node.ChildCount; ++i2) { writer.Write(node.ChildIDs[i2]); } writer.FixPadding(8); } foreach (var fileTree in fileTrees) { var tree = fileTree.Value; for (int i = 0; i < tree.Nodes.Count; ++i) { var node = tree.Nodes[i]; if (node.ChildCount < 1) { continue; } writer.FillInOffsetLong( $"{Types[fileTree.Key]}nodeChildIDTableOffset{i}", true, false); for (int i2 = 0; i2 < node.ChildCount; ++i2) { writer.Write(node.ChildIDs[i2]); } writer.FixPadding(8); } } // Write PACs section Header.SplitListLength = 0; if (isRootPAC) { writer.Write((ulong)splitList.Count); writer.AddOffset("splitPACsOffset", 8); Header.SplitListLength += 16; writer.FillInOffsetLong("splitPACsOffset", true, false); for (int i = 0; i < splitList.Count; ++i) { writer.AddString($"splitPACName{i}", splitList[i], 8); Header.SplitListLength += 8; } } // Write File Entries long fileEntriesOffset = fileStream.Position; foreach (var fileTree in fileTrees) { var tree = fileTree.Value; for (int i = 0; i < tree.DataNodeCount; ++i) { var node = tree.Nodes[tree.DataNodeIndices[i]]; writer.FillInOffsetLong( $"{Types[fileTree.Key]}nodeDataOffset{i}", true, false); var file = Data[node.ArchiveFileIndex] as ArchiveFile; writer.Write(Header.ID); writer.Write((ulong)file.Data.Length); writer.Write(0U); if (node.DataType == 1) { writer.Write(0UL); } else { writer.AddOffset($"{Types[fileTree.Key]}fileDataOffset{i}", 8); } writer.Write(0UL); writer.AddString($"{Types[fileTree.Key]}extDataOffset{i}", fileTree.Key.Substring(1), 8); writer.Write(node.DataType); } } // Write String Table uint stringTablePos = writer.WriteStringTable(); writer.FixPadding(8); // Write File Data long fileDataOffset = fileStream.Position; foreach (var fileTree in fileTrees) { var tree = fileTree.Value; for (int i = 0; i < tree.DataNodeCount; ++i) { var node = tree.Nodes[tree.DataNodeIndices[i]]; if (node.DataType == 1) { continue; } writer.FixPadding(16); writer.FillInOffsetLong( $"{Types[fileTree.Key]}fileDataOffset{i}", true, false); var file = Data[node.ArchiveFileIndex] as ArchiveFile; writer.Write(file.Data); } } // Write Offset Table writer.FixPadding(8); uint footerPos = writer.WriteFooter(Header); // Fill-In Header uint fileSize = (uint)fileStream.Position; writer.BaseStream.Position = 0; Header.NodeTreeLength = (uint)(fileEntriesOffset - PACxHeader.Length) - Header.SplitListLength; Header.FileEntriesLength = stringTablePos - (uint)fileEntriesOffset; Header.StringTableLength = (uint)fileDataOffset - stringTablePos; Header.DataLength = (footerPos - (uint)fileDataOffset); // 5 if there are splits and this is the root, 2 if this is a split, 1 if no splits Header.PacType = (sizeLimit.HasValue) ? PACxHeader.PACTypes.IsSplit : (splitList != null) ? PACxHeader.PACTypes.HasSplits : PACxHeader.PACTypes.HasNoSplits; Header.SplitCount = (splitList == null) ? 0U : (uint)splitList.Count; Header.FinishWrite(writer); return(endIndex); }
private static void WriteObject(BINAWriter writer, SetObject obj, SOBJType type, bool rawDataMode = false) // true = full, false = only remaining bytes) { // Get a bunch of values from the object's custom data, if present. uint unknown1 = obj.GetCustomDataValue <ushort>("Unknown1"); uint unknown2 = obj.GetCustomDataValue <uint>("Unknown2"); uint unknown3 = obj.GetCustomDataValue <uint>("Unknown3"); float unknown4 = obj.GetCustomDataValue <float>("Unknown4"); float rangeIn = obj.GetCustomDataValue <float>("RangeIn"); float rangeOut = obj.GetCustomDataValue <float>("RangeOut"); uint parent = (type == SOBJType.LostWorld) ? obj.GetCustomDataValue <uint>("Parent") : 0; var rawParamData = obj.GetCustomDataValue <byte[]>("RawParamData"); // Combine the two values back into one so we can write with correct endianness. uint unknownData = (unknown1 << 16) | (obj.ObjectID & 0xFFFF); writer.Write(unknownData); writer.Write(unknown2); writer.Write(unknown3); writer.Write(unknown4); writer.Write(rangeIn); writer.Write(rangeOut); if (type == SOBJType.LostWorld) { writer.Write(parent); } writer.FixPadding(4); writer.AddOffset($"transformsOffset_{obj.ObjectID}"); writer.Write((uint)obj.Children.Length + 1); writer.WriteNulls((type == SOBJType.LostWorld) ? 0xC : 4u); // Parameters long paramBegin = writer.BaseStream.Position; foreach (var param in obj.Parameters) { // Write Special Types/Fix Padding if (param.DataType == typeof(uint[])) { // Data Info var arr = (uint[])param.Data; writer.FixPadding(4); writer.AddOffset("arrOffset"); writer.Write((uint)arr.Length); writer.WriteNulls(4); // TODO: Figure out what this is. // Data writer.FillInOffset("arrOffset", false); foreach (uint value in arr) { writer.Write(value); } continue; } else if (param.DataType == typeof(string)) { // Data Info string str = (string)param.Data; writer.AddOffset("strOffset"); writer.WriteNulls(4); // TODO: Figure out what this is. if (string.IsNullOrEmpty(str)) { writer.FillInOffset("strOffset", 0, true); } else { writer.FillInOffset("strOffset", false); writer.WriteNullTerminatedString(str); } continue; } else if (param.DataType == typeof(float) || param.DataType == typeof(int) || param.DataType == typeof(uint)) { writer.FixPadding(4); } else if (type == SOBJType.LostWorld && param.DataType == typeof(Vector3)) { writer.FixPadding(16); } // Write Data writer.WriteByType(param.DataType, param.Data); } // Write remaining raw data from loaded ORC if (rawDataMode == false) { writer.Write(rawParamData); } else { int knownParamLength = (int)(writer.BaseStream.Position - paramBegin); writer.Write(rawParamData, knownParamLength, rawParamData.Length - knownParamLength); } writer.FixPadding(4); }
public static void Write(BINAWriter writer, List <SetObject> objects, SOBJType type) { // Get some data we need to write the file var objectsByType = new Dictionary <string, List <int> >(); uint transformCount = 0, objTypeCount = 0; for (int objIndex = 0; objIndex < objects.Count; ++objIndex) { var obj = objects[objIndex]; if (!objectsByType.ContainsKey(obj.ObjectType)) { objectsByType.Add(obj.ObjectType, new List <int>() { objIndex }); ++objTypeCount; } else { objectsByType[obj.ObjectType].Add(objIndex); } transformCount += (uint)obj.Children.Length + 1; } // SOBJ Header var sig = Signature.ToCharArray(); if (!writer.IsBigEndian) { Array.Reverse(sig); } writer.Write(sig); writer.Write(1u); // TODO: Figure out what this value is. writer.Write(objTypeCount); writer.AddOffset("objTypeOffsetsOffset"); writer.Write((type == SOBJType.LostWorld) ? 0 : 0xFFFFFFFF); // I doubt this matters at all tbh. writer.AddOffset("objOffsetsOffset"); writer.Write(objects.Count); writer.WriteNulls(4); writer.Write(transformCount); // Object Offsets writer.FillInOffset("objOffsetsOffset", false); writer.AddOffsetTable("objOffset", (uint)objects.Count); // Object Types uint i = 0; writer.FillInOffset("objTypeOffsetsOffset", false); foreach (var obj in objectsByType) { writer.AddString($"objName_{i}", obj.Key); writer.Write((uint)obj.Value.Count); writer.AddOffset($"objIndicesOffset_{i}"); ++i; } // Object Indices ushort i2 = 0; i = 0; foreach (var obj in objectsByType) { writer.FillInOffset($"objIndicesOffset_{i}", false); for (int i3 = 0; i3 < obj.Value.Count; ++i3) { writer.Write(i2); ++i2; } ++i; } // Objects writer.FixPadding(4); i = 0; foreach (var objType in objectsByType) { foreach (int objIndex in objType.Value) { writer.FillInOffset($"objOffset_{i}", false); WriteObject(writer, objects[objIndex], type); writer.FixPadding(0x4); ++i; } } // TODO: Clean this up writer.FixPadding(4); foreach (var objType in objectsByType) { foreach (int objIndex in objType.Value) { // Transforms writer.FixPadding(0x4); writer.FillInOffset($"transformsOffset_{objects[objIndex].ObjectID}", false); WriteTransform(writer, objects[objIndex].Transform, type == SOBJType.LostWorld); foreach (var childTransform in objects[objIndex].Children) { WriteTransform(writer, childTransform, type == SOBJType.LostWorld); } } } }
public override void Save(Stream fileStream) { //Determine amount of Cues that use a CSB and amount that use an XMA int csbCueCount = 0; int streamCueCount = 0; for (int i = 0; i < Cues.Count; i++) { if (Cues[i].Stream == null) { csbCueCount++; } else { streamCueCount++; } } // Header var writer = new BINAWriter(fileStream, Header); writer.WriteSignature(Signature); writer.Write(537265920); //Hardcoded as all Scene Banks seem to have this number in this position. writer.AddOffset("banksOffset"); writer.AddOffset("cueNamesOffset"); writer.AddOffset("cueIndiciesOffset"); writer.AddOffset("streamsOffset"); writer.FillInOffset("banksOffset", false); writer.Write(Name); writer.Write(Cues.Count); writer.Write(csbCueCount); writer.Write(streamCueCount); //Cue Information writer.FillInOffset("cueNamesOffset", false); int csbCueID = 0; int streamCueID = 0; for (int i = 0; i < Cues.Count; i++) { writer.Write(Cues[i].Name); if (Cues[i].Stream == null) { writer.Write(0); writer.Write(csbCueID); csbCueID++; } else { writer.Write(1); writer.Write(streamCueID); streamCueID++; } writer.Write(Cues[i].Category); writer.Write(Cues[i].Unknown1); writer.Write(Cues[i].Unknown2); } //CSB Cue ID List writer.FillInOffset("cueIndiciesOffset", false); for (int i = 0; i < csbCueCount; i++) { writer.Write(i); } //Stream Names if (streamCueCount != 0) { writer.FillInOffset("streamsOffset", false); for (int i = 0; i < Cues.Count; i++) { if (Cues[i].Stream != null) { writer.AddString($"streamOffset{i}", $"{Cues[i].Stream}"); } } } writer.FinishWrite(Header); }