// Reads all records in SaveTree // Reader must be positioned at start of first RecordElement void ReadRecords(BinaryReader reader) { RecordDictionary.Clear(); RootRecord = new SaveTreeBaseRecord(); while (reader.BaseStream.Position < reader.BaseStream.Length) { // Read record length and skip empty records as they have no data int length = reader.ReadInt32(); if (length <= 0) { continue; } // Handle potential stream overflow (e.g. corrupt save, something went wrong) if (reader.BaseStream.Position + length >= reader.BaseStream.Length) { break; } // Peek record type from RecordRoot so we can instantiate record class based on type RecordTypes type = PeekRecordType(reader); // Add record based on type SaveTreeBaseRecord record; switch (type) { case RecordTypes.Item: record = new ItemRecord(reader, length); break; case RecordTypes.Character: record = new CharacterRecord(reader, length); break; case RecordTypes.Spell: record = new SpellRecord(reader, length); break; case RecordTypes.Container: record = new ContainerRecord(reader, length); break; //case RecordTypes.Door: // record = new SaveTreeBaseRecord(reader, length); // Read then skip these records for now // continue; //case RecordTypes.DungeonData: // record = new SaveTreeBaseRecord(reader, length); // Read then skip these records for now // continue; default: record = new SaveTreeBaseRecord(reader, length); break; } AddRecord(record); } LinkChildren(); }
/// <summary> /// Finds first record of type in tree starting from root record. /// </summary> /// <param name="type">Type of record to search for.</param> /// <param name="root">Root record to start searching from. If null, will start from RecordRoot.</param> /// <returns>Found item or null if not found.</returns> public SaveTreeBaseRecord FindRecord(RecordTypes type, SaveTreeBaseRecord root = null) { List <SaveTreeBaseRecord> records = FindRecords(type, root); if (records.Count == 0) { return(null); } return(records[0]); }
// Recursively search for record type void FindRecordsByType(RecordTypes type, SaveTreeBaseRecord parent, List <SaveTreeBaseRecord> recordList) { if (parent.RecordType == type) { recordList.Add(parent); } for (int i = 0; i < parent.Children.Count; i++) { FindRecordsByType(type, parent.Children[i], recordList); } }
/// <summary> /// Finds all instances of a specific record type in tree starting from root record. /// </summary> /// <param name="type">Type of record to search for.</param> /// <param name="root">Root record to start searching from. If null, will start from RecordRoot.</param> /// <returns>List of records found. May contain 0 records.</returns> public List <SaveTreeBaseRecord> FindRecords(RecordTypes type, SaveTreeBaseRecord root = null) { List <SaveTreeBaseRecord> recordList = new List <SaveTreeBaseRecord>(); if (root == null) { root = RootRecord; } FindRecordsByType(type, root, recordList); return(recordList); }
/// <summary> /// Shallow copy record data from this record to another. /// </summary> /// <param name="other">Other record to receive data.</param> public virtual void CopyTo(SaveTreeBaseRecord other) { if (other == null) { return; } other.streamPosition = this.streamPosition; other.streamLength = this.streamLength; other.streamData = (byte[])this.streamData.Clone(); other.recordType = this.recordType; other.recordRoot = this.recordRoot; other.parent = this.parent; other.children.AddRange(this.children.ToArray()); }
// Populates children of parent records void LinkChildren() { foreach (var kvp in RecordDictionary) { uint parentKey = kvp.Value.RecordRoot.ParentRecordID; if (RecordDictionary.ContainsKey(parentKey)) { SaveTreeBaseRecord parent = RecordDictionary[parentKey]; parent.Children.Add(kvp.Value); } else { RootRecord.Children.Add(kvp.Value); } } }
// Adds record to dictionary bool AddRecord(SaveTreeBaseRecord record) { // Count duplicate IDs and ignore record uint myKey = record.RecordRoot.RecordID; if (RecordDictionary.ContainsKey(myKey)) { duplicateKeysFound++; return(false); } // Add record to dictionary RecordDictionary.Add(myKey, record); return(true); }
void DisplaySaveTree(SaveTreeBaseRecord parent) { for (int i = 0; i < parent.Children.Count; i++) { RecordTypes recordType = parent.Children[i].RecordType; if (recordType == RecordTypes.UnknownTownLink || recordType == RecordTypes.UnknownItemRecord) continue; string textLabel = recordType.ToString(); if (recordType == RecordTypes.Item || recordType == RecordTypes.Spell) { textLabel += string.Format(" [{0}]", GetItemOrSpellName(parent.Children[i])); } // Check if item equipped if (recordType == RecordTypes.Item) { //ItemRecord itemRecord = (ItemRecord)parent.Children[i]; //textLabel += string.Format(" [id={0}]", itemRecord.RecordRoot.RecordID); int equippedIndex = GetEquippedIndex(parent.Children[i] as ItemRecord); if (equippedIndex != -1) { //textLabel = string.Format("(*={0:00}) {1}", equippedIndex, textLabel); } } // Tag wagon container if (recordType == RecordTypes.Container && parent.RecordType == RecordTypes.Character) { ContainerRecord containerRecord = (ContainerRecord)parent.Children[i]; if (containerRecord.IsWagon) textLabel += " [Wagon]"; } EditorGUILayout.LabelField(textLabel); GUILayoutHelper.Indent(() => { DisplaySaveTree(parent.Children[i]); }); } }
/// <summary> /// Finds all instances of a specific record type in tree starting from root record. /// </summary> /// <param name="type">Type of record to search for.</param> /// <param name="root">Root record to start searching from. If null, will start from RecordRoot.</param> /// <returns>List of records found. May contain 0 records.</returns> public List<SaveTreeBaseRecord> FindRecords(RecordTypes type, SaveTreeBaseRecord root = null) { List<SaveTreeBaseRecord> recordList = new List<SaveTreeBaseRecord>(); if (root == null) root = RootRecord; FindRecordsByType(type, root, recordList); return recordList; }
// Adds record to dictionary bool AddRecord(SaveTreeBaseRecord record) { // Count duplicate IDs and ignore record uint myKey = record.RecordRoot.RecordID; if (RecordDictionary.ContainsKey(myKey)) { duplicateKeysFound++; return false; } // Add record to dictionary RecordDictionary.Add(myKey, record); return true; }
// Reads all records in SaveTree // Reader must be positioned at start of first RecordElement void ReadRecords(BinaryReader reader) { RecordDictionary.Clear(); RootRecord = new SaveTreeBaseRecord(); while (reader.BaseStream.Position < reader.BaseStream.Length) { // Read record length and skip empty records as they have no data int length = reader.ReadInt32(); if (length <= 0) continue; // Handle potential stream overflow (e.g. corrupt save, something went wrong) if (reader.BaseStream.Position + length >= reader.BaseStream.Length) break; // Peek record type from RecordRoot so we can instantiate record class based on type RecordTypes type = PeekRecordType(reader); // Add record based on type SaveTreeBaseRecord record; switch (type) { case RecordTypes.Item: record = new ItemRecord(reader, length); break; case RecordTypes.Spell: record = new SpellRecord(reader, length); break; case RecordTypes.Character: record = new CharacterRecord(reader, length); break; //case RecordTypes.UnknownTownLink: // record = new SaveTreeBaseRecord(reader, length); // Read then skip these records for now // continue; //case RecordTypes.DungeonData: // record = new SaveTreeBaseRecord(reader, length); // Read then skip these records for now // continue; default: record = new SaveTreeBaseRecord(reader, length); break; } AddRecord(record); } LinkChildren(); }
// Recursively search for record type void FindRecordsByType(RecordTypes type, SaveTreeBaseRecord parent, List<SaveTreeBaseRecord> recordList) { if (parent.RecordType == type) recordList.Add(parent); for (int i = 0; i < parent.Children.Count; i++) { FindRecordsByType(type, parent.Children[i], recordList); } }
string GetItemOrSpellName(SaveTreeBaseRecord record) { string result = string.Empty; byte[] recordData = record.RecordData; MemoryStream stream = new MemoryStream(recordData); BinaryReader reader = new BinaryReader(stream); if (record.RecordType == RecordTypes.Item) { result = FileProxy.ReadCString(reader); // Name starts at offset 0 } else if (record.RecordType == RecordTypes.Spell) { reader.BaseStream.Position += 47; // Name starts at offset 47 result = FileProxy.ReadCString(reader); } reader.Close(); return result; }
void ExportGUI(SaveTreeBaseRecord[] records, string name) { string path = EditorUtility.OpenFolderPanel("Export Path (Note: Place in subfolder as many files will be created)", "", ""); if (!string.IsNullOrEmpty(path)) { int index = 0; foreach(SaveTreeBaseRecord record in records) { string filename = Path.Combine(path, string.Format("{0}-{1}", name, index++)); File.WriteAllBytes(filename, record.StreamData); } EditorUtility.DisplayDialog("Finished", string.Format("Exported {0} records.", index), "Close"); } }
/// <summary> /// Finds first record of type in tree starting from root record. /// </summary> /// <param name="type">Type of record to search for.</param> /// <param name="root">Root record to start searching from. If null, will start from RecordRoot.</param> /// <returns>Found item or null if not found.</returns> public SaveTreeBaseRecord FindRecord(RecordTypes type, SaveTreeBaseRecord root = null) { List<SaveTreeBaseRecord> records = FindRecords(type, root); if (records.Count == 0) return null; return records[0]; }
void DisplaySaveTree(SaveTreeBaseRecord parent) { for (int i = 0; i < parent.Children.Count; i++) { RecordTypes recordType = parent.Children[i].RecordType; if (recordType == RecordTypes.UnknownTownLink || recordType == RecordTypes.UnknownItemRecord) continue; string textLabel = recordType.ToString(); if (recordType == RecordTypes.Item || recordType == RecordTypes.Spell) { textLabel += string.Format(" [{0}]", GetItemOrSpellName(parent.Children[i])); } EditorGUILayout.LabelField(textLabel); GUILayoutHelper.Indent(() => { DisplaySaveTree(parent.Children[i]); }); } }
/// <summary> /// Shallow copy record data from this record to another. /// </summary> /// <param name="other">Other record to receive data.</param> public virtual void CopyTo(SaveTreeBaseRecord other) { if (other == null) return; other.streamPosition = this.streamPosition; other.streamLength = this.streamLength; other.streamData = (byte[])this.streamData.Clone(); other.recordType = this.recordType; other.recordRoot = this.recordRoot; other.parent = this.parent; other.children.AddRange(this.children.ToArray()); }