public static void Serialize(PARAMDEF def, XmlWriter xw, int xmlVersion) { if (xmlVersion < 0 || xmlVersion > CURRENT_XML_VERSION) { throw new InvalidOperationException($"XML version {xmlVersion} not recognized."); } xw.WriteStartDocument(); xw.WriteStartElement("PARAMDEF"); xw.WriteAttributeString("XmlVersion", xmlVersion.ToString()); xw.WriteElementString("ParamType", def.ParamType); xw.WriteElementString(xmlVersion == 0 ? "Unk06" : "DataVersion", def.DataVersion.ToString()); xw.WriteElementString("BigEndian", def.BigEndian.ToString()); xw.WriteElementString("Unicode", def.Unicode.ToString()); xw.WriteElementString(xmlVersion == 0 ? "Version" : "FormatVersion", def.FormatVersion.ToString()); xw.WriteStartElement("Fields"); foreach (Field field in def.Fields) { xw.WriteStartElement("Field"); SerializeField(field, xw); xw.WriteEndElement(); } xw.WriteEndElement(); xw.WriteEndElement(); }
internal void WriteDescription(BinaryWriterEx bw, PARAMDEF def, int index) { long descriptionOffset = 0; if (Description != null) { descriptionOffset = bw.Position; if (def.Unicode) { bw.WriteUTF16(Description, true); } else { bw.WriteShiftJIS(Description, true); } } if (def.Version >= 201) { bw.FillInt64($"DescriptionOffset{index}", descriptionOffset); } else { bw.FillInt32($"DescriptionOffset{index}", (int)descriptionOffset); } }
internal Field(BinaryReaderEx br, PARAMDEF def) { if (def.Unicode) { DisplayName = br.ReadFixStrW(0x40); } else { DisplayName = br.ReadFixStr(0x40); } DisplayType = (DefType)Enum.Parse(typeof(DefType), br.ReadFixStr(8)); DisplayFormat = br.ReadFixStr(8); Default = br.ReadSingle(); Minimum = br.ReadSingle(); Maximum = br.ReadSingle(); Increment = br.ReadSingle(); EditFlags = (EditFlags)br.ReadInt32(); ByteCount = br.ReadInt32(); long descriptionOffset; if (def.Version >= 201) { descriptionOffset = br.ReadInt64(); } else { descriptionOffset = br.ReadInt32(); } InternalType = br.ReadFixStr(0x20); if (def.Version >= 102) { InternalName = br.ReadFixStr(0x20); } if (def.Version >= 104) { SortID = br.ReadInt32(); } if (def.Version >= 201) { br.AssertPattern(0x1C, 0x00); } if (descriptionOffset != 0) { if (def.Unicode) { Description = br.GetUTF16(descriptionOffset); } else { Description = br.GetShiftJIS(descriptionOffset); } } }
internal void Write(BinaryWriterEx bw, PARAMDEF def, int index) { if (def.Unicode) { bw.WriteFixStrW(DisplayName, 0x40, (byte)(def.Version >= 104 ? 0x00 : 0x20)); } else { bw.WriteFixStr(DisplayName, 0x40, (byte)(def.Version >= 104 ? 0x00 : 0x20)); } byte padding = (byte)(def.Version >= 201 ? 0x00 : 0x20); bw.WriteFixStr(DisplayType.ToString(), 8, padding); bw.WriteFixStr(DisplayFormat, 8, padding); bw.WriteSingle(Default); bw.WriteSingle(Minimum); bw.WriteSingle(Maximum); bw.WriteSingle(Increment); bw.WriteInt32((int)EditFlags); bw.WriteInt32(ParamUtil.GetValueSize(DisplayType) * (ParamUtil.IsArrayType(DisplayType) ? ArrayLength : 1)); if (def.Version >= 201) { bw.ReserveInt64($"DescriptionOffset{index}"); } else { bw.ReserveInt32($"DescriptionOffset{index}"); } bw.WriteFixStr(InternalType, 0x20, padding); if (def.Version >= 102) { string internalName = InternalName; // This is accurate except for "hasTarget : 1" in SpEffect if (BitSize != -1) { internalName = $"{internalName}:{BitSize}"; } // BB is not consistent about including [1] or not, but PTDE always does else if (ParamUtil.IsArrayType(DisplayType)) { internalName = $"{internalName}[{ArrayLength}]"; } bw.WriteFixStr(internalName, 0x20, padding); } if (def.Version >= 104) { bw.WriteInt32(SortID); } if (def.Version >= 201) { bw.WritePattern(0x1C, 0x00); } }
/// <summary> /// Interprets row data according to the given paramdef and stores it for later writing. /// </summary> public void ApplyParamdef(PARAMDEF paramdef) { AppliedParamdef = paramdef; foreach (Row row in Rows) { row.ReadCells(RowReader, AppliedParamdef); } }
/// <summary> /// Applies a paramdef only if its param type, data version, and row size match this param's. Returns true if applied. /// </summary> public bool ApplyParamdefCarefully(PARAMDEF paramdef) { if (ParamType == paramdef.ParamType && ParamdefDataVersion == paramdef.DataVersion && (DetectedSize == -1 || DetectedSize == paramdef.GetRowSize())) { ApplyParamdef(paramdef); return(true); } return(false); }
/// <summary> /// Creates a new row based on the given paramdef with default values. /// </summary> public Row(long id, string name, PARAMDEF paramdef) { ID = id; Name = name; var cells = new Cell[paramdef.Fields.Count]; for (int i = 0; i < paramdef.Fields.Count; i++) { PARAMDEF.Field field = paramdef.Fields[i]; object value = ParamUtil.CastDefaultValue(field); cells[i] = new Cell(field, value); } Cells = cells; }
internal void Write(BinaryWriterEx bw, PARAMDEF def, int index) { if (def.Unicode) { bw.WriteFixStrW(DisplayName, 0x40, (byte)(def.Version >= 104 ? 0x00 : 0x20)); } else { bw.WriteFixStr(DisplayName, 0x40, (byte)(def.Version >= 104 ? 0x00 : 0x20)); } byte padding = (byte)(def.Version >= 201 ? 0x00 : 0x20); bw.WriteFixStr(DisplayType.ToString(), 8, padding); bw.WriteFixStr(DisplayFormat, 8, padding); bw.WriteSingle(Default); bw.WriteSingle(Minimum); bw.WriteSingle(Maximum); bw.WriteSingle(Increment); bw.WriteInt32((int)EditFlags); bw.WriteInt32(ByteCount); if (def.Version >= 201) { bw.ReserveInt64($"DescriptionOffset{index}"); } else { bw.ReserveInt32($"DescriptionOffset{index}"); } bw.WriteFixStr(InternalType, 0x20, padding); if (def.Version >= 102) { bw.WriteFixStr(InternalName, 0x20, padding); } if (def.Version >= 104) { bw.WriteInt32(SortID); } if (def.Version >= 201) { bw.WritePattern(0x1C, 0x00); } }
public static PARAMDEF Deserialize(XmlDocument xml) { var def = new PARAMDEF(); XmlNode root = xml.SelectSingleNode("PARAMDEF"); // In the interest of maximum compatibility, we will no longer check the XML version; // just try everything and hope it works. def.ParamType = root.SelectSingleNode("ParamType").InnerText; def.DataVersion = root.ReadInt16IfExist("DataVersion") ?? root.ReadInt16("Unk06"); def.BigEndian = root.ReadBoolean("BigEndian"); def.Unicode = root.ReadBoolean("Unicode"); def.FormatVersion = root.ReadInt16IfExist("FormatVersion") ?? root.ReadInt16("Version"); def.Fields = new List <Field>(); foreach (XmlNode node in root.SelectNodes("Fields/Field")) { def.Fields.Add(DeserializeField(node)); } return(def); }
internal void ReadCells(BinaryReaderEx br, PARAMDEF paramdef) { // In case someone decides to add new rows before applying the paramdef (please don't do that) if (DataOffset == 0) { return; } Def = paramdef; br.Position = DataOffset; var cells = new Cell[paramdef.Fields.Count]; int bitOffset = -1; PARAMDEF.DefType bitType = PARAMDEF.DefType.u8; uint bitValue = 0; for (int i = 0; i < paramdef.Fields.Count; i++) { PARAMDEF.Field field = paramdef.Fields[i]; object value = null; PARAMDEF.DefType type = field.DisplayType; if (type == PARAMDEF.DefType.s8) { value = br.ReadSByte(); } else if (type == PARAMDEF.DefType.s16) { value = br.ReadInt16(); } else if (type == PARAMDEF.DefType.s32) { value = br.ReadInt32(); } else if (type == PARAMDEF.DefType.f32) { value = br.ReadSingle(); } else if (type == PARAMDEF.DefType.fixstr) { value = br.ReadFixStr(field.ArrayLength); } else if (type == PARAMDEF.DefType.fixstrW) { value = br.ReadFixStrW(field.ArrayLength * 2); } else if (ParamUtil.IsBitType(type)) { if (field.BitSize == -1) { if (type == PARAMDEF.DefType.u8) { value = br.ReadByte(); } else if (type == PARAMDEF.DefType.u16) { value = br.ReadUInt16(); } else if (type == PARAMDEF.DefType.u32) { value = br.ReadUInt32(); } else if (type == PARAMDEF.DefType.dummy8) { value = br.ReadBytes(field.ArrayLength); } } } else { throw new NotImplementedException($"Unsupported field type: {type}"); } if (value != null) { bitOffset = -1; } else { PARAMDEF.DefType newBitType = type == PARAMDEF.DefType.dummy8 ? PARAMDEF.DefType.u8 : type; int bitLimit = ParamUtil.GetBitLimit(newBitType); if (field.BitSize == 0) { throw new NotImplementedException($"Bit size 0 is not supported."); } if (field.BitSize > bitLimit) { throw new InvalidDataException($"Bit size {field.BitSize} is too large to fit in type {newBitType}."); } if (bitOffset == -1 || newBitType != bitType || bitOffset + field.BitSize > bitLimit) { bitOffset = 0; bitType = newBitType; if (bitType == PARAMDEF.DefType.u8) { bitValue = br.ReadByte(); } else if (bitType == PARAMDEF.DefType.u16) { bitValue = br.ReadUInt16(); } else if (bitType == PARAMDEF.DefType.u32) { bitValue = br.ReadUInt32(); } } uint shifted = bitValue << (32 - field.BitSize - bitOffset) >> (32 - field.BitSize); bitOffset += field.BitSize; if (bitType == PARAMDEF.DefType.u8) { value = (byte)shifted; } else if (bitType == PARAMDEF.DefType.u16) { value = (ushort)shifted; } else if (bitType == PARAMDEF.DefType.u32) { value = shifted; } } cells[i] = new Cell(field, value); } Cells = cells; }
/// <summary> /// Converts the layout to a paramdef with the given type, and all child enums to paramtdfs. /// </summary> public PARAMDEF ToParamdef(string paramType, out List <PARAMTDF> paramtdfs) { paramtdfs = new List <PARAMTDF>(Enums.Count); foreach (string enumName in Enums.Keys) { paramtdfs.Add(Enums[enumName].ToParamtdf(enumName)); } var def = new PARAMDEF { ParamType = paramType, Unicode = true, FormatVersion = 201 }; foreach (Entry entry in this) { PARAMDEF.DefType fieldType; switch (entry.Type) { case CellType.dummy8: fieldType = PARAMDEF.DefType.dummy8; break; case CellType.b8: case CellType.u8: case CellType.x8: fieldType = PARAMDEF.DefType.u8; break; case CellType.s8: fieldType = PARAMDEF.DefType.s8; break; case CellType.b16: case CellType.u16: case CellType.x16: fieldType = PARAMDEF.DefType.u16; break; case CellType.s16: fieldType = PARAMDEF.DefType.s16; break; case CellType.b32: case CellType.u32: case CellType.x32: fieldType = PARAMDEF.DefType.u32; break; case CellType.s32: fieldType = PARAMDEF.DefType.s32; break; case CellType.f32: fieldType = PARAMDEF.DefType.f32; break; case CellType.fixstr: fieldType = PARAMDEF.DefType.fixstr; break; case CellType.fixstrW: fieldType = PARAMDEF.DefType.fixstrW; break; default: throw new NotImplementedException($"DefType not specified for CellType {entry.Type}."); } var field = new PARAMDEF.Field(fieldType, entry.Name); field.Description = entry.Description; if (entry.Enum != null) { field.InternalType = entry.Enum; } if (entry.Type == CellType.s8) { field.Default = (sbyte)entry.Default; } else if (entry.Type == CellType.u8 || entry.Type == CellType.x8) { field.Default = (byte)entry.Default; } else if (entry.Type == CellType.s16) { field.Default = (short)entry.Default; } else if (entry.Type == CellType.u16 || entry.Type == CellType.x16) { field.Default = (ushort)entry.Default; } else if (entry.Type == CellType.s32) { field.Default = (int)entry.Default; } else if (entry.Type == CellType.u32 || entry.Type == CellType.x32) { field.Default = (uint)entry.Default; } else if (entry.Type == CellType.dummy8 || entry.Type == CellType.fixstr) { field.ArrayLength = entry.Size; } else if (entry.Type == CellType.fixstrW) { field.ArrayLength = entry.Size / 2; } else if (entry.Type == CellType.b8 || entry.Type == CellType.b16 || entry.Type == CellType.b32) { field.Default = (bool)entry.Default ? 1 : 0; field.BitSize = 1; } def.Fields.Add(field); } return(def); }
internal Field(BinaryReaderEx br, PARAMDEF def) { Parent = def; if (def.Unicode) { DisplayName = br.ReadFixStrW(0x40); } else { DisplayName = br.ReadFixStr(0x40); } DisplayType = (DefType)Enum.Parse(typeof(DefType), br.ReadFixStr(8)); DisplayFormat = br.ReadFixStr(8); Default = br.ReadSingle(); Minimum = br.ReadSingle(); Maximum = br.ReadSingle(); Increment = br.ReadSingle(); EditFlags = (EditFlags)br.ReadInt32(); int byteCount = br.ReadInt32(); if (!ParamUtil.IsArrayType(DisplayType) && byteCount != ParamUtil.GetValueSize(DisplayType) || ParamUtil.IsArrayType(DisplayType) && byteCount % ParamUtil.GetValueSize(DisplayType) != 0) { throw new InvalidDataException($"Unexpected byte count {byteCount} for type {DisplayType}."); } ArrayLength = byteCount / ParamUtil.GetValueSize(DisplayType); long descriptionOffset; if (def.Version >= 201) { descriptionOffset = br.ReadInt64(); } else { descriptionOffset = br.ReadInt32(); } InternalType = br.ReadFixStr(0x20); BitSize = -1; if (def.Version >= 102) { // A few fields in DS1 FaceGenParam have a trailing space in the name InternalName = br.ReadFixStr(0x20).Trim(); Match match = bitSizeRx.Match(InternalName); if (match.Success) { InternalName = match.Groups["name"].Value; BitSize = int.Parse(match.Groups["size"].Value); } if (ParamUtil.IsArrayType(DisplayType)) { match = arrayLengthRx.Match(InternalName); int length = match.Success ? int.Parse(match.Groups["length"].Value) : 1; if (length != ArrayLength) { throw new InvalidDataException($"Mismatched array length in {InternalName} with byte count {byteCount}."); } if (match.Success) { InternalName = match.Groups["name"].Value; } } } if (def.Version >= 104) { SortID = br.ReadInt32(); } if (def.Version >= 201) { br.AssertPattern(0x1C, 0x00); } if (descriptionOffset != 0) { if (def.Unicode) { Description = br.GetUTF16(descriptionOffset); } else { Description = br.GetShiftJIS(descriptionOffset); } } }
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 void Write(BinaryWriterEx bw, PARAMDEF def, int index) { if (def.FormatVersion >= 202) { bw.ReserveInt64($"DisplayNameOffset{index}"); } else if (def.Unicode) { bw.WriteFixStrW(DisplayName, 0x40, (byte)(def.FormatVersion >= 104 ? 0x00 : 0x20)); } else { bw.WriteFixStr(DisplayName, 0x40, (byte)(def.FormatVersion >= 104 ? 0x00 : 0x20)); } byte padding = (byte)(def.FormatVersion >= 200 ? 0x00 : 0x20); bw.WriteFixStr(DisplayType.ToString(), 8, padding); bw.WriteFixStr(DisplayFormat, 8, padding); bw.WriteSingle(Default); bw.WriteSingle(Minimum); bw.WriteSingle(Maximum); bw.WriteSingle(Increment); bw.WriteInt32((int)EditFlags); bw.WriteInt32(ParamUtil.GetValueSize(DisplayType) * (ParamUtil.IsArrayType(DisplayType) ? ArrayLength : 1)); if (def.FormatVersion >= 200) { bw.ReserveInt64($"DescriptionOffset{index}"); } else { bw.ReserveInt32($"DescriptionOffset{index}"); } if (def.FormatVersion >= 202) { bw.ReserveInt64($"InternalTypeOffset{index}"); } else { bw.WriteFixStr(InternalType, 0x20, padding); } if (def.FormatVersion >= 202) { bw.ReserveInt64($"InternalNameOffset{index}"); } else if (def.FormatVersion >= 102) { bw.WriteFixStr(MakeInternalName(), 0x20, padding); } if (def.FormatVersion >= 104) { bw.WriteInt32(SortID); } if (def.FormatVersion >= 200) { bw.WriteInt32(0); bw.ReserveInt64($"UnkB8Offset{index}"); bw.ReserveInt64($"UnkC0Offset{index}"); bw.ReserveInt64($"UnkC8Offset{index}"); } }