diff --git a/SabreTools.Serialization/Wrappers/BFPK.cs b/SabreTools.Serialization/Wrappers/BFPK.cs index 5034364c..027a95f7 100644 --- a/SabreTools.Serialization/Wrappers/BFPK.cs +++ b/SabreTools.Serialization/Wrappers/BFPK.cs @@ -1,8 +1,10 @@ using System.IO; +using SabreTools.Compression.Deflate; +using SabreTools.Models.BFPK; namespace SabreTools.Serialization.Wrappers { - public class BFPK : WrapperBase + public class BFPK : WrapperBase { #region Descriptive Properties @@ -11,17 +13,26 @@ namespace SabreTools.Serialization.Wrappers #endregion + #region Extension Properties + + /// + /// + /// + public FileEntry[] Files => Model.Files ?? []; + + #endregion + #region Constructors /// - public BFPK(Models.BFPK.Archive? model, byte[]? data, int offset) + public BFPK(Archive? model, byte[]? data, int offset) : base(model, data, offset) { // All logic is handled by the base class } /// - public BFPK(Models.BFPK.Archive? model, Stream? data) + public BFPK(Archive? model, Stream? data) : base(model, data) { // All logic is handled by the base class @@ -74,5 +85,96 @@ namespace SabreTools.Serialization.Wrappers } #endregion + + #region Extraction + + /// + /// Extract all files from the BFPK to an output directory + /// + /// Output directory to write to + /// True if all files extracted, false otherwise + public bool ExtractAll(string outputDirectory) + { + // 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; + } + + /// + /// Extract a file from the BFPK to an output directory by index + /// + /// File index to extract + /// Output directory to write to + /// 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 we have an invalid index + if (index < 0 || index >= Files.Length) + return false; + + // Get the file information + var file = Files[index]; + if (file == null) + return false; + + // Get the read index and length + int offset = file.Offset + 4; + int compressedSize = file.CompressedSize; + + // Some files can lack the length prefix + if (compressedSize > GetEndOfFile()) + { + offset -= 4; + compressedSize = file.UncompressedSize; + } + + try + { + // Ensure the output directory exists + Directory.CreateDirectory(outputDirectory); + + // Create the output path + string filePath = Path.Combine(outputDirectory, file.Name ?? $"file{index}"); + using FileStream fs = File.OpenWrite(filePath); + + // Read the data block + var data = ReadFromDataSource(offset, compressedSize); + if (data == null) + return false; + + // If we have uncompressed data + if (compressedSize == file.UncompressedSize) + { + fs.Write(data, 0, compressedSize); + } + else + { + MemoryStream ms = new MemoryStream(data); + ZlibStream zs = new ZlibStream(ms, CompressionMode.Decompress); + zs.CopyTo(fs); + } + + return true; + } + catch + { + return false; + } + } + + #endregion } } \ No newline at end of file