using System; using System.Collections.Generic; using System.IO; using System.Text; using FSO.Files.FAR3; using System.Globalization; namespace FSO.Files { public struct TuningEntry { public string EntryName; public uint KeyValueCount; public Dictionary KeyValues; public override string ToString() { return EntryName; } public float GetNum(string key) { CultureInfo floatParse = CultureInfo.InvariantCulture; return float.Parse(KeyValues[key], floatParse); } public static TuningEntry DEFAULT = new TuningEntry { EntryName = "", KeyValueCount = 0, KeyValues = new Dictionary() }; } //NOTE: important tuning variables: //28: object wear repair - important because this stuff doen't run in simantics anymore //36: sim motive decay //24: object deprecation //21: "motivesTunning": regen motives when not online, dead makes motives drain faster... //17: motive score weights for different lot types /// /// Loader/parser for tuning.dat. /// public class Tuning { private BinaryReader m_Reader; public uint EntryCount = 0; public Dictionary EntriesByName = new Dictionary(); public Tuning(byte[] Data) { m_Reader = new BinaryReader(new MemoryStream(Data)); Create(m_Reader); } public Tuning(string Path) { try { m_Reader = new BinaryReader(File.OpenRead(Path)); } catch(Exception) { throw new Exception("Tuning.cs: Invalid path: "+Path); } Create(m_Reader); } private void Create(BinaryReader Reader) { byte BodyType = m_Reader.ReadByte(); if (BodyType != 0x01 && BodyType != 0x03) throw (new Exception("Tuning.cs: Unknown Persist BodyType!")); uint DecompressedSize = m_Reader.ReadUInt32(); uint CompressedSize = m_Reader.ReadUInt32(); uint StreamBodySize = m_Reader.ReadUInt32(); //same as compressed size for all current examples //Note: wiki.niotso.org says this is actually one byte Compressor and four bytes CompressionParameters. ushort CompressionID = m_Reader.ReadUInt16(); if (CompressionID != 0xFB10) throw (new Exception("Tuning.cs: Unknown CompressionID!")); byte[] Dummy = m_Reader.ReadBytes(3); uint DecompressedSize2 = (uint)((Dummy[0] << 0x10) | (Dummy[1] << 0x08) | +Dummy[2]); Decompresser Dec = new Decompresser(); Dec.CompressedSize = CompressedSize; Dec.DecompressedSize = DecompressedSize; var decompressedData = Dec.Decompress(m_Reader.ReadBytes((int)CompressedSize)); if (decompressedData == null) throw (new Exception("Tuning.cs: Decompression failed!")); m_Reader = new BinaryReader(new MemoryStream(decompressedData)); EntryCount = m_Reader.ReadUInt32(); for (int i = 0; i < EntryCount; i++) { TuningEntry Entry = new TuningEntry(); Entry.EntryName = DecodeString(m_Reader); Entry.KeyValueCount = m_Reader.ReadUInt32(); Entry.KeyValues = new Dictionary(); for (int j = 0; j < Entry.KeyValueCount; j++) { string Key = DecodeString(m_Reader); string Val = DecodeString(m_Reader); Entry.KeyValues.Add(Key, Val); } EntriesByName.Add(Entry.EntryName.ToLowerInvariant(), Entry); } } /// /// Reads a variable-length pascal string offset by 13 characters. /// /// A decoded string. private string DecodeString(BinaryReader reader) { int length = 0; byte last = reader.ReadByte(); byte j = 0; while ((last & 0x80) > 0) { length |= (last&0x7F) << ((j++) * 7); last = reader.ReadByte(); } length |= last << ((j++) * 7); byte[] Chars = reader.ReadBytes(length); for(int i = 0; i < Chars.Length; i++) Chars[i] = (byte)(Chars[i] - 13); return Encoding.ASCII.GetString(Chars); } } }