/// <summary> /// Deserializes file data from a stream. /// </summary> protected override void Read(BinaryReaderEx br) { br.Position = 0x2C; br.BigEndian = BigEndian = br.AssertByte(0, 0xFF) == 0xFF; Format2D = (FormatFlags1)br.ReadByte(); Format2E = (FormatFlags2)br.ReadByte(); ParamdefFormatVersion = br.ReadByte(); br.Position = 0; // Make a private copy of the file to read row data from later byte[] copy = br.GetBytes(0, (int)br.Stream.Length); RowReader = new BinaryReaderEx(BigEndian, copy); // The strings offset in the header is highly unreliable; only use it as a last resort long actualStringsOffset = 0; long stringsOffset = br.ReadUInt32(); if (Format2D.HasFlag(FormatFlags1.Flag01) && Format2D.HasFlag(FormatFlags1.IntDataOffset) || Format2D.HasFlag(FormatFlags1.LongDataOffset)) { br.AssertInt16(0); } else { br.ReadUInt16(); // Data start } Unk06 = br.ReadInt16(); ParamdefDataVersion = br.ReadInt16(); ushort rowCount = br.ReadUInt16(); if (Format2D.HasFlag(FormatFlags1.OffsetParamType)) { br.AssertInt32(0); long paramTypeOffset = br.ReadInt64(); br.AssertPattern(0x14, 0x00); ParamType = br.GetASCII(paramTypeOffset); actualStringsOffset = paramTypeOffset; } else { ParamType = br.ReadFixStr(0x20); } br.Skip(4); // Format if (Format2D.HasFlag(FormatFlags1.Flag01) && Format2D.HasFlag(FormatFlags1.IntDataOffset)) { br.ReadInt32(); // Data start br.AssertInt32(0); br.AssertInt32(0); br.AssertInt32(0); } else if (Format2D.HasFlag(FormatFlags1.LongDataOffset)) { br.ReadInt64(); // Data start br.AssertInt64(0); } Rows = new List <Row>(rowCount); for (int i = 0; i < rowCount; i++) { Rows.Add(new Row(br, this, ref actualStringsOffset)); } if (Rows.Count > 1) { DetectedSize = Rows[1].DataOffset - Rows[0].DataOffset; } else if (Rows.Count == 1) { DetectedSize = (actualStringsOffset == 0 ? stringsOffset : actualStringsOffset) - Rows[0].DataOffset; } else { DetectedSize = -1; } }
/// <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 }