internal override void Write(BinaryWriterEx bw) { bw.WriteASCII("CLM2"); bw.WriteInt32(0); bw.WriteInt16(1); bw.WriteInt16(1); bw.WriteInt32(0); bw.WriteInt32(0); bw.WriteInt32(Meshes.Count); bw.WriteInt32(0x28); bw.WriteInt32(0); bw.WriteInt32(0); bw.WriteInt32(0x28); for (int i = 0; i < Meshes.Count; i++) { Meshes[i].WriteHeader(bw, i); } for (int i = 0; i < Meshes.Count; i++) { Meshes[i].WriteEntries(bw, i); } }
/// <summary> /// Write a DDS file from this header object and given pixel data. /// </summary> public byte[] Write(byte[] pixelData) { BinaryWriterEx bw = new BinaryWriterEx(false); bw.WriteASCII("DDS "); bw.WriteInt32(124); bw.WriteUInt32((uint)dwFlags); bw.WriteInt32(dwHeight); bw.WriteInt32(dwWidth); bw.WriteInt32(dwPitchOrLinearSize); bw.WriteInt32(dwDepth); bw.WriteInt32(dwMipMapCount); for (int i = 0; i < 11; i++) { bw.WriteInt32(0); } ddspf.Write(bw); bw.WriteUInt32((uint)dwCaps); bw.WriteUInt32((uint)dwCaps2); for (int i = 0; i < 3; i++) { bw.WriteInt32(0); } if (ddspf.dwFourCC == "DX10") { header10.Write(bw); } bw.WriteBytes(pixelData); return(bw.FinishBytes()); }
/// <summary> /// Writes BND3 data to a BinaryWriterEx. /// </summary> internal override void Write(BinaryWriterEx bw) { bw.BigEndian = false; bw.WriteASCII("BND3"); bw.WriteFixStr(Timestamp, 8); bw.WriteByte((byte)Format); bw.WriteBoolean(BigEndian); bw.WriteBoolean(Unk1); bw.WriteByte(0); bw.BigEndian = BigEndian || Binder.ForceBigEndian(Format); bw.WriteInt32(Files.Count); bw.ReserveInt32("HeaderEnd"); bw.WriteInt32(Unk2); bw.WriteInt32(0); for (int i = 0; i < Files.Count; i++) { WriteFileHeader(Files[i], bw, i, Format); } if (Binder.HasName(Format)) { for (int i = 0; i < Files.Count; i++) { WriteFileName(Files[i], bw, i); } } bw.FillInt32($"HeaderEnd", (int)bw.Position); for (int i = 0; i < Files.Count; i++) { WriteFileData(Files[i], bw, i); } }
/// <summary> /// Serializes file data to a stream. /// </summary> protected override void Write(BinaryWriterEx bw) { if (AppliedParamdef == null) { throw new InvalidOperationException("Params cannot be written without applying a paramdef."); } bw.BigEndian = BigEndian; bw.ReserveUInt32("StringsOffset"); if (Format2D.HasFlag(FormatFlags1.Flag01) && Format2D.HasFlag(FormatFlags1.IntDataOffset) || Format2D.HasFlag(FormatFlags1.LongDataOffset)) { bw.WriteInt16(0); } else { bw.ReserveUInt16("DataStart"); } bw.WriteInt16(Unk06); bw.WriteInt16(ParamdefDataVersion); bw.WriteUInt16((ushort)Rows.Count); if (Format2D.HasFlag(FormatFlags1.OffsetParamType)) { bw.WriteInt32(0); bw.ReserveInt64("ParamTypeOffset"); bw.WritePattern(0x14, 0x00); } else { // This padding heuristic isn't completely accurate, not that it matters bw.WriteFixStr(ParamType, 0x20, (byte)(Format2D.HasFlag(FormatFlags1.Flag01) ? 0x20 : 0x00)); } bw.WriteByte((byte)(BigEndian ? 0xFF : 0x00)); bw.WriteByte((byte)Format2D); bw.WriteByte((byte)Format2E); bw.WriteByte(ParamdefFormatVersion); if (Format2D.HasFlag(FormatFlags1.Flag01) && Format2D.HasFlag(FormatFlags1.IntDataOffset)) { bw.ReserveUInt32("DataStart"); bw.WriteInt32(0); bw.WriteInt32(0); bw.WriteInt32(0); } else if (Format2D.HasFlag(FormatFlags1.LongDataOffset)) { bw.ReserveInt64("DataStart"); bw.WriteInt64(0); } for (int i = 0; i < Rows.Count; i++) { Rows[i].WriteHeader(bw, this, i); } // This is probably pretty stupid if (Format2D == FormatFlags1.Flag01) { bw.WritePattern(0x20, 0x00); } if (Format2D.HasFlag(FormatFlags1.Flag01) && Format2D.HasFlag(FormatFlags1.IntDataOffset)) { bw.FillUInt32("DataStart", (uint)bw.Position); } else if (Format2D.HasFlag(FormatFlags1.LongDataOffset)) { bw.FillInt64("DataStart", bw.Position); } else { bw.FillUInt16("DataStart", (ushort)bw.Position); } for (int i = 0; i < Rows.Count; i++) { Rows[i].WriteCells(bw, this, i); } bw.FillUInt32("StringsOffset", (uint)bw.Position); if (Format2D.HasFlag(FormatFlags1.OffsetParamType)) { bw.FillInt64("ParamTypeOffset", bw.Position); bw.WriteASCII(ParamType, true); } for (int i = 0; i < Rows.Count; i++) { Rows[i].WriteName(bw, this, i); } // DeS and BB sometimes (but not always) include some useless padding here }
protected override void Write(BinaryWriterEx bw) { bw.BigEndian = BigEndian; bw.VarintLong = Version == MQBVersion.DarkSouls2Scholar; bw.WriteASCII("MQB "); bw.WriteSByte((sbyte)(BigEndian ? -1 : 0)); bw.WriteByte(0); bw.WriteSByte((sbyte)(Version == MQBVersion.DarkSouls2Scholar ? -1 : 0)); bw.WriteByte(0); bw.WriteUInt32((uint)Version); switch (Version) { case MQBVersion.DarkSouls2: bw.WriteInt32(0x14); break; case MQBVersion.DarkSouls2Scholar: bw.WriteInt32(0x28); break; case MQBVersion.Bloodborne: bw.WriteInt32(0x20); break; case MQBVersion.DarkSouls3: bw.WriteInt32(0x24); break; default: throw new NotImplementedException($"Missing header size for version {Version}."); } bw.ReserveVarint("ResourcePathsOffset"); if (Version == MQBVersion.DarkSouls2Scholar) { bw.WriteInt32(0); bw.WriteInt32(0); bw.WriteInt32(0); bw.WriteInt32(0); } else if (Version >= MQBVersion.Bloodborne) { bw.WriteInt32(1); bw.WriteInt32(0); bw.WriteInt32(0); if (Version >= MQBVersion.DarkSouls3) { bw.WriteInt32(0); } } bw.WriteFixStrW(Name, 0x40, 0x00); bw.WriteSingle(Framerate); bw.WriteInt32(Resources.Count); bw.WriteInt32(Cuts.Count); bw.WriteInt32(0); bw.WriteInt32(0); bw.WriteInt32(0); bw.WriteInt32(0); bw.WriteInt32(0); var allCustomData = new List <CustomData>(); var customDataValueOffsets = new List <long>(); for (int i = 0; i < Resources.Count; i++) { Resources[i].Write(bw, i, allCustomData, customDataValueOffsets); } var offsetsByDispos = new Dictionary <Disposition, long>(); for (int i = 0; i < Cuts.Count; i++) { Cuts[i].Write(bw, Version, offsetsByDispos, i, allCustomData, customDataValueOffsets); } for (int i = 0; i < Cuts.Count; i++) { Cuts[i].WriteTimelines(bw, Version, i); } for (int i = 0; i < Cuts.Count; i++) { Cuts[i].WriteTimelineCustomData(bw, i, allCustomData, customDataValueOffsets); } for (int i = 0; i < Cuts.Count; i++) { Cuts[i].WriteDisposOffsets(bw, offsetsByDispos, i); } bw.FillVarint("ResourcePathsOffset", bw.Position); for (int i = 0; i < Resources.Count; i++) { bw.ReserveVarint($"ResourcePathOffset{i}"); } bw.WriteUTF16(ResourceDirectory, true); for (int i = 0; i < Resources.Count; i++) { if (Resources[i].Path == null) { bw.FillVarint($"ResourcePathOffset{i}", 0); } else { bw.FillVarint($"ResourcePathOffset{i}", bw.Position); bw.WriteUTF16(Resources[i].Path, true); } } // I know this is weird, but trust me. if (Version >= MQBVersion.Bloodborne) { bw.WriteInt16(0); bw.Pad(4); } for (int i = 0; i < allCustomData.Count; i++) { allCustomData[i].WriteSequences(bw, i, customDataValueOffsets[i]); } for (int i = 0; i < allCustomData.Count; i++) { allCustomData[i].WriteSequencePoints(bw, i); } }
internal static void WriteBDFHeader(IBXF3 bxf, BinaryWriterEx bw) { bw.WriteASCII("BDF3"); bw.WriteFixStr(bxf.Version, 8); bw.WriteInt32(0); }
private static void CompressDCXEDGE(byte[] data, BinaryWriterEx bw) { int chunkCount = data.Length / 0x10000; if (data.Length % 0x10000 > 0) { chunkCount++; } bw.WriteASCII("DCX\0"); bw.WriteInt32(0x10000); bw.WriteInt32(0x18); bw.WriteInt32(0x24); bw.WriteInt32(0x24); bw.WriteInt32(0x50 + chunkCount * 0x10); bw.WriteASCII("DCS\0"); bw.WriteInt32(data.Length); bw.ReserveInt32("CompressedSize"); bw.WriteASCII("DCP\0"); bw.WriteASCII("EDGE"); bw.WriteInt32(0x20); bw.WriteInt32(0x9000000); bw.WriteInt32(0x10000); bw.WriteInt32(0); bw.WriteInt32(0); bw.WriteInt32(0x00100100); long dcaStart = bw.Position; bw.WriteASCII("DCA\0"); bw.ReserveInt32("DCASize"); long egdtStart = bw.Position; bw.WriteASCII("EgdT"); bw.WriteInt32(0x00010100); bw.WriteInt32(0x24); bw.WriteInt32(0x10); bw.WriteInt32(0x10000); bw.WriteInt32(data.Length % 0x10000); bw.ReserveInt32("EGDTSize"); bw.WriteInt32(chunkCount); bw.WriteInt32(0x100000); for (int i = 0; i < chunkCount; i++) { bw.WriteInt32(0); bw.ReserveInt32($"ChunkOffset{i}"); bw.ReserveInt32($"ChunkSize{i}"); bw.ReserveInt32($"ChunkCompressed{i}"); } bw.FillInt32("DCASize", (int)(bw.Position - dcaStart)); bw.FillInt32("EGDTSize", (int)(bw.Position - egdtStart)); long dataStart = bw.Position; int compressedSize = 0; for (int i = 0; i < chunkCount; i++) { int chunkSize = 0x10000; if (i == chunkCount - 1) { chunkSize = data.Length % 0x10000; } byte[] chunk; using (MemoryStream cmpStream = new MemoryStream()) using (MemoryStream dcmpStream = new MemoryStream(data, i * 0x10000, chunkSize)) { DeflateStream dfltStream = new DeflateStream(cmpStream, CompressionMode.Compress); dcmpStream.CopyTo(dfltStream); dfltStream.Close(); chunk = cmpStream.ToArray(); } if (chunk.Length < chunkSize) { bw.FillInt32($"ChunkCompressed{i}", 1); } else { bw.FillInt32($"ChunkCompressed{i}", 0); chunk = data; } compressedSize += chunk.Length; bw.FillInt32($"ChunkOffset{i}", (int)(bw.Position - dataStart)); bw.FillInt32($"ChunkSize{i}", chunk.Length); bw.WriteBytes(chunk); bw.Pad(0x10); } bw.FillInt32("CompressedSize", compressedSize); }
internal override void Write(BinaryWriterEx bw) { bw.WriteASCII("TAE "); bw.WriteByte(0); bw.WriteByte(0); bw.WriteByte(0); bw.WriteByte(0xFF); bw.WriteInt32(0x1000C); bw.ReserveInt32("FileSize"); bw.WriteInt64(0x40); bw.WriteInt64(1); bw.WriteInt64(0x50); bw.WriteInt64(0x80); bw.WriteInt64(Unk30); bw.WriteInt64(0); bw.WriteBytes(Flags); bw.WriteInt64(1); bw.WriteInt32(ID); bw.WriteInt32(Animations.Count); bw.ReserveInt64("AnimsOffset"); bw.ReserveInt64("AnimGroupsOffset"); bw.WriteInt64(0xA0); bw.WriteInt64(Animations.Count); bw.ReserveInt64("FirstAnimOffset"); bw.WriteInt64(1); bw.WriteInt64(0x90); bw.WriteInt32(ID); bw.WriteInt32(ID); bw.WriteInt64(0x50); bw.WriteInt64(0); bw.WriteInt64(0xB0); bw.ReserveInt64("SkeletonName"); bw.ReserveInt64("SibName"); bw.WriteInt64(0); bw.WriteInt64(0); bw.FillInt64("SkeletonName", bw.Position); bw.WriteUTF16(SkeletonName, true); bw.Pad(0x10); bw.FillInt64("SibName", bw.Position); bw.WriteUTF16(SibName, true); bw.Pad(0x10); Animations.Sort((a1, a2) => a1.ID.CompareTo(a2.ID)); bw.FillInt64("AnimsOffset", bw.Position); var animOffsets = new List <long>(Animations.Count); for (int i = 0; i < Animations.Count; i++) { animOffsets.Add(bw.Position); Animations[i].WriteHeader(bw, i); } bw.FillInt64("AnimGroupsOffset", bw.Position); bw.ReserveInt64("AnimGroupsCount"); bw.ReserveInt64("AnimGroupsOffset"); int groupCount = 0; long groupStart = bw.Position; for (int i = 0; i < Animations.Count; i++) { int firstIndex = i; bw.WriteInt32((int)Animations[i].ID); while (i < Animations.Count - 1 && Animations[i + 1].ID == Animations[i].ID + 1) { i++; } bw.WriteInt32((int)Animations[i].ID); bw.WriteInt64(animOffsets[firstIndex]); groupCount++; } bw.FillInt64("AnimGroupsCount", groupCount); if (groupCount == 0) { bw.FillInt64("AnimGroupsOffset", 0); } else { bw.FillInt64("AnimGroupsOffset", groupStart); } bw.FillInt64("FirstAnimOffset", bw.Position); for (int i = 0; i < Animations.Count; i++) { Animations[i].WriteBody(bw, i); } for (int i = 0; i < Animations.Count; i++) { Animations[i].WriteAnimFile(bw, i); long timeStart = bw.Position; Animations[i].WriteTimes(bw, i); var eventHeaderOffsets = Animations[i].WriteEventHeaders(bw, i, timeStart); Animations[i].WriteEventData(bw, i); Animations[i].WriteEventGroupHeaders(bw, i); Animations[i].WriteEventGroupData(bw, i, eventHeaderOffsets); } bw.FillInt32("FileSize", (int)bw.Position); }
/// <summary> /// Serializes file data to a stream. /// </summary> protected override void Write(BinaryWriterEx bw) { bw.BigEndian = false; Entries entries; entries.Models = Models.GetEntries(); entries.Events = Events.GetEntries(); entries.Regions = Regions.GetEntries(); entries.Routes = Routes.GetEntries(); entries.Layers = Layers.GetEntries(); entries.Parts = Parts.GetEntries(); entries.PartsPoses = PartsPoses.GetEntries(); entries.BoneNames = BoneNames.GetEntries(); foreach (Model model in entries.Models) { model.CountInstances(entries.Parts); } foreach (Event evt in entries.Events) { evt.GetIndices(this, entries); } foreach (Region region in entries.Regions) { region.GetIndices(this, entries); } foreach (Part part in entries.Parts) { part.GetIndices(this, entries); } foreach (PartsPose pose in entries.PartsPoses) { pose.GetIndices(this, entries); } bw.WriteASCII("MSB "); bw.WriteInt32(1); bw.WriteInt32(0x10); bw.WriteBoolean(false); bw.WriteBoolean(false); bw.WriteByte(1); bw.WriteByte(0xFF); Models.Write(bw, entries.Models); bw.FillInt64("NextParamOffset", bw.Position); Events.Write(bw, entries.Events); bw.FillInt64("NextParamOffset", bw.Position); Regions.Write(bw, entries.Regions); bw.FillInt64("NextParamOffset", bw.Position); Routes.Write(bw, entries.Routes); bw.FillInt64("NextParamOffset", bw.Position); Layers.Write(bw, entries.Layers); bw.FillInt64("NextParamOffset", bw.Position); Parts.Write(bw, entries.Parts); bw.FillInt64("NextParamOffset", bw.Position); PartsPoses.Write(bw, entries.PartsPoses); bw.FillInt64("NextParamOffset", bw.Position); BoneNames.Write(bw, entries.BoneNames); bw.FillInt64("NextParamOffset", 0); }
/// <summary> /// Writes BND4 data to a BinaryWriterEx. /// </summary> internal override void Write(BinaryWriterEx bw) { bw.BigEndian = BigEndian; bw.WriteASCII("BND4"); bw.WriteBoolean(Flag1); bw.WriteBoolean(Flag2); bw.WriteByte(0); bw.WriteByte(0); bw.WriteInt32(0x10000); bw.WriteInt32(Files.Count); bw.WriteInt64(0x40); bw.WriteFixStr(Timestamp, 8); if (Format == 0x0C) { bw.WriteInt64(0x18); } else if (Format == 0x70) { bw.WriteInt64(0x1C); } else { bw.WriteInt64(0x24); } bw.ReserveInt64("DataStart"); bw.WriteBoolean(Unicode); bw.WriteByte(Format); bw.WriteByte(Extended); bw.WriteByte(0); bw.WriteInt32(0); if (Extended == 4) { bw.ReserveInt64("HashGroups"); } else { bw.WriteInt64(0); } for (int i = 0; i < Files.Count; i++) { Files[i].Write(bw, i, Format); } for (int i = 0; i < Files.Count; i++) { File file = Files[i]; bw.FillInt32($"FileName{i}", (int)bw.Position); if (Unicode) { bw.WriteUTF16(file.Name, true); } else { bw.WriteShiftJIS(file.Name, true); } } if (Extended == 4) { uint groupCount = 0; for (uint p = (uint)Files.Count / 7; p <= 100000; p++) { if (SFUtil.IsPrime(p)) { groupCount = p; break; } } if (groupCount == 0) { throw new InvalidOperationException("Hash group count not determined in BND4."); } var hashLists = new List <PathHash> [groupCount]; for (int i = 0; i < groupCount; i++) { hashLists[i] = new List <PathHash>(); } for (int i = 0; i < Files.Count; i++) { var pathHash = new PathHash(i, Files[i].Name); uint group = pathHash.Hash % groupCount; hashLists[group].Add(pathHash); } for (int i = 0; i < groupCount; i++) { hashLists[i].Sort((ph1, ph2) => ph1.Hash.CompareTo(ph2.Hash)); } var hashGroups = new List <HashGroup>(); var pathHashes = new List <PathHash>(); int count = 0; foreach (List <PathHash> hashList in hashLists) { int index = count; foreach (PathHash pathHash in hashList) { pathHashes.Add(pathHash); count++; } hashGroups.Add(new HashGroup(index, count - index)); } bw.Pad(0x8); bw.FillInt64("HashGroups", bw.Position); bw.ReserveInt64("PathHashes"); bw.WriteUInt32(groupCount); bw.WriteInt32(0x00080810); foreach (HashGroup hashGroup in hashGroups) { hashGroup.Write(bw); } // No padding after section 1 bw.FillInt64("PathHashes", bw.Position); foreach (PathHash pathHash in pathHashes) { pathHash.Write(bw); } } bw.FillInt64("DataStart", bw.Position); for (int i = 0; i < Files.Count; i++) { File file = Files[i]; if (file.Bytes.LongLength > 0) { bw.Pad(0x10); } bw.FillInt32($"FileData{i}", (int)bw.Position); byte[] bytes = file.Bytes; int compressedSize = bytes.Length; if (file.Flags == 0x03 || file.Flags == 0xC0) { compressedSize = SFUtil.WriteZlib(bw, 0x9C, bytes); } else { bw.WriteBytes(bytes); } bw.FillInt64($"CompressedSize{i}", bytes.Length); } }
public void Write(BinaryWriterEx bw, uint sectionBaseOffset) { bw.WriteUInt32(Signature); bw.WriteByte(0x09); bw.WriteASCII(ClassName, true); }
internal override void Write(BinaryWriterEx bw) { bw.BigEndian = false; bw.WriteInt32(0); bw.ReserveInt32("FileSize"); int fileStart = (int)bw.Position; bw.WriteInt32(0); bw.WriteInt32(3); WriteMarker(bw, 0x01); bw.WriteInt32(0); bw.WriteInt32(0x1C); bw.WriteInt32(1); bw.WriteInt32(2); WriteMarker(bw, 0xB0); bw.WriteInt32(4); bw.WriteASCII("MTD "); WriteMarker(bw, 0x34); bw.WriteInt32(0x3E8); WriteMarker(bw, 0x01); bw.WriteInt32(0); bw.ReserveInt32("DataSize"); bw.WriteInt32(2); bw.WriteInt32(4); int dataStart = (int)bw.Position; WriteMarker(bw, 0xA3); bw.WriteShiftJISLengthPrefixed(ShaderPath, 0xA3); bw.WriteShiftJISLengthPrefixed(Description, 0x03); bw.WriteInt32(1); bw.WriteInt32(0); bw.ReserveInt32("ParamSize"); bw.WriteInt32(3); bw.WriteInt32(4); WriteMarker(bw, 0xA3); bw.WriteInt32(0); int paramStart = (int)bw.Position; WriteMarker(bw, 0x03); bw.WriteInt32(Params.Count); foreach (Param internalEntry in Params) { internalEntry.Write(bw); } WriteMarker(bw, 0x03); bw.WriteInt32(Textures.Count); foreach (Texture externalEntry in Textures) { externalEntry.Write(bw); } WriteMarker(bw, 0x04); bw.WriteInt32(0); WriteMarker(bw, 0x04); bw.WriteInt32(0); WriteMarker(bw, 0x04); bw.WriteInt32(0); int position = (int)bw.Position; bw.FillInt32("FileSize", position - fileStart); bw.FillInt32("DataSize", position - dataStart); bw.FillInt32("ParamSize", position - paramStart); }
internal override void Write(BinaryWriterEx bw) { bw.BigEndian = false; bw.WriteASCII("filt"); bw.WriteInt32(2); bw.WriteInt32(0); bw.WriteInt32(Groups.Count); bw.WriteInt32(Unk1); bw.WriteInt32(0x40); bw.ReserveInt32("GroupHeadersOffset"); bw.ReserveInt32("ParamHeaderOffsetsOffset"); bw.ReserveInt32("ParamHeadersOffset"); bw.ReserveInt32("ValuesOffset"); bw.ReserveInt32("UnkBlock1Offset"); bw.ReserveInt32("UnkBlock2Offset"); bw.WriteInt32(Unk2); bw.ReserveInt32("UnkBlock3Offset"); bw.ReserveInt32("UnkBlock4Offset"); bw.WriteInt32(0); for (int i = 0; i < Groups.Count; i++) { Groups[i].WriteHeaderOffset(bw, i); } int groupHeadersOffset = (int)bw.Position; bw.FillInt32("GroupHeadersOffset", groupHeadersOffset); for (int i = 0; i < Groups.Count; i++) { Groups[i].WriteHeader(bw, i, groupHeadersOffset); } int paramHeaderOffsetsOffset = (int)bw.Position; bw.FillInt32("ParamHeaderOffsetsOffset", paramHeaderOffsetsOffset); for (int i = 0; i < Groups.Count; i++) { Groups[i].WriteParamHeaderOffsets(bw, i, paramHeaderOffsetsOffset); } int paramHeadersOffset = (int)bw.Position; bw.FillInt32("ParamHeadersOffset", paramHeadersOffset); for (int i = 0; i < Groups.Count; i++) { Groups[i].WriteParamHeaders(bw, i, paramHeadersOffset); } int valuesOffset = (int)bw.Position; bw.FillInt32("ValuesOffset", valuesOffset); for (int i = 0; i < Groups.Count; i++) { Groups[i].WriteValues(bw, i, valuesOffset); } bw.FillInt32("UnkBlock1Offset", (int)bw.Position); bw.WriteBytes(UnkBlock1); bw.FillInt32("UnkBlock2Offset", (int)bw.Position); bw.WriteBytes(UnkBlock2); bw.FillInt32("UnkBlock3Offset", (int)bw.Position); bw.WriteBytes(UnkBlock3); bw.FillInt32("UnkBlock4Offset", (int)bw.Position); bw.WriteBytes(UnkBlock4); }
internal void WriteStrings(BinaryWriterEx bw, PARAMDEF def, int index, Dictionary <string, long> sharedStringOffsets) { if (def.FormatVersion >= 202) { bw.FillInt64($"DisplayNameOffset{index}", bw.Position); bw.WriteUTF16(DisplayName, true); } long descriptionOffset = 0; if (Description != null) { descriptionOffset = bw.Position; if (def.Unicode) { bw.WriteUTF16(Description, true); } else { bw.WriteShiftJIS(Description, true); } } if (def.FormatVersion >= 200) { bw.FillInt64($"DescriptionOffset{index}", descriptionOffset); } else { bw.FillInt32($"DescriptionOffset{index}", (int)descriptionOffset); } if (def.FormatVersion >= 202) { bw.FillInt64($"InternalTypeOffset{index}", bw.Position); bw.WriteASCII(InternalType, true); bw.FillInt64($"InternalNameOffset{index}", bw.Position); bw.WriteASCII(MakeInternalName(), true); } if (def.FormatVersion >= 200) { long writeSharedStringMaybe(string str, bool unicode) { if (str == null) { return(0); } if (!sharedStringOffsets.ContainsKey(str)) { sharedStringOffsets[str] = bw.Position; if (unicode) { bw.WriteUTF16(str, true); } else { bw.WriteASCII(str, true); } } return(sharedStringOffsets[str]); } bw.FillInt64($"UnkB8Offset{index}", writeSharedStringMaybe(UnkB8, false)); bw.FillInt64($"UnkC0Offset{index}", writeSharedStringMaybe(UnkC0, false)); bw.FillInt64($"UnkC8Offset{index}", writeSharedStringMaybe(UnkC8, true)); } }
internal override void Write(BinaryWriterEx bw) { bw.BigEndian = false; if (Game == GPGame.DarkSouls2) { bw.WriteASCII("filt"); } else { bw.WriteUTF16("filt"); } bw.WriteUInt32((uint)Game); bw.WriteByte(0); bw.WriteBoolean(Unk0D); bw.WriteInt16(0); bw.WriteInt32(Groups.Count); bw.WriteInt32(Unk14); bw.ReserveInt32("HeaderSize"); bw.ReserveInt32("GroupHeadersOffset"); bw.ReserveInt32("ParamHeaderOffsetsOffset"); bw.ReserveInt32("ParamHeadersOffset"); bw.ReserveInt32("ValuesOffset"); bw.ReserveInt32("ValueIDsOffset"); bw.ReserveInt32("UnkOffset2"); bw.WriteInt32(Unk3s.Count); bw.ReserveInt32("UnkOffset3"); bw.ReserveInt32("Unk3ValuesOffset"); bw.WriteInt32(0); if (Game == GPGame.DarkSouls3 || Game == GPGame.Sekiro) { bw.ReserveInt32("CommentOffsetsOffsetsOffset"); bw.ReserveInt32("CommentOffsetsOffset"); bw.ReserveInt32("CommentsOffset"); } if (Game == GPGame.Sekiro) { bw.WriteSingle(Unk50); } bw.FillInt32("HeaderSize", (int)bw.Position); for (int i = 0; i < Groups.Count; i++) { Groups[i].WriteHeaderOffset(bw, i); } int groupHeadersOffset = (int)bw.Position; bw.FillInt32("GroupHeadersOffset", groupHeadersOffset); for (int i = 0; i < Groups.Count; i++) { Groups[i].WriteHeader(bw, Game, i, groupHeadersOffset); } int paramHeaderOffsetsOffset = (int)bw.Position; bw.FillInt32("ParamHeaderOffsetsOffset", paramHeaderOffsetsOffset); for (int i = 0; i < Groups.Count; i++) { Groups[i].WriteParamHeaderOffsets(bw, i, paramHeaderOffsetsOffset); } int paramHeadersOffset = (int)bw.Position; bw.FillInt32("ParamHeadersOffset", paramHeadersOffset); for (int i = 0; i < Groups.Count; i++) { Groups[i].WriteParamHeaders(bw, Game, i, paramHeadersOffset); } int valuesOffset = (int)bw.Position; bw.FillInt32("ValuesOffset", valuesOffset); for (int i = 0; i < Groups.Count; i++) { Groups[i].WriteValues(bw, i, valuesOffset); } int valueIDsOffset = (int)bw.Position; bw.FillInt32("ValueIDsOffset", (int)bw.Position); for (int i = 0; i < Groups.Count; i++) { Groups[i].WriteValueIDs(bw, Game, i, valueIDsOffset); } bw.FillInt32("UnkOffset2", (int)bw.Position); bw.WriteBytes(UnkBlock2); bw.FillInt32("UnkOffset3", (int)bw.Position); for (int i = 0; i < Unk3s.Count; i++) { Unk3s[i].WriteHeader(bw, Game, i); } int unk3ValuesOffset = (int)bw.Position; bw.FillInt32("Unk3ValuesOffset", unk3ValuesOffset); for (int i = 0; i < Unk3s.Count; i++) { Unk3s[i].WriteValues(bw, Game, i, unk3ValuesOffset); } if (Game == GPGame.DarkSouls3 || Game == GPGame.Sekiro) { bw.FillInt32("CommentOffsetsOffsetsOffset", (int)bw.Position); for (int i = 0; i < Groups.Count; i++) { Groups[i].WriteCommentOffsetsOffset(bw, i); } int commentOffsetsOffset = (int)bw.Position; bw.FillInt32("CommentOffsetsOffset", commentOffsetsOffset); for (int i = 0; i < Groups.Count; i++) { Groups[i].WriteCommentOffsets(bw, i, commentOffsetsOffset); } int commentsOffset = (int)bw.Position; bw.FillInt32("CommentsOffset", commentsOffset); for (int i = 0; i < Groups.Count; i++) { Groups[i].WriteComments(bw, i, commentsOffset); } } }
protected override void Write(BinaryWriterEx bw) { bool bigEndian = Format == Game.DarkSouls1BE; bool is64Bit = Format >= Game.Bloodborne; bool unk06 = Format >= Game.DarkSouls3; bool unk07 = Format >= Game.Sekiro; int version = Format < Game.DarkSouls3 ? 0xCC : 0xCD; var layers = new List <uint>(); foreach (Event evt in Events) { foreach (Instruction inst in evt.Instructions) { if (inst.Layer.HasValue && !layers.Contains(inst.Layer.Value)) { layers.Add(inst.Layer.Value); } } } bw.WriteASCII("EVD\0"); bw.WriteBoolean(bigEndian); bw.WriteSByte((sbyte)(is64Bit ? -1 : 0)); bw.WriteBoolean(unk06); bw.WriteSByte((sbyte)(unk07 ? -1 : 0)); bw.BigEndian = bigEndian; bw.VarintLong = is64Bit; bw.WriteInt32(version); bw.ReserveInt32("FileSize"); Offsets offsets = default; bw.WriteVarint(Events.Count); bw.ReserveVarint("EventsOffset"); bw.WriteVarint(Events.Sum(e => e.Instructions.Count)); bw.ReserveVarint("InstructionsOffset"); bw.WriteVarint(0); bw.ReserveVarint("Offset3"); bw.WriteVarint(layers.Count); bw.ReserveVarint("LayersOffset"); bw.WriteVarint(Events.Sum(e => e.Parameters.Count)); bw.ReserveVarint("ParametersOffset"); bw.WriteVarint(LinkedFileOffsets.Count); bw.ReserveVarint("LinkedFilesOffset"); bw.ReserveVarint("ArgumentsLength"); bw.ReserveVarint("ArgumentsOffset"); bw.WriteVarint(StringData.Length); bw.ReserveVarint("StringsOffset"); if (!is64Bit) { bw.WriteInt32(0); } offsets.Events = bw.Position; bw.FillVarint("EventsOffset", bw.Position); for (int i = 0; i < Events.Count; i++) { Events[i].Write(bw, Format, i); } offsets.Instructions = bw.Position; bw.FillVarint("InstructionsOffset", bw.Position); for (int i = 0; i < Events.Count; i++) { Events[i].WriteInstructions(bw, Format, offsets, i); } bw.FillVarint("Offset3", bw.Position); offsets.Layers = bw.Position; bw.FillVarint("LayersOffset", bw.Position); var layerOffsets = new Dictionary <uint, long>(layers.Count); foreach (uint layer in layers) { layerOffsets[layer] = bw.Position - offsets.Layers; Layer.Write(bw, layer); } for (int i = 0; i < Events.Count; i++) { Event evt = Events[i]; for (int j = 0; j < evt.Instructions.Count; j++) { evt.Instructions[j].FillLayerOffset(bw, Format, i, j, layerOffsets); } } offsets.Arguments = bw.Position; bw.FillVarint("ArgumentsOffset", bw.Position); for (int i = 0; i < Events.Count; i++) { Event evt = Events[i]; for (int j = 0; j < evt.Instructions.Count; j++) { evt.Instructions[j].WriteArgs(bw, Format, offsets, i, j); } } if ((bw.Position - offsets.Arguments) % 0x10 > 0) { bw.WritePattern(0x10 - (int)(bw.Position - offsets.Arguments) % 0x10, 0x00); } bw.FillVarint("ArgumentsLength", bw.Position - offsets.Arguments); offsets.Parameters = bw.Position; bw.FillVarint("ParametersOffset", bw.Position); for (int i = 0; i < Events.Count; i++) { Events[i].WriteParameters(bw, Format, offsets, i); } offsets.LinkedFiles = bw.Position; bw.FillVarint("LinkedFilesOffset", bw.Position); foreach (long offset in LinkedFileOffsets) { bw.WriteVarint((int)offset); } offsets.Strings = bw.Position; bw.FillVarint("StringsOffset", bw.Position); bw.WriteBytes(StringData); bw.FillInt32("FileSize", (int)bw.Position); }
/// <summary> /// Serializes file data to a stream. /// </summary> protected override void Write(BinaryWriterEx bw) { bw.BigEndian = false; bw.WriteASCII(LongFormat ? "fsSL" : "fSSL"); bw.WriteInt32(1); bw.WriteInt32(DarkSoulsCount); bw.WriteInt32(DarkSoulsCount); bw.WriteInt32(0x54); bw.ReserveInt32("DataSize"); bw.WriteInt32(6); bw.WriteInt32(LongFormat ? 0x48 : 0x2C); bw.WriteInt32(1); bw.WriteInt32(LongFormat ? 0x20 : 0x10); bw.WriteInt32(StateGroups.Count); int stateSize = LongFormat ? 0x48 : 0x24; bw.WriteInt32(stateSize); bw.WriteInt32(StateGroups.Values.Sum(sg => sg.Count + (sg.Count == 1 ? 0 : 1))); bw.WriteInt32(LongFormat ? 0x38 : 0x1C); bw.ReserveInt32("ConditionCount"); bw.WriteInt32(LongFormat ? 0x18 : 0x10); bw.ReserveInt32("CommandCallCount"); bw.WriteInt32(LongFormat ? 0x10 : 0x8); bw.ReserveInt32("CommandArgCount"); bw.ReserveInt32("ConditionOffsetsOffset"); bw.ReserveInt32("ConditionOffsetsCount"); bw.ReserveInt32("NameBlockOffset"); bw.WriteInt32(Name == null ? 0 : Name.Length + 1); bw.ReserveInt32("UnkOffset1"); bw.WriteInt32(0); bw.ReserveInt32("UnkOffset2"); bw.WriteInt32(0); long dataStart = bw.Position; bw.WriteInt32(1); bw.WriteInt32(Unk70); bw.WriteInt32(Unk74); bw.WriteInt32(Unk78); bw.WriteInt32(Unk7C); if (LongFormat) { bw.WriteInt32(0); } ReserveVarint(bw, LongFormat, "StateGroupsOffset"); WriteVarint(bw, LongFormat, StateGroups.Count); ReserveVarint(bw, LongFormat, "NameOffset"); WriteVarint(bw, LongFormat, Name == null ? 0 : Name.Length + 1); long unkNull = DarkSoulsCount == 1 ? 0 : -1; WriteVarint(bw, LongFormat, unkNull); WriteVarint(bw, LongFormat, unkNull); // Collect and sort all the IDs so everything is definitely in the same order everywhere List <long> stateGroupIDs = StateGroups.Keys.ToList(); stateGroupIDs.Sort(); var stateIDs = new Dictionary <long, List <long> >(); foreach (long groupID in stateGroupIDs) { stateIDs[groupID] = StateGroups[groupID].Keys.ToList(); stateIDs[groupID].Sort(); } if (StateGroups.Count == 0) { FillVarint(bw, LongFormat, "StateGroupsOffset", -1); } else { FillVarint(bw, LongFormat, "StateGroupsOffset", bw.Position - dataStart); foreach (long groupID in stateGroupIDs) { WriteVarint(bw, LongFormat, groupID); ReserveVarint(bw, LongFormat, $"StateGroup{groupID}:StatesOffset1"); WriteVarint(bw, LongFormat, StateGroups[groupID].Count); ReserveVarint(bw, LongFormat, $"StateGroup{groupID}:StatesOffset2"); } } var stateOffsets = new Dictionary <long, Dictionary <long, long> >(); var weirdStateOffsets = new List <long[]>(); foreach (long groupID in stateGroupIDs) { stateOffsets[groupID] = new Dictionary <long, long>(); FillVarint(bw, LongFormat, $"StateGroup{groupID}:StatesOffset1", bw.Position - dataStart); FillVarint(bw, LongFormat, $"StateGroup{groupID}:StatesOffset2", bw.Position - dataStart); long firstStateOffset = bw.Position; foreach (long stateID in stateIDs[groupID]) { stateOffsets[groupID][stateID] = bw.Position - dataStart; StateGroups[groupID][stateID].WriteHeader(bw, LongFormat, groupID, stateID); } if (StateGroups[groupID].Count > 1) { weirdStateOffsets.Add(new long[] { firstStateOffset, bw.Position }); bw.Position += stateSize; } } // Make a list of every unique condition var conditions = new Dictionary <long, List <Condition> >(); foreach (long groupID in stateGroupIDs) { conditions[groupID] = new List <Condition>(); void addCondition(Condition cond) { if (!conditions[groupID].Any(c => ReferenceEquals(cond, c))) { conditions[groupID].Add(cond); foreach (Condition subCond in cond.Subconditions) { addCondition(subCond); } } } foreach (State state in StateGroups[groupID].Values) { foreach (Condition cond in state.Conditions) { addCondition(cond); } } } bw.FillInt32("ConditionCount", conditions.Values.Sum(group => group.Count)); // Yes, I do in fact want this to be keyed by reference var conditionOffsets = new Dictionary <Condition, long>(); foreach (long groupID in stateGroupIDs) { for (int i = 0; i < conditions[groupID].Count; i++) { Condition cond = conditions[groupID][i]; conditionOffsets[cond] = bw.Position - dataStart; cond.WriteHeader(bw, LongFormat, groupID, i, stateOffsets[groupID]); } } var commands = new List <CommandCall>(); foreach (long groupID in stateGroupIDs) { foreach (long stateID in stateIDs[groupID]) { StateGroups[groupID][stateID].WriteCommandCalls(bw, LongFormat, groupID, stateID, dataStart, commands); } for (int i = 0; i < conditions[groupID].Count; i++) { conditions[groupID][i].WriteCommandCalls(bw, LongFormat, groupID, i, dataStart, commands); } } bw.FillInt32("CommandCallCount", commands.Count); bw.FillInt32("CommandArgCount", commands.Sum(command => command.Arguments.Count)); for (int i = 0; i < commands.Count; i++) { commands[i].WriteArgs(bw, LongFormat, i, dataStart); } bw.FillInt32("ConditionOffsetsOffset", (int)(bw.Position - dataStart)); int conditionOffsetsCount = 0; foreach (long groupID in stateGroupIDs) { foreach (long stateID in stateIDs[groupID]) { conditionOffsetsCount += StateGroups[groupID][stateID].WriteConditionOffsets(bw, LongFormat, groupID, stateID, dataStart, conditionOffsets); } for (int i = 0; i < conditions[groupID].Count; i++) { conditionOffsetsCount += conditions[groupID][i].WriteConditionOffsets(bw, LongFormat, groupID, i, dataStart, conditionOffsets); } } bw.FillInt32("ConditionOffsetsCount", conditionOffsetsCount); foreach (long groupID in stateGroupIDs) { for (int i = 0; i < conditions[groupID].Count; i++) { conditions[groupID][i].WriteEvaluator(bw, LongFormat, groupID, i, dataStart); } } for (int i = 0; i < commands.Count; i++) { commands[i].WriteBytecode(bw, LongFormat, i, dataStart); } bw.FillInt32("NameBlockOffset", (int)(bw.Position - dataStart)); if (Name == null) { FillVarint(bw, LongFormat, "NameOffset", -1); } else { bw.Pad(2); FillVarint(bw, LongFormat, "NameOffset", bw.Position - dataStart); bw.WriteUTF16(Name, true); } bw.FillInt32("UnkOffset1", (int)(bw.Position - dataStart)); bw.FillInt32("UnkOffset2", (int)(bw.Position - dataStart)); bw.FillInt32("DataSize", (int)(bw.Position - dataStart)); if (DarkSoulsCount == 1) { bw.Pad(4); } else if (DarkSoulsCount == 2) { bw.Pad(0x10); } foreach (long[] offsets in weirdStateOffsets) { bw.Position = offsets[0]; byte[] bytes = new byte[stateSize]; bw.Stream.Read(bytes, 0, stateSize); bw.Position = offsets[1]; bw.WriteBytes(bytes); } }
// <summary> // Convert the internal format to PC tpf format // </summary> public void ConsoleToPC(TPFPlatform source) { // Need to create a DDS Header BinaryWriterEx bw = new BinaryWriterEx(false); bw.WriteASCII("DDS "); bw.WriteInt32(124); bw.WriteUInt32(659463); // Flags bw.WriteUInt32((uint)Header.Height); bw.WriteUInt32((uint)Header.Width); bw.WriteUInt32(((uint)Header.Width * (uint)Header.Height) / 2); // Dummy pitch size bw.WriteUInt32(1); // Depth if (source == TPFPlatform.PS3) { // DeS sometimes has mipmap count set to 0 :trashcat: if (Mipmaps == 0) { var dim = Math.Max(Header.Width, Header.Height); while (dim >= 1) { Mipmaps++; dim >>= 1; } } } bw.WriteUInt32(Mipmaps); for (int i = 0; i < 11; i++) { bw.WriteInt32(0); } // Pixel format bw.WriteInt32(32); bw.WriteInt32(4); // Flags (compressed) bool writeExtendedHeader = false; if (Header.DXGIFormat == 71 || Header.DXGIFormat == 72 || (source == TPFPlatform.PS3 && (Format == 0 || Format == 1))) { bw.WriteASCII("DXT1"); } else if (Header.DXGIFormat == 73 || Header.DXGIFormat == 74 || Header.DXGIFormat == 75 || (source == TPFPlatform.PS3 && (Format == 2 || Format == 3))) { bw.WriteASCII("DXT3"); } else if (Header.DXGIFormat == 76 || Header.DXGIFormat == 77 || Header.DXGIFormat == 78 || (source == TPFPlatform.PS3 && (Format == 4 || Format == 5))) { bw.WriteASCII("DXT5"); } else if (Header.DXGIFormat == 79 || Header.DXGIFormat == 80 || Header.DXGIFormat == 81) { bw.WriteASCII("ATI1"); } else if (Header.DXGIFormat == 82 || Header.DXGIFormat == 83 || Header.DXGIFormat == 84) { bw.WriteASCII("ATI2"); } else if (source == TPFPlatform.PS3 && (Format == 9 || Format == 10)) { bw.WriteASCII("DXT1"); Console.WriteLine("ARGB"); } else { bw.WriteASCII("DX10"); writeExtendedHeader = true; } bw.WriteInt32(0); bw.WriteInt32(0); bw.WriteInt32(0); bw.WriteInt32(0); bw.WriteInt32(0); bw.WriteUInt32(0x401008); // Caps if (Cubemap) { bw.WriteUInt32(0xFE00); } else { bw.WriteUInt32(0); } bw.WriteUInt32(0); bw.WriteUInt32(0); bw.WriteUInt32(0); // DX10 extended header if (writeExtendedHeader) { bw.WriteInt32(Header.DXGIFormat); bw.WriteUInt32(3); // 2D texture if (Cubemap) { bw.WriteUInt32(0x4); } else { bw.WriteUInt32(0); } bw.WriteUInt32(1); // Array Size bw.WriteUInt32(0); // Misc } // Next attempt to unswizzle the texture byte[] unswizzled = new byte[Bytes.Length]; for (int i = 0; i < unswizzled.Length; i++) { unswizzled[i] = Bytes[i]; } if (source == TPFPlatform.PS4) { uint blockSize = 16; if (Header.DXGIFormat == 71 || Header.DXGIFormat == 72 || Header.DXGIFormat == 79 || Header.DXGIFormat == 80 || Header.DXGIFormat == 81) { blockSize = 8; } int mipBase = 0; int mipBaseSrc = 0; for (int miplevel = 0; miplevel < Mipmaps; miplevel++) { uint bytesPerLine = Math.Max((uint)Header.Width >> miplevel, 1) * blockSize / 4; int heightBlock = Math.Max((Header.Height / 4) >> miplevel, 1); int widthBlock = Math.Max((Header.Width / 4) >> miplevel, 1); // Convert swizzled to linear strided int index = 0; for (int y = 0; y < heightBlock; y++) { for (int x = 0; x < widthBlock; x++) { int mx = x; int my = y; if (widthBlock > 1 && heightBlock > 1) { MapBlockPosition(x, y, widthBlock, 2, out mx, out my); } if (widthBlock > 2 && heightBlock > 2) { MapBlockPosition(mx, my, widthBlock, 4, out mx, out my); } if (widthBlock > 4 && heightBlock > 4) { MapBlockPosition(mx, my, widthBlock, 8, out mx, out my); } int destinationIndex = (int)blockSize * (my * widthBlock + mx); for (int i = 0; i < blockSize; i++) { unswizzled[mipBase + destinationIndex + i] = Bytes[mipBaseSrc + index]; index += 1; } } } mipBase += index; if (index < 512) { mipBaseSrc += 512; } else { mipBaseSrc += index; } } } // Append the rest of the original texture and update bw.WriteBytes(unswizzled); Bytes = bw.FinishBytes(); }
/// <summary> /// Serializes file data to a stream. /// </summary> protected override void Write(BinaryWriterEx bw) { if (AppliedParamdef == null) { throw new InvalidOperationException("Params cannot be written without applying a paramdef."); } bw.BigEndian = BigEndian; void WriteFormat() { bw.WriteByte((byte)(BigEndian ? 0xFF : 0x00)); bw.WriteByte(Format2D); bw.WriteByte(Format2E); bw.WriteByte(Format2F); } // DeS, DS1 if ((Format2D & 0x7F) < 3) { bw.ReserveUInt32("StringsOffset"); bw.ReserveUInt16("DataStart"); bw.WriteInt16(Unk06); bw.WriteInt16(Unk08); bw.WriteUInt16((ushort)Rows.Count); bw.WriteFixStr(ParamType, 0x20, (byte)((Format2D & 0x7F) < 2 ? 0x20 : 0x00)); WriteFormat(); } // DS2 else if ((Format2D & 0x7F) == 3) { bw.ReserveUInt32("StringsOffset"); bw.WriteInt16(0); bw.WriteInt16(Unk06); bw.WriteInt16(Unk08); bw.WriteUInt16((ushort)Rows.Count); bw.WriteFixStr(ParamType, 0x20, 0x20); WriteFormat(); bw.ReserveUInt32("DataStart"); bw.WriteInt32(0); bw.WriteInt32(0); bw.WriteInt32(0); } // SotFS, BB else if ((Format2D & 0x7F) == 4) { bw.ReserveUInt32("StringsOffset"); bw.WriteInt16(0); bw.WriteInt16(Unk06); bw.WriteInt16(Unk08); bw.WriteUInt16((ushort)Rows.Count); bw.WriteFixStr(ParamType, 0x20, 0x00); WriteFormat(); bw.ReserveInt64("DataStart"); bw.WriteInt64(0); } // DS3, SDT else { bw.ReserveUInt32("StringsOffset"); bw.WriteInt16(0); bw.WriteInt16(Unk06); bw.WriteInt16(Unk08); bw.WriteUInt16((ushort)Rows.Count); bw.WriteInt32(0); bw.ReserveInt64("IDOffset"); bw.WritePattern(0x14, 0x00); WriteFormat(); bw.ReserveInt64("DataStart"); bw.WriteInt64(0); } for (int i = 0; i < Rows.Count; i++) { Rows[i].WriteHeader(bw, Format2D, i); } if ((Format2D & 0x7F) < 2) { bw.WritePattern(0x20, 0x00); } if ((Format2D & 0x7F) < 3) { bw.FillUInt16("DataStart", (ushort)bw.Position); } else if ((Format2D & 0x7F) == 3) { bw.FillUInt32("DataStart", (uint)bw.Position); } else { bw.FillInt64("DataStart", bw.Position); } for (int i = 0; i < Rows.Count; i++) { Rows[i].WriteCells(bw, Format2D, i); } bw.FillUInt32("StringsOffset", (uint)bw.Position); if ((Format2D & 0x7F) > 4) { bw.FillInt64("IDOffset", bw.Position); bw.WriteASCII(ParamType, true); } for (int i = 0; i < Rows.Count; i++) { Rows[i].WriteName(bw, Format2D, Format2E, i); } // DeS and BB sometimes (but not always) include some useless padding here }
/// <summary> /// Writes FLVER data to a BinaryWriterEx. /// </summary> protected override void Write(BinaryWriterEx bw) { bw.BigEndian = Header.BigEndian; bw.WriteASCII("FLVER\0"); bw.WriteASCII(Header.BigEndian ? "B\0" : "L\0"); bw.WriteInt32(Header.Version); bw.ReserveInt32("DataOffset"); bw.ReserveInt32("DataSize"); bw.WriteInt32(Dummies.Count); bw.WriteInt32(Materials.Count); bw.WriteInt32(Bones.Count); bw.WriteInt32(Meshes.Count); bw.WriteInt32(Meshes.Sum(m => m.VertexBuffers.Count)); bw.WriteVector3(Header.BoundingBoxMin); bw.WriteVector3(Header.BoundingBoxMax); int trueFaceCount = 0; int totalFaceCount = 0; foreach (Mesh mesh in Meshes) { bool allowPrimitiveRestarts = mesh.Vertices.Length < ushort.MaxValue; foreach (FaceSet faceSet in mesh.FaceSets) { faceSet.AddFaceCounts(allowPrimitiveRestarts, ref trueFaceCount, ref totalFaceCount); } } bw.WriteInt32(trueFaceCount); bw.WriteInt32(totalFaceCount); byte vertexIndicesSize = 0; if (Header.Version < 0x20013) { vertexIndicesSize = 16; foreach (Mesh mesh in Meshes) { foreach (FaceSet fs in mesh.FaceSets) { vertexIndicesSize = (byte)Math.Max(vertexIndicesSize, fs.GetVertexIndexSize()); } } } bw.WriteByte(vertexIndicesSize); bw.WriteBoolean(Header.Unicode); bw.WriteBoolean(Header.Unk4A); bw.WriteByte(0); bw.WriteInt32(Header.Unk4C); bw.WriteInt32(Meshes.Sum(m => m.FaceSets.Count)); bw.WriteInt32(BufferLayouts.Count); bw.WriteInt32(Materials.Sum(m => m.Textures.Count)); bw.WriteByte(Header.Unk5C); bw.WriteByte(Header.Unk5D); bw.WriteByte(0); bw.WriteByte(0); bw.WriteInt32(0); bw.WriteInt32(0); bw.WriteInt32(Header.Unk68); bw.WriteInt32(0); bw.WriteInt32(0); bw.WriteInt32(0); bw.WriteInt32(0); bw.WriteInt32(0); foreach (FLVER.Dummy dummy in Dummies) { dummy.Write(bw, Header.Version); } for (int i = 0; i < Materials.Count; i++) { Materials[i].Write(bw, i); } for (int i = 0; i < Bones.Count; i++) { Bones[i].Write(bw, i); } for (int i = 0; i < Meshes.Count; i++) { Meshes[i].Write(bw, i); } int faceSetIndex = 0; foreach (Mesh mesh in Meshes) { for (int i = 0; i < mesh.FaceSets.Count; i++) { int indexSize = vertexIndicesSize; if (indexSize == 0) { indexSize = mesh.FaceSets[i].GetVertexIndexSize(); } mesh.FaceSets[i].Write(bw, Header, indexSize, faceSetIndex + i); } faceSetIndex += mesh.FaceSets.Count; } int vertexBufferIndex = 0; foreach (Mesh mesh in Meshes) { for (int i = 0; i < mesh.VertexBuffers.Count; i++) { mesh.VertexBuffers[i].Write(bw, Header, vertexBufferIndex + i, i, BufferLayouts, mesh.Vertices.Length); } vertexBufferIndex += mesh.VertexBuffers.Count; } for (int i = 0; i < BufferLayouts.Count; i++) { BufferLayouts[i].Write(bw, i); } int textureIndex = 0; for (int i = 0; i < Materials.Count; i++) { Materials[i].WriteTextures(bw, i, textureIndex); textureIndex += Materials[i].Textures.Count; } if (Header.Version >= 0x2001A) { SekiroUnk.Write(bw); } bw.Pad(0x10); for (int i = 0; i < BufferLayouts.Count; i++) { BufferLayouts[i].WriteMembers(bw, i); } bw.Pad(0x10); for (int i = 0; i < Meshes.Count; i++) { Meshes[i].WriteBoundingBox(bw, i, Header); } bw.Pad(0x10); int boneIndicesStart = (int)bw.Position; for (int i = 0; i < Meshes.Count; i++) { Meshes[i].WriteBoneIndices(bw, i, boneIndicesStart); } bw.Pad(0x10); faceSetIndex = 0; for (int i = 0; i < Meshes.Count; i++) { bw.FillInt32($"MeshFaceSetIndices{i}", (int)bw.Position); for (int j = 0; j < Meshes[i].FaceSets.Count; j++) { bw.WriteInt32(faceSetIndex + j); } faceSetIndex += Meshes[i].FaceSets.Count; } bw.Pad(0x10); vertexBufferIndex = 0; for (int i = 0; i < Meshes.Count; i++) { bw.FillInt32($"MeshVertexBufferIndices{i}", (int)bw.Position); for (int j = 0; j < Meshes[i].VertexBuffers.Count; j++) { bw.WriteInt32(vertexBufferIndex + j); } vertexBufferIndex += Meshes[i].VertexBuffers.Count; } bw.Pad(0x10); var gxOffsets = new List <int>(); foreach (GXList gxList in GXLists) { gxOffsets.Add((int)bw.Position); gxList.Write(bw, Header); } for (int i = 0; i < Materials.Count; i++) { Materials[i].FillGXOffset(bw, i, gxOffsets); } bw.Pad(0x10); textureIndex = 0; for (int i = 0; i < Materials.Count; i++) { Material material = Materials[i]; material.WriteStrings(bw, Header, i); for (int j = 0; j < material.Textures.Count; j++) { material.Textures[j].WriteStrings(bw, Header, textureIndex + j); } textureIndex += material.Textures.Count; } bw.Pad(0x10); for (int i = 0; i < Bones.Count; i++) { Bones[i].WriteStrings(bw, Header.Unicode, i); } int alignment = Header.Version <= 0x2000E ? 0x20 : 0x10; bw.Pad(alignment); if (Header.Version == 0x2000F || Header.Version == 0x20010) { bw.Pad(0x20); } int dataStart = (int)bw.Position; bw.FillInt32("DataOffset", dataStart); faceSetIndex = 0; vertexBufferIndex = 0; for (int i = 0; i < Meshes.Count; i++) { Mesh mesh = Meshes[i]; for (int j = 0; j < mesh.FaceSets.Count; j++) { int indexSize = vertexIndicesSize; if (indexSize == 0) { indexSize = mesh.FaceSets[j].GetVertexIndexSize(); } bw.Pad(alignment); mesh.FaceSets[j].WriteVertices(bw, indexSize, faceSetIndex + j, dataStart); } faceSetIndex += mesh.FaceSets.Count; foreach (FLVER.Vertex vertex in mesh.Vertices) { vertex.PrepareWrite(); } for (int j = 0; j < mesh.VertexBuffers.Count; j++) { bw.Pad(alignment); mesh.VertexBuffers[j].WriteBuffer(bw, vertexBufferIndex + j, BufferLayouts, mesh.Vertices, dataStart, Header); } foreach (FLVER.Vertex vertex in mesh.Vertices) { vertex.FinishWrite(); } vertexBufferIndex += mesh.VertexBuffers.Count; } bw.Pad(alignment); bw.FillInt32("DataSize", (int)bw.Position - dataStart); if (Header.Version == 0x2000F || Header.Version == 0x20010) { bw.Pad(0x20); } }
internal override void Write(BinaryWriterEx bw) { bw.WriteASCII("EVD\0"); if (Game == GameType.DS1) { bw.WriteUInt32(0); bw.WriteUInt32(0xCC); } else if (Game == GameType.BB) { bw.WriteUInt32(0xFF00); bw.WriteUInt32(0xCC); } else if (Game == GameType.DS3) { bw.WriteUInt32(0x1FF00); bw.WriteUInt32(0xCD); } void ReserveIntW(string name, bool?isWide = null) { if (isWide ?? Game != GameType.DS1) { bw.ReserveInt64(name); } else { bw.ReserveInt32(name); } } void FillIntW(string name, long value, bool?isWide = null) { if (isWide ?? Game != GameType.DS1) { bw.FillInt64(name, value); } else { bw.FillInt32(name, (int)value); } } void WriteIntW(long value, bool?isWide = null) { if (isWide ?? Game != GameType.DS1) { bw.WriteInt64(value); } else { bw.WriteInt32((int)value); } } if (Game == GameType.BB) { bw.ReserveInt64("FileLength"); } else { bw.ReserveInt32("FileLength"); } WriteIntW(Events.Count); ReserveIntW("EventsOffset"); ReserveIntW("InstructionsCount"); ReserveIntW("InstructionsOffset"); //Dummy count. Always empty. WriteIntW(0); // Same as EventLayersOffset because it's always empty. ReserveIntW("DummiesOffset"); ReserveIntW("EventLayersCount"); ReserveIntW("EventLayersOffset"); ReserveIntW("ParametersCount"); ReserveIntW("ParametersOffset"); WriteIntW(LinkedFileStringIndices.Count); ReserveIntW("LinkedFilesOffset"); ReserveIntW("ArgsBlockSize"); ReserveIntW("ArgsBlockOffset"); ReserveIntW("StringsBlockSize"); ReserveIntW("StringsBlockOffset"); if (Game == GameType.DS1) { bw.WriteInt32(0); } // Events FillIntW("EventsOffset", bw.Position); for (int i = 0; i < Events.Count; i++) { Events[i].Write(bw, Game, i); } // Instructions long instructionsOffset = bw.Position; FillIntW("InstructionsOffset", instructionsOffset); long instructionsCount = 0; for (int i = 0; i < Events.Count; i++) { FillIntW($"EventInstructionsOffset{i}", bw.Position - instructionsOffset); for (int j = 0; j < Events[i].Instructions.Count; j++) { Events[i].Instructions[j].Write(bw, Game, i, j); } instructionsCount += Events[i].Instructions.Count; } FillIntW("InstructionsCount", instructionsCount); // Dummies FillIntW("DummiesOffset", bw.Position); // EventLayers long eventLayersOffset = bw.Position; FillIntW("EventLayersOffset", eventLayersOffset); long eventLayersCount = 0; for (int i = 0; i < Events.Count; i++) { for (int j = 0; j < Events[i].Instructions.Count; j++) { if (Events[i].Instructions[j].Layer != null) { if (Game == GameType.DS3) { bw.FillInt64($"InstructionLayerOffset{i}:{j}", bw.Position - eventLayersOffset); } else { bw.FillInt32($"InstructionLayerOffset{i}:{j}", (int)(bw.Position - eventLayersOffset)); } Events[i].Instructions[j].Layer.Write(bw, Game); eventLayersCount++; } } } FillIntW("EventLayersCount", eventLayersCount); // Args long argsBlockOffset = bw.Position; FillIntW("ArgsBlockOffset", argsBlockOffset); for (int i = 0; i < Events.Count; i++) { for (int j = 0; j < Events[i].Instructions.Count; j++) { if (Events[i].Instructions[j].Args.Length > 0) { bw.FillInt32($"InstructionArgsOffset{i}:{j}", (int)(bw.Position - argsBlockOffset)); bw.WriteBytes(Events[i].Instructions[j].Args); //bw.Pad(4); } } } if (Game == GameType.DS1) { if ((bw.Position - argsBlockOffset) % 16 > 0) { bw.WritePattern(16 - (int)(bw.Position - argsBlockOffset) % 16, 0x00); } } else { bw.Pad(16); } FillIntW("ArgsBlockSize", bw.Position - argsBlockOffset); // Parameters long parametersOffset = bw.Position; FillIntW("ParametersOffset", parametersOffset); long parametersCount = 0; for (int i = 0; i < Events.Count; i++) { if (Events[i].Parameters.Count > 0) { if (Game == GameType.DS3) { bw.FillInt64($"EventParametersOffset{i}", bw.Position - parametersOffset); } else { bw.FillInt32($"EventParametersOffset{i}", (int)(bw.Position - parametersOffset)); } for (int j = 0; j < Events[i].Parameters.Count; j++) { Events[i].Parameters[j].Write(bw, Game); } parametersCount += Events[i].Parameters.Count; } } FillIntW("ParametersCount", parametersCount); // Linked Files FillIntW("LinkedFilesOffset", bw.Position); for (int i = 0; i < LinkedFileStringIndices.Count; i++) { ReserveIntW($"LinkedFileStringOffset{i}"); } // Strings FillIntW("StringsBlockOffset", bw.Position); long stringsStartOffset = bw.Position; List <long> stringTableOffsets = new List <long>(); for (int i = 0; i < StringTable.Count; i++) { stringTableOffsets.Add(bw.Position - stringsStartOffset); bw.WriteUTF16(StringTable[i], terminate: true); } FillIntW("StringsBlockSize", bw.Position - stringsStartOffset); // Linked Files - Second Pass for (int i = 0; i < LinkedFileStringIndices.Count; i++) { FillIntW($"LinkedFileStringOffset{i}", stringTableOffsets[LinkedFileStringIndices[i]]); } if (Game == GameType.BB) { bw.FillInt64("FileLength", bw.Position); } else { bw.FillInt32("FileLength", (int)bw.Position); } }
protected override void Write(BinaryWriterEx bw) { bw.WriteASCII("FXR\0"); bw.WriteInt16(0); bw.WriteUInt16((ushort)Version); bw.WriteInt32(1); bw.WriteInt32(ID); bw.ReserveInt32("Section1Offset"); bw.WriteInt32(1); bw.ReserveInt32("Section2Offset"); bw.WriteInt32(Section1Tree.Section2s.Count); bw.ReserveInt32("Section3Offset"); bw.ReserveInt32("Section3Count"); bw.ReserveInt32("Section4Offset"); bw.ReserveInt32("Section4Count"); bw.ReserveInt32("Section5Offset"); bw.ReserveInt32("Section5Count"); bw.ReserveInt32("Section6Offset"); bw.ReserveInt32("Section6Count"); bw.ReserveInt32("Section7Offset"); bw.ReserveInt32("Section7Count"); bw.ReserveInt32("Section8Offset"); bw.ReserveInt32("Section8Count"); bw.ReserveInt32("Section9Offset"); bw.ReserveInt32("Section9Count"); bw.ReserveInt32("Section10Offset"); bw.ReserveInt32("Section10Count"); bw.ReserveInt32("Section11Offset"); bw.ReserveInt32("Section11Count"); bw.WriteInt32(1); bw.WriteInt32(0); if (Version == FXRVersion.Sekiro) { bw.ReserveInt32("Section12Offset"); bw.WriteInt32(Section12s.Count); bw.ReserveInt32("Section13Offset"); bw.WriteInt32(Section13s.Count); bw.ReserveInt32("Section14Offset"); bw.WriteInt32(0); bw.WriteInt32(0); bw.WriteInt32(0); } bw.FillInt32("Section1Offset", (int)bw.Position); Section1Tree.Write(bw); bw.Pad(0x10); bw.FillInt32("Section2Offset", (int)bw.Position); Section1Tree.WriteSection2s(bw); bw.Pad(0x10); bw.FillInt32("Section3Offset", (int)bw.Position); List <Section2> section2s = Section1Tree.Section2s; var section3s = new List <Section3>(); for (int i = 0; i < section2s.Count; i++) { section2s[i].WriteSection3s(bw, i, section3s); } bw.FillInt32("Section3Count", section3s.Count); bw.Pad(0x10); bw.FillInt32("Section4Offset", (int)bw.Position); var section4s = new List <Section4>(); Section4Tree.Write(bw, section4s); Section4Tree.WriteSection4s(bw, section4s); bw.FillInt32("Section4Count", section4s.Count); bw.Pad(0x10); bw.FillInt32("Section5Offset", (int)bw.Position); int section5Count = 0; for (int i = 0; i < section4s.Count; i++) { section4s[i].WriteSection5s(bw, i, ref section5Count); } bw.FillInt32("Section5Count", section5Count); bw.Pad(0x10); bw.FillInt32("Section6Offset", (int)bw.Position); section5Count = 0; var section6s = new List <FFXDrawEntityHost>(); for (int i = 0; i < section4s.Count; i++) { section4s[i].WriteSection6s(bw, i, ref section5Count, section6s); } bw.FillInt32("Section6Count", section6s.Count); bw.Pad(0x10); bw.FillInt32("Section7Offset", (int)bw.Position); var section7s = new List <FFXProperty>(); for (int i = 0; i < section6s.Count; i++) { section6s[i].WriteSection7s(bw, i, section7s); } bw.FillInt32("Section7Count", section7s.Count); bw.Pad(0x10); bw.FillInt32("Section8Offset", (int)bw.Position); var section8s = new List <Section8>(); for (int i = 0; i < section7s.Count; i++) { section7s[i].WriteSection8s(bw, i, section8s); } bw.FillInt32("Section8Count", section8s.Count); bw.Pad(0x10); bw.FillInt32("Section9Offset", (int)bw.Position); var section9s = new List <Section9>(); for (int i = 0; i < section8s.Count; i++) { section8s[i].WriteSection9s(bw, i, section9s); } bw.FillInt32("Section9Count", section9s.Count); bw.Pad(0x10); bw.FillInt32("Section10Offset", (int)bw.Position); var section10s = new List <Section10>(); for (int i = 0; i < section6s.Count; i++) { section6s[i].WriteSection10s(bw, i, section10s); } bw.FillInt32("Section10Count", section10s.Count); bw.Pad(0x10); bw.FillInt32("Section11Offset", (int)bw.Position); int section11Count = 0; for (int i = 0; i < section3s.Count; i++) { section3s[i].WriteSection11s(bw, i, ref section11Count); } for (int i = 0; i < section6s.Count; i++) { section6s[i].WriteSection11s(bw, i, ref section11Count); } for (int i = 0; i < section7s.Count; i++) { section7s[i].WriteSection11s(bw, i, ref section11Count); } for (int i = 0; i < section8s.Count; i++) { section8s[i].WriteSection11s(bw, i, ref section11Count); } for (int i = 0; i < section9s.Count; i++) { section9s[i].WriteSection11s(bw, i, ref section11Count); } for (int i = 0; i < section10s.Count; i++) { section10s[i].WriteSection11s(bw, i, ref section11Count); } bw.FillInt32("Section11Count", section11Count); bw.Pad(0x10); if (Version == FXRVersion.Sekiro) { bw.FillInt32("Section12Offset", (int)bw.Position); bw.WriteInt32s(Section12s); bw.Pad(0x10); bw.FillInt32("Section13Offset", (int)bw.Position); bw.WriteInt32s(Section13s); bw.Pad(0x10); bw.FillInt32("Section14Offset", (int)bw.Position); } }
internal override void Write(BinaryWriterEx bw) { if (layout == null) { throw new InvalidOperationException("Params cannot be written without a layout."); } Rows.Sort((r1, r2) => r1.ID.CompareTo(r2.ID)); bw.BigEndian = BigEndian; void WriteFormat() { bw.WriteByte((byte)(BigEndian ? 0xFF : 0x00)); bw.WriteByte(Format2D); bw.WriteByte(Format2E); bw.WriteByte(Format2F); } // DeS, DS1 if ((Format2D & 0x7F) < 3) { bw.ReserveUInt32("StringsOffset"); bw.ReserveUInt16("DataStart"); bw.WriteInt16(Unk06); bw.WriteInt16(Unk08); bw.WriteUInt16((ushort)Rows.Count); bw.WriteFixStr(ID, 0x20, 0x20); WriteFormat(); } // DS2 else if ((Format2D & 0x7F) == 3) { bw.ReserveUInt32("StringsOffset"); bw.WriteInt16(0); bw.WriteInt16(Unk06); bw.WriteInt16(Unk08); bw.WriteUInt16((ushort)Rows.Count); bw.WriteFixStr(ID, 0x20, 0x20); WriteFormat(); bw.ReserveUInt32("DataStart"); bw.WriteInt32(0); bw.WriteInt32(0); bw.WriteInt32(0); } // SotFS, BB else if ((Format2D & 0x7F) == 4) { bw.ReserveUInt32("StringsOffset"); bw.WriteInt16(0); bw.WriteInt16(Unk06); bw.WriteInt16(Unk08); bw.WriteUInt16((ushort)Rows.Count); bw.WriteFixStr(ID, 0x20, 0x00); WriteFormat(); bw.ReserveInt64("DataStart"); bw.WriteInt64(0); } // DS3, SDT else { bw.ReserveUInt32("StringsOffset"); bw.WriteInt16(0); bw.WriteInt16(Unk06); bw.WriteInt16(Unk08); bw.WriteUInt16((ushort)Rows.Count); bw.WriteInt32(0); bw.ReserveInt64("IDOffset"); bw.WritePattern(0x14, 0x00); WriteFormat(); bw.ReserveInt64("DataStart"); bw.WriteInt64(0); } for (int i = 0; i < Rows.Count; i++) { Rows[i].WriteHeader(bw, Format2D, i); } if ((Format2D & 0x7F) < 3) { bw.FillUInt16("DataStart", (ushort)bw.Position); } else if ((Format2D & 0x7F) == 3) { bw.FillUInt32("DataStart", (uint)bw.Position); } else { bw.FillInt64("DataStart", bw.Position); } for (int i = 0; i < Rows.Count; i++) { Rows[i].WriteCells(bw, Format2D, i, layout); } bw.FillUInt32("StringsOffset", (uint)bw.Position); if ((Format2D & 0x7F) > 4) { bw.FillInt64("IDOffset", bw.Position); bw.WriteASCII(ID, true); } for (int i = 0; i < Rows.Count; i++) { Rows[i].WriteName(bw, Format2D, i); } }
protected override void Write(BinaryWriterEx bw) { bw.BigEndian = BigEndian; bw.VarintLong = Version == NGPVersion.Scholar; bw.WriteASCII("NVG2"); bw.WriteUInt16((ushort)Version); bw.WriteInt16(0); bw.WriteInt32(Meshes.Count); bw.WriteInt32(StructAs.Count); bw.WriteInt32(StructBs.Count); bw.WriteInt32(StructCs.Count); bw.WriteInt32(StructDs.Count); bw.WriteInt32(Unk1C); bw.ReserveVarint("OffsetA"); bw.ReserveVarint("OffsetB"); bw.ReserveVarint("OffsetC"); bw.ReserveVarint("OffsetD"); for (int i = 0; i < Meshes.Count; i++) { bw.ReserveVarint($"MeshOffset{i}"); } void writeMeshes() { for (int i = 0; i < Meshes.Count; i++) { bw.Pad(bw.VarintSize); bw.FillVarint($"MeshOffset{i}", bw.Position); Meshes[i].Write(bw, Version); } } if (Version == NGPVersion.Vanilla) { writeMeshes(); } bw.Pad(bw.VarintSize); bw.FillVarint("OffsetA", bw.Position); foreach (StructA structA in StructAs) { structA.Write(bw); } bw.Pad(bw.VarintSize); bw.FillVarint("OffsetB", bw.Position); foreach (StructB structB in StructBs) { structB.Write(bw); } bw.Pad(bw.VarintSize); bw.FillVarint("OffsetC", bw.Position); bw.WriteInt32s(StructCs); bw.Pad(bw.VarintSize); bw.FillVarint("OffsetD", bw.Position); bw.WriteInt16s(StructDs); if (Version == NGPVersion.Scholar) { writeMeshes(); } }
internal override void Write(BinaryWriterEx bw) { if (layout == null) { throw new InvalidOperationException("Params cannot be written without a layout."); } Rows.Sort((r1, r2) => r1.ID.CompareTo(r2.ID)); bw.BigEndian = false; bw.ReserveInt32("NameOffset"); bw.WriteInt16(0); bw.WriteInt16(Unk1); bw.WriteInt16(Unk2); bw.WriteUInt16((ushort)Rows.Count); if (FixStrID) { bw.WriteFixStr(ID, 0x20); } else { bw.WriteInt32(0); bw.ReserveInt32("IDOffset"); bw.WriteInt32(0); bw.WriteInt32(0); bw.WriteInt32(0); bw.WriteInt32(0); bw.WriteInt32(0); bw.WriteInt32(0); } bw.WriteByte(0); bw.WriteByte(Unk3); bw.WriteByte(Unk4); bw.WriteByte(0); bw.ReserveInt64("DataStart"); bw.WriteInt32(0); bw.WriteInt32(0); for (int i = 0; i < Rows.Count; i++) { Rows[i].WriteHeader(bw, i); } bw.FillInt64("DataStart", bw.Position); for (int i = 0; i < Rows.Count; i++) { Rows[i].WriteCells(bw, i, layout); } bw.FillInt32("NameOffset", (int)bw.Position); if (FixStrID) { if (Unk4 == 6) { bw.WriteShiftJIS(Name, true); } else if (Unk4 == 7) { bw.WriteUTF16(Name, true); } } else { bw.WriteShiftJIS(Name, true); bw.FillInt32("IDOffset", (int)bw.Position); bw.WriteASCII(ID, true); } for (int i = 0; i < Rows.Count; i++) { Rows[i].WriteName(bw, i, Unk4); } }
internal void Write(BinaryWriterEx bw, List <File> files) { bw.BigEndian = BigEndian; bw.WriteASCII("BHF4"); bw.WriteBoolean(Flag1); bw.WriteBoolean(Flag2); bw.WriteByte(0); bw.WriteByte(0); bw.WriteInt32(0x10000); bw.WriteInt32(files.Count); bw.WriteInt64(0x40); bw.WriteASCII(Timestamp.PadRight(8, '\0')); if (Format == 0x0C || Format == 0x30) { bw.WriteInt64(0x18); } else if (Format == 0x2E || Format == 0x74) { bw.WriteInt64(0x24); } else if (Format == 0x3E) { bw.WriteInt64(0x28); } bw.WriteInt64(0); bw.WriteBoolean(Unicode); bw.WriteByte(Format); bw.WriteByte(Extended); bw.WriteByte(0); bw.WriteInt32(0); if (Extended == 4) { bw.ReserveInt64("HashGroups"); } else { bw.WriteInt64(0); } for (int i = 0; i < files.Count; i++) { FileHeader.Write(bw, files[i], i, Format); } for (int i = 0; i < files.Count; i++) { File file = files[i]; bw.FillInt32($"FileName{i}", (int)bw.Position); if (Unicode) { bw.WriteUTF16(file.Name, true); } else { bw.WriteShiftJIS(file.Name, true); } } if (Extended == 4) { uint groupCount = 0; for (uint p = (uint)files.Count / 7; p <= 100000; p++) { if (Util.IsPrime(p)) { groupCount = p; break; } } if (groupCount == 0) { throw new InvalidOperationException("Hash group count not determined in BXF4."); } var hashLists = new List <PathHash> [groupCount]; for (int i = 0; i < groupCount; i++) { hashLists[i] = new List <PathHash>(); } for (int i = 0; i < files.Count; i++) { var pathHash = new PathHash(i, files[i].Name); uint group = pathHash.Hash % groupCount; hashLists[group].Add(pathHash); } for (int i = 0; i < groupCount; i++) { hashLists[i].Sort((ph1, ph2) => ph1.Hash.CompareTo(ph2.Hash)); } var hashGroups = new List <HashGroup>(); var pathHashes = new List <PathHash>(); int count = 0; foreach (List <PathHash> hashList in hashLists) { int index = count; foreach (PathHash pathHash in hashList) { pathHashes.Add(pathHash); count++; } hashGroups.Add(new HashGroup(index, count - index)); } bw.Pad(0x8); bw.FillInt64("HashGroups", bw.Position); bw.ReserveInt64("PathHashes"); bw.WriteUInt32(groupCount); bw.WriteInt32(0x00080810); foreach (HashGroup hashGroup in hashGroups) { hashGroup.Write(bw); } // No padding after section 1 bw.FillInt64("PathHashes", bw.Position); foreach (PathHash pathHash in pathHashes) { pathHash.Write(bw); } } }