public static void Extract(string tfaiFile, string tfafFile, string targetDir) { Console.WriteLine("Extracting to {0}", targetDir); // Read the index file first var entries = ArchiveIndexReader.ReadIndex(tfaiFile); using (var stream = new FileStream(tfafFile, FileMode.Open)) { foreach (var entry in entries) { Console.Write("Extracting {0}...", entry.Path); var fullPath = Path.Combine(targetDir, entry.Path); if (entry.Directory) { Directory.CreateDirectory(fullPath); } else { using (var outStream = new FileStream(fullPath, FileMode.Create)) { byte[] buffer = new byte[entry.Size]; stream.Read(buffer, 0, buffer.Length); outStream.Write(buffer, 0, buffer.Length); } } Console.WriteLine(); } } }
public static SaveGameFile Load(string basePath, string currentSaveDir) { var result = new SaveGameFile(); var indexPath = basePath + ".tfai"; var archiveIndex = ArchiveIndexReader.ReadIndex(indexPath); using var dataStream = new FileStream(basePath + ".tfaf", FileMode.Open); byte[] gameStateData = null; byte[] spellPacketData = null; byte[] partyConfigData = null; byte[] mapFleeData = null; byte[] uiStateData = null; bool GrabData(ArchiveIndexEntry entry, string filename, ref byte[] bufferOut) { if (entry.Path == filename) { if (bufferOut != null) { throw new CorruptSaveException($"File {filename} exists twice in the save game!"); } var buffer = new byte[entry.Size]; dataStream.Read(buffer); if (Debugger.IsAttached) { var fullPath = Path.Join(currentSaveDir, entry.Path); File.WriteAllBytes(fullPath, buffer); } bufferOut = buffer; return(true); } return(false); } foreach (var entry in archiveIndex) { var fullPath = Path.Join(currentSaveDir, entry.Path); if (entry.Directory) { Directory.CreateDirectory(fullPath); continue; } if (GrabData(entry, MainStateFile, ref gameStateData) || GrabData(entry, ActionSequencesSpellsFile, ref spellPacketData) || GrabData(entry, PartyConfigFile, ref partyConfigData) || GrabData(entry, MapFleeFile, ref mapFleeData) || GrabData(entry, UiStateFile, ref uiStateData)) { continue; } CopyStreamToFile(dataStream, entry.Size, fullPath); } if (gameStateData == null) { throw new CorruptSaveException($"Save file is missing {MainStateFile}"); } if (spellPacketData == null) { throw new CorruptSaveException($"Save file is missing {ActionSequencesSpellsFile}"); } if (partyConfigData == null) { throw new CorruptSaveException($"Save file is missing {PartyConfigFile}"); } if (mapFleeData == null) { throw new CorruptSaveException($"Save file is missing {MapFleeFile}"); } if (uiStateData == null) { throw new CorruptSaveException($"Save file is missing {UiStateFile}"); } result.GameState = SavedGameState.Load(gameStateData, spellPacketData, partyConfigData, mapFleeData); result.UiState = SavedUiState.Load(uiStateData); // Load the optional Co8 data if it exists var co8Path = basePath + ".co8"; if (File.Exists(co8Path)) { result.Co8State = SavedCo8State.Load(co8Path); } return(result); }