From c389ea1e4913ff7a3df0547fc9322dfd0c55cb32 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Tue, 27 Dec 2022 00:55:24 -0800 Subject: [PATCH] Add SGA extraction --- .../BurnOutSharp.Wrappers.csproj | 1 + BurnOutSharp.Wrappers/SGA.cs | 148 +++++++++++++++++- 2 files changed, 147 insertions(+), 2 deletions(-) diff --git a/BurnOutSharp.Wrappers/BurnOutSharp.Wrappers.csproj b/BurnOutSharp.Wrappers/BurnOutSharp.Wrappers.csproj index c0c3470a..b8c7de94 100644 --- a/BurnOutSharp.Wrappers/BurnOutSharp.Wrappers.csproj +++ b/BurnOutSharp.Wrappers/BurnOutSharp.Wrappers.csproj @@ -21,6 +21,7 @@ + diff --git a/BurnOutSharp.Wrappers/SGA.cs b/BurnOutSharp.Wrappers/SGA.cs index 0e2910e2..0dc6d861 100644 --- a/BurnOutSharp.Wrappers/SGA.cs +++ b/BurnOutSharp.Wrappers/SGA.cs @@ -1,6 +1,8 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; +using ComponentAce.Compression.Libs.zlib; namespace BurnOutSharp.Wrappers { @@ -508,7 +510,7 @@ namespace BurnOutSharp.Wrappers Console.WriteLine($" File start index: {section5.FileStartIndex}"); Console.WriteLine($" File end index: {section5.FileEndIndex}"); Console.WriteLine($" Folder root index: {section5.FolderRootIndex}"); - break; + break; default: Console.WriteLine($" Unknown format for version {MajorVersion}"); break; @@ -642,7 +644,18 @@ namespace BurnOutSharp.Wrappers /// True if all files extracted, false otherwise public bool ExtractAll(string outputDirectory) { - return false; + // If we have no files + if (Files == null || Files.Length == 0) + return false; + + // Loop through and extract all files to the output + bool allExtracted = true; + for (int i = 0; i < Files.Length; i++) + { + allExtracted &= ExtractFile(i, outputDirectory); + } + + return allExtracted; } /// @@ -653,6 +666,137 @@ namespace BurnOutSharp.Wrappers /// True if the file extracted, false otherwise public bool ExtractFile(int index, string outputDirectory) { + // If we have no files + if (Files == null || Files.Length == 0) + return false; + + // If the files index is invalid + if (index < 0 || index >= Files.Length) + return false; + + // Get the files + var file = Files[index]; + if (file == null) + return false; + + // Create the filename + string filename; + switch (MajorVersion) + { + case 4: + case 5: filename = (file as Models.SGA.File4).Name; break; + case 6: filename = (file as Models.SGA.File6).Name; break; + case 7: filename = (file as Models.SGA.File7).Name; break; + default: return false; + } + + // Loop through and get all parent directories + var parentNames = new List { filename }; + + // Get the immediate parent directory + object folder; + switch (MajorVersion) + { + case 4: folder = (Folders as Models.SGA.Folder4[]).FirstOrDefault(f => index >= f.FileStartIndex && index <= f.FileEndIndex); break; + case 5: + case 6: + case 7: folder = (Folders as Models.SGA.Folder5[]).FirstOrDefault(f => index >= f.FileStartIndex && index <= f.FileEndIndex); break; + default: return false; + } + + // If we have a parent folder + if (folder != null) + { + switch (MajorVersion) + { + case 4: parentNames.Add((folder as Models.SGA.Folder4).Name); break; + case 5: + case 6: + case 7: parentNames.Add((folder as Models.SGA.Folder5).Name); break; + default: return false; + } + } + + // Reverse and assemble the filename + parentNames.Reverse(); + filename = Path.Combine(parentNames.ToArray()); + + // Get the file offset + long fileOffset; + switch (MajorVersion) + { + case 4: + case 5: fileOffset = (file as Models.SGA.File4).Offset; break; + case 6: fileOffset = (file as Models.SGA.File6).Offset; break; + case 7: fileOffset = (file as Models.SGA.File7).Offset; break; + default: return false; + } + + // Adjust the file offset + fileOffset += FileDataOffset.Value; + + // Get the file sizes + long fileSize, outputFileSize; + switch (MajorVersion) + { + case 4: + case 5: + fileSize = (file as Models.SGA.File4).SizeOnDisk; + outputFileSize = (file as Models.SGA.File4).Size; + break; + case 6: + fileSize = (file as Models.SGA.File6).SizeOnDisk; + outputFileSize = (file as Models.SGA.File6).Size; + break; + case 7: + fileSize = (file as Models.SGA.File7).SizeOnDisk; + outputFileSize = (file as Models.SGA.File7).Size; + break; + default: return false; + } + + // Read the compressed data directly + byte[] compressedData = ReadFromDataSource((int)fileOffset, (int)fileSize); + if (compressedData == null) + return false; + + // Decompress the data + byte[] data = new byte[outputFileSize]; + ZStream zst = new ZStream + { + next_in = compressedData, + avail_in = compressedData.Length, + next_out = data, + avail_out = data.Length, + }; + zst.inflateInit(); + zst.inflate(zlibConst.Z_FULL_FLUSH); + zst.inflateEnd(); + + // If we have an invalid output directory + if (string.IsNullOrWhiteSpace(outputDirectory)) + return false; + + // Create the full output path + filename = Path.Combine(outputDirectory, filename); + + // Ensure the output directory is created + Directory.CreateDirectory(Path.GetDirectoryName(filename)); + + // Try to write the data + try + { + // Open the output file for writing + using (Stream fs = File.OpenWrite(filename)) + { + fs.Write(data, 0, data.Length); + } + } + catch + { + return false; + } + return false; }