diff --git a/BurnOutSharp/BurnOutSharp.csproj b/BurnOutSharp/BurnOutSharp.csproj index 487eb05d..c98dcd1f 100644 --- a/BurnOutSharp/BurnOutSharp.csproj +++ b/BurnOutSharp/BurnOutSharp.csproj @@ -66,4 +66,8 @@ + + + + diff --git a/BurnOutSharp/External/libmspack/CAB/Cabinet.cs b/BurnOutSharp/External/libmspack/CAB/Cabinet.cs index 3b9f10f7..7f4c42d1 100644 --- a/BurnOutSharp/External/libmspack/CAB/Cabinet.cs +++ b/BurnOutSharp/External/libmspack/CAB/Cabinet.cs @@ -69,9 +69,9 @@ namespace LibMSPackSharp.CAB /// If this cabinet is part of a merged cabinet set, the #files and #folders /// fields are common to all cabinets in the set, and will be identical. /// - /// - /// - /// + /// + /// + /// public class Cabinet { #region Internal diff --git a/BurnOutSharp/External/libmspack/CAB/Compressor.cs b/BurnOutSharp/External/libmspack/CAB/Compressor.cs index befc5215..5bf72ee1 100644 --- a/BurnOutSharp/External/libmspack/CAB/Compressor.cs +++ b/BurnOutSharp/External/libmspack/CAB/Compressor.cs @@ -21,6 +21,6 @@ namespace LibMSPackSharp.CAB /// public class Compressor { - public int Dummy { get; set; } + public SystemImpl System { get; set; } } } diff --git a/BurnOutSharp/External/libmspack/CAB/CompressorImpl.cs b/BurnOutSharp/External/libmspack/CAB/CompressorImpl.cs deleted file mode 100644 index 430be7b7..00000000 --- a/BurnOutSharp/External/libmspack/CAB/CompressorImpl.cs +++ /dev/null @@ -1,19 +0,0 @@ -/* This file is part of libmspack. - * (C) 2003-2018 Stuart Caie. - * - * libmspack is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License (LGPL) version 2.1 - * - * For further details, see the file COPYING.LIB distributed with libmspack - */ - -namespace LibMSPackSharp.CAB -{ - /// - /// TODO - /// - public class CompressorImpl : Compressor - { - public SystemImpl System { get; set; } - } -} diff --git a/BurnOutSharp/External/libmspack/CAB/Constants.cs b/BurnOutSharp/External/libmspack/CAB/Constants.cs new file mode 100644 index 00000000..522c61f2 --- /dev/null +++ b/BurnOutSharp/External/libmspack/CAB/Constants.cs @@ -0,0 +1,91 @@ +/* This file is part of libmspack. + * (C) 2003-2018 Stuart Caie. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +/* Cabinet (.CAB) files are a form of file archive. Each cabinet contains + * "folders", which are compressed spans of data. Each cabinet has + * "files", whose metadata is in the cabinet header, but whose actual data + * is stored compressed in one of the "folders". Cabinets can span more + * than one physical file on disk, in which case they are a "cabinet set", + * and usually the last folder of each cabinet extends into the next + * cabinet. + * + * For a complete description of the format, see the MSDN site: + * http://msdn.microsoft.com/en-us/library/bb267310.aspx + */ + +/* Notes on compliance with cabinet specification: + * + * One of the main changes between cabextract 0.6 and libmspack's cab + * decompressor is the move from block-oriented decompression to + * stream-oriented decompression. + * + * cabextract would read one data block from disk, decompress it with the + * appropriate method, then write the decompressed data. The CAB + * specification is specifically designed to work like this, as it ensures + * compression matches do not span the maximum decompressed block size + * limit of 32kb. + * + * However, the compression algorithms used are stream oriented, with + * specific hacks added to them to enforce the "individual 32kb blocks" + * rule in CABs. In other file formats, they do not have this limitation. + * + * In order to make more generalised decompressors, libmspack's CAB + * decompressor has moved from being block-oriented to more stream + * oriented. This also makes decompression slightly faster. + * + * However, this leads to incompliance with the CAB specification. The + * CAB controller can no longer ensure each block of input given to the + * decompressors is matched with their output. The "decompressed size" of + * each individual block is thrown away. + * + * Each CAB block is supposed to be seen as individually compressed. This + * means each consecutive data block can have completely different + * "uncompressed" sizes, ranging from 1 to 32768 bytes. However, in + * reality, all data blocks in a folder decompress to exactly 32768 bytes, + * excepting the final block. + * + * Given this situation, the decompression algorithms are designed to + * realign their input bitstreams on 32768 output-byte boundaries, and + * various other special cases have been made. libmspack will not + * correctly decompress LZX or Quantum compressed folders where the blocks + * do not follow this "32768 bytes until last block" pattern. It could be + * implemented if needed, but hopefully this is not necessary -- it has + * not been seen in over 3Gb of CAB archives. + */ + +namespace LibMSPackSharp.CAB +{ + public static class Constants + { + // CAB data blocks are <= 32768 bytes in uncompressed form.Uncompressed + // blocks have zero growth. MSZIP guarantees that it won't grow above + // uncompressed size by more than 12 bytes.LZX guarantees it won't grow + // more than 6144 bytes.Quantum has no documentation, but the largest + // block seen in the wild is 337 bytes above uncompressed size. + + public const int CAB_BLOCKMAX = 32768; + public const int CAB_INPUTMAX = CAB_BLOCKMAX + 6144; + + // input buffer needs to be CAB_INPUTMAX + 1 byte to allow for max-sized block + // plus 1 trailer byte added by cabd_sys_read_block() for Quantum alignment. + // + // When MSCABD_PARAM_SALVAGE is set, block size is not checked so can be + // up to 65535 bytes, so max input buffer size needed is 65535 + 1 + + public const int CAB_INPUTMAX_SALVAGE = 65535; + public const int CAB_INPUTBUF = CAB_INPUTMAX_SALVAGE + 1; + + // There are no more than 65535 data blocks per folder, so a folder cannot + // be more than 32768*65535 bytes in length.As files cannot span more than + // one folder, this is also their max offset, length and offset+length limit. + + public const int CAB_FOLDERMAX = 65535; + public const int CAB_LENGTHMAX = CAB_BLOCKMAX * CAB_FOLDERMAX; + } +} diff --git a/BurnOutSharp/External/libmspack/CAB/DecompressState.cs b/BurnOutSharp/External/libmspack/CAB/DecompressState.cs index b8b2d5a2..501c71b7 100644 --- a/BurnOutSharp/External/libmspack/CAB/DecompressState.cs +++ b/BurnOutSharp/External/libmspack/CAB/DecompressState.cs @@ -66,12 +66,12 @@ namespace LibMSPackSharp.CAB /// /// Input file handle /// - public object InputFileHandle { get; set; } + public DefaultFileImpl InputFileHandle { get; set; } /// /// Output file handle /// - public object OutputFileHandle { get; set; } + public DefaultFileImpl OutputFileHandle { get; set; } /// /// Input data consumed @@ -86,6 +86,6 @@ namespace LibMSPackSharp.CAB /// /// One input block of data /// - public byte[] Input { get; set; } = new byte[Implementation.CAB_INPUTBUF]; + public byte[] Input { get; set; } = new byte[Constants.CAB_INPUTBUF]; } } diff --git a/BurnOutSharp/External/libmspack/CAB/Decompressor.cs b/BurnOutSharp/External/libmspack/CAB/Decompressor.cs index 25095713..99f40ad6 100644 --- a/BurnOutSharp/External/libmspack/CAB/Decompressor.cs +++ b/BurnOutSharp/External/libmspack/CAB/Decompressor.cs @@ -15,6 +15,9 @@ */ using System; +using System.Text; +using LibMSPackSharp.Compression; +using static LibMSPackSharp.CAB.Constants; namespace LibMSPackSharp.CAB { @@ -27,6 +30,28 @@ namespace LibMSPackSharp.CAB /// public class Decompressor { + #region Fields + + public DecompressState State { get; set; } + + public SystemImpl System { get; set; } + + public int BufferSize { get; set; } + + public int SearchBufferSize { get; set; } + + public bool FixMSZip { get; set; } + + public bool Salvage { get; set; } + + public Error Error { get; set; } + + public Error ReadError { get; set; } + + #endregion + + #region Public Functionality + /// /// Opens a cabinet file and reads its contents. /// @@ -40,13 +65,32 @@ namespace LibMSPackSharp.CAB /// The filename pointer should be considered "in use" until close() is /// called on the cabinet. /// - /// A self-referential pointer to the Decompressor instance being called /// The filename of the cabinet file. This is passed directly to SystemImpl::open(). /// A pointer to a Cabinet structure, or NULL on failure - /// - /// - /// - public Func Open; + /// + /// + /// + public Cabinet Open(string filename) + { + DefaultFileImpl fileHandle = System.Open(filename, OpenMode.MSPACK_SYS_OPEN_READ); + if (fileHandle == null) + { + Error = Error.MSPACK_ERR_OPEN; + return null; + } + + Cabinet cab = new Cabinet() { Filename = filename }; + Error error = ReadHeaders(fileHandle, cab, 0, Salvage, false); + if (error != Error.MSPACK_ERR_OK) + { + Close(cab); + cab = null; + } + + Error = error; + System.Close(fileHandle); + return cab; + } /// /// Closes a previously opened cabinet or cabinet set. @@ -72,13 +116,57 @@ namespace LibMSPackSharp.CAB /// not allocated by the library. The caller should free this itself if /// necessary, before it is lost forever. /// - /// A self-referential pointer to the Decompressor instance being called - /// The cabinet to close - /// - /// - /// - /// - public Action Close; + /// The cabinet to close + /// + /// + /// + /// + public void Close(Cabinet cabinet) + { + FolderData dat, ndat; + Cabinet cab; + Folder fol, nfol; + InternalFile fi, nfi; + + Error = Error.MSPACK_ERR_OK; + + while (cabinet != null) + { + // Free files + for (fi = cabinet.Files; fi != null; fi = nfi) + { + nfi = fi.Next; + } + + // Free folders + for (fol = cabinet.Folders; fol != null; fol = nfol) + { + nfol = fol.Next; + + // Free folder decompression state if it has been decompressed + if (State != null && (State.Folder == fol)) + { + if (State.InputFileHandle != null) + System.Close(State.InputFileHandle); + + FreeDecompressionState(); + State = null; + } + + // Free folder data segments + for (dat = fol.Data.Next; dat != null; dat = ndat) + { + ndat = dat.Next; + } + } + + // Free actual cabinet structure + cab = cabinet.Next; + + // Repeat full procedure again with the cab.Next pointer (if set) + cabinet = cab; + } + } /// /// Searches a regular file for embedded cabinets. @@ -107,19 +195,47 @@ namespace LibMSPackSharp.CAB /// close() should only be called on the result of search(), not on any /// subsequent cabinets in the Cabinet::next chain. /// - /// - /// a self-referential pointer to the Decompressor - /// instance being called - /// - /// - /// the filename of the file to search for cabinets. This - /// is passed directly to SystemImpl::open(). - /// + /// The filename of the file to search for cabinets. This is passed directly to SystemImpl::open(). /// a pointer to a Cabinet structure, or NULL - /// - /// - /// - public Func Search; + /// + /// + /// + public Cabinet Search(string filename) + { + // Allocate a search buffer + byte[] search_buf = new byte[SearchBufferSize]; + if (search_buf == null) + { + Error = Error.MSPACK_ERR_NOMEMORY; + return null; + } + + // Open file and get its full file length + DefaultFileImpl fh; Cabinet cab = null; + if ((fh = System.Open(filename, OpenMode.MSPACK_SYS_OPEN_READ)) != null) + { + long firstlen = 0; + if ((Error = System.GetFileLength(fh, out long filelen)) == Error.MSPACK_ERR_OK) + Error = Find(search_buf, fh, filename, filelen, ref firstlen, out cab); + + // Truncated / extraneous data warning: + if (firstlen != 0 && (firstlen != filelen) && (cab == null || cab.BaseOffset == 0)) + { + if (firstlen < filelen) + System.Message(fh, $"WARNING; possible {filelen - firstlen} extra bytes at end of file."); + else + System.Message(fh, $"WARNING; file possibly truncated by {firstlen - filelen} bytes."); + } + + System.Close(fh); + } + else + { + Error = Error.MSPACK_ERR_OPEN; + } + + return cab; + } /// /// Appends one Cabinet to another, forming or extending a cabinet @@ -152,23 +268,13 @@ namespace LibMSPackSharp.CAB /// structures in either cabinet must be discarded and re-obtained after /// merging. /// - /// - /// a self-referential pointer to the Decompressor - /// instance being called - /// - /// - /// the cabinet which will be appended to, - /// predecessor of nextcab - /// - /// - /// the cabinet which will be appended, - /// successor of cab - /// + /// The cabinet which will be appended to, predecessor of nextcab + /// The cabinet which will be appended, successor of cab /// an error code, or MSPACK_ERR_OK if successful - /// - /// - /// - public Func Append; + /// + /// + /// + public Error Append(Cabinet cab, Cabinet nextcab) => Merge(cab, nextcab); /// /// Prepends one Cabinet to another, forming or extending a @@ -179,23 +285,13 @@ namespace LibMSPackSharp.CAB /// all other respects, it is identical to append(). See append() for the /// full documentation. /// - /// - /// a self-referential pointer to the Decompressor - /// instance being called - /// - /// - /// the cabinet which will be prepended to, - /// successor of nextcab - /// - /// - /// the cabinet which will be prepended, - /// predecessor of cab - /// + /// The cabinet which will be prepended to, successor of prevcab + /// The cabinet which will be prepended, predecessor of cab /// an error code, or MSPACK_ERR_OK if successful - /// - /// - /// - public Func Prepend; + /// + /// + /// + public Error Prepend(Cabinet cab, Cabinet prevcab) => Merge(prevcab, cab); /// /// Extracts a file from a cabinet or cabinet set. @@ -213,14 +309,139 @@ namespace LibMSPackSharp.CAB /// and not enough parts of the cabinet set have been loaded and appended /// or prepended, an error will be returned immediately. /// - /// - /// a self-referential pointer to the Decompressor - /// instance being called - /// /// the file to be decompressed /// the filename of the file being written to /// an error code, or MSPACK_ERR_OK if successful - public Func Extract; + public Error Extract(InternalFile file, string filename) + { + if (file == null) + return Error = Error.MSPACK_ERR_ARGS; + + Folder fol = file.Folder; + + // If offset is beyond 2GB, nothing can be extracted + if (file.Header.FolderOffset > CAB_LENGTHMAX) + return Error = Error.MSPACK_ERR_DATAFORMAT; + + // If file claims to go beyond 2GB either error out, + // or in salvage mode reduce file length so it fits 2GB limit + long filelen = file.Header.UncompressedSize; + if (filelen > CAB_LENGTHMAX || (file.Header.FolderOffset + filelen) > CAB_LENGTHMAX) + { + if (Salvage) + filelen = CAB_LENGTHMAX - file.Header.FolderOffset; + else + return Error = Error.MSPACK_ERR_DATAFORMAT; + } + + // Extraction impossible if no folder, or folder needs predecessor + if (fol == null || fol.MergePrev != null) + { + System.Message(null, $"ERROR; file \"{file.Filename}\" cannot be extracted, cabinet set is incomplete"); + return Error = Error.MSPACK_ERR_DECRUNCH; + } + + // If file goes beyond what can be decoded, given an error. + // In salvage mode, don't assume block sizes, just try decoding + if (!Salvage) + { + long maxlen = fol.Header.NumBlocks * CAB_BLOCKMAX; + if ((file.Header.FolderOffset + filelen) > maxlen) + { + System.Message(null, $"ERROR; file \"{file.Filename}\" cannot be extracted, cabinet set is incomplete"); + return Error = Error.MSPACK_ERR_DECRUNCH; + } + } + + // Allocate generic decompression state + if (State == null) + { + State = new DecompressState() { Sys = System }; + + State.Sys.Read = SysRead; + State.Sys.Write = SysWrite; + } + + // Do we need to change folder or reset the current folder? + if ((State.Folder != fol) || (State.Offset > file.Header.FolderOffset) || State.DecompressorState == null) + { + // Free any existing decompressor + FreeDecompressionState(); + + // Do we need to open a new cab file? + if (State.InputFileHandle == null || (fol.Data.Cab != State.InputCabinet)) + { + // Close previous file handle if from a different cab + if (State.InputFileHandle != null) + System.Close(State.InputFileHandle); + + State.InputCabinet = fol.Data.Cab; + State.InputFileHandle = System.Open(fol.Data.Cab.Filename, OpenMode.MSPACK_SYS_OPEN_READ); + if (State.InputFileHandle == null) + return Error = Error.MSPACK_ERR_OPEN; + } + + // Seek to start of data blocks + if (!System.Seek(State.InputFileHandle, fol.Data.Offset, SeekMode.MSPACK_SYS_SEEK_START)) + return Error = Error.MSPACK_ERR_SEEK; + + // Set up decompressor + if (InitDecompressionState(fol.Header.CompType) != Error.MSPACK_ERR_OK) + return Error; + + // Initialise new folder state + State.Folder = fol; + State.Data = fol.Data; + State.Offset = 0; + State.Block = 0; + State.Outlen = 0; + State.InputPointer = State.InputEnd = 0; + + // Read_error lasts for the lifetime of a decompressor + ReadError = Error.MSPACK_ERR_OK; + } + + // Open file for output + DefaultFileImpl fh = System.Open(filename, OpenMode.MSPACK_SYS_OPEN_WRITE); + if (fh == null) + return Error = Error.MSPACK_ERR_OPEN; + + Error = Error.MSPACK_ERR_OK; + + // If file has more than 0 bytes + if (filelen != 0) + { + long bytes; + Error error; + + // Get to correct offset. + // - use null fh to say 'no writing' to cabd_sys_write() + // - if cabd_sys_read() has an error, it will set self.ReadError + // and pass back MSPACK_ERR_READ + State.OutputFileHandle = null; + if ((bytes = file.Header.FolderOffset - State.Offset) != 0) + { + error = State.Decompress(State.DecompressorState, bytes); + Error = (error == Error.MSPACK_ERR_READ) ? ReadError : error; + } + + // If getting to the correct offset was error free, unpack file + if (Error == Error.MSPACK_ERR_OK) + { + State.OutputFileHandle = fh; + InitDecompressionState(fol.Header.CompType); + + error = State.Decompress(State.DecompressorState, filelen); + Error = (error == Error.MSPACK_ERR_READ) ? ReadError : error; + } + } + + // Close output file + System.Close(fh); + State.OutputFileHandle = null; + + return Error; + } /// /// Sets a CAB decompression engine parameter. @@ -236,33 +457,1032 @@ namespace LibMSPackSharp.CAB /// bit buffer by decompressors? The minimum value is 4. The default /// value is 4096. /// - /// - /// a self-referential pointer to the Decompressor - /// instance being called - /// /// the parameter to set /// the value to set the parameter to /// /// MSPACK_ERR_OK if all is OK, or MSPACK_ERR_ARGS if there /// is a problem with either parameter or value. /// - /// - /// - public Func SetParam; + /// + /// + public Error SetParam(Parameters param, int value) + { + switch (param) + { + case Parameters.MSCABD_PARAM_SEARCHBUF: + if (value < 4) + return Error.MSPACK_ERR_ARGS; + + SearchBufferSize = value; + break; + + case Parameters.MSCABD_PARAM_FIXMSZIP: + FixMSZip = value != 0; + break; + + case Parameters.MSCABD_PARAM_DECOMPBUF: + if (value < 4) + return Error.MSPACK_ERR_ARGS; + + BufferSize = value; + break; + + case Parameters.MSCABD_PARAM_SALVAGE: + Salvage = value != 0; + break; + + default: + return Error.MSPACK_ERR_ARGS; + } + + return Error.MSPACK_ERR_OK; + } + + #endregion + + #region Decompression State /// - /// Returns the error code set by the most recently called method. - /// - /// This is useful for open() and search(), which do not return an error - /// code directly. + /// Initialises decompression state, according to which + /// decompression method was used. relies on self.State.Folder being the same + /// as when initialised. /// - /// - /// a self-referential pointer to the Decompressor - /// instance being called - /// - /// the most recent error code - /// - /// - public Func LastError; + internal Error InitDecompressionState(CompressionType ct) + { + State.CompressionType = ct; + switch (ct & CompressionType.COMPTYPE_MASK) + { + case CompressionType.COMPTYPE_NONE: + State.Decompress = None.Decompress; + State.DecompressorState = None.Init(State.Sys, State.InputFileHandle, State.OutputFileHandle, BufferSize); + break; + + case CompressionType.COMPTYPE_MSZIP: + State.Decompress = MSZIP.Decompress; + State.DecompressorState = MSZIP.Init(State.Sys, State.InputFileHandle, State.OutputFileHandle, BufferSize, FixMSZip); + break; + + case CompressionType.COMPTYPE_QUANTUM: + State.Decompress = QTM.Decompress; + State.DecompressorState = QTM.Init(State.Sys, State.InputFileHandle, State.OutputFileHandle, ((ushort)ct >> 8) & 0x1f, BufferSize); + break; + + case CompressionType.COMPTYPE_LZX: + State.Decompress = LZX.Decompress; + State.DecompressorState = LZX.Init(State.Sys, State.InputFileHandle, State.OutputFileHandle, ((ushort)ct >> 8) & 0x1f, 0, BufferSize, 0, false); + break; + + default: + return Error = Error.MSPACK_ERR_DATAFORMAT; + } + + return Error = (State.DecompressorState != null) ? Error.MSPACK_ERR_OK : Error.MSPACK_ERR_NOMEMORY; + } + + /// + /// Frees decompression state, according to which method was used. + /// + /// + internal void FreeDecompressionState() + { + if (State?.DecompressorState == null) + return; + + State.Decompress = null; + State.DecompressorState = null; + } + + #endregion + + #region I/O Methods + + /// + /// The internal reader function which the decompressors + /// use. will read data blocks (and merge split blocks) from the cabinet + /// and serve the read bytes to the decompressors + /// + internal static int SysRead(object file, byte[] buffer, int pointer, int bytes) + { + if (file is Decompressor self) + { + SystemImpl sys = self.System; + + int avail, todo, outlen = 0; + + bool ignore_cksum = self.Salvage || + (self.FixMSZip && + ((self.State.CompressionType & CompressionType.COMPTYPE_MASK) == CompressionType.COMPTYPE_MSZIP)); + bool ignore_blocksize = self.Salvage; + + todo = bytes; + while (todo > 0) + { + avail = self.State.InputEnd - self.State.InputPointer; + + // If out of input data, read a new block + if (avail != 0) + { + // Copy as many input bytes available as possible + if (avail > todo) + avail = todo; + + Array.Copy(self.State.Input, self.State.InputPointer, buffer, pointer, avail); + self.State.InputPointer += avail; + pointer += avail; + todo -= avail; + } + else + { + // Out of data, read a new block + + // Check if we're out of input blocks, advance block counter + if (self.State.Block++ >= self.State.Folder.Header.NumBlocks) + { + if (!self.Salvage) + self.ReadError = Error.MSPACK_ERR_DATAFORMAT; + else + Console.WriteLine("Ran out of CAB input blocks prematurely"); + + break; + } + + // Read a block + self.ReadError = SysReadBlock(sys, self.State, ref outlen, ignore_cksum, ignore_blocksize); + if (self.ReadError != Error.MSPACK_ERR_OK) + return -1; + + self.State.Outlen += outlen; + + // Special Quantum hack -- trailer byte to allow the decompressor + // to realign itself. CAB Quantum blocks, unlike LZX blocks, can have + // anything from 0 to 4 trailing null bytes. + if ((self.State.CompressionType & CompressionType.COMPTYPE_MASK) == CompressionType.COMPTYPE_QUANTUM) + self.State.Input[self.State.InputEnd++] = 0xFF; + + // Is this the last block? + if (self.State.Block >= self.State.Folder.Header.NumBlocks) + { + if ((self.State.CompressionType & CompressionType.COMPTYPE_MASK) == CompressionType.COMPTYPE_LZX) + { + // Special LZX hack -- on the last block, inform LZX of the + // size of the output data stream. + LZX.SetOutputLength(self.State.DecompressorState as LZXDStream, self.State.Outlen); + } + } + } + } + + return bytes - todo; + } + else if (file is DefaultFileImpl impl) + { + return SystemImpl.DefaultSystem.Read(file, buffer, pointer, bytes); + } + + return -1; + } + + /// + /// The internal writer function which the decompressors + /// use. it either writes data to disk (self.State.OutputFileHandle) with the real + /// sys.write() function, or does nothing with the data when + /// self.State.OutputFileHandle == null. advances self.State.Offset + /// + internal static int SysWrite(object file, byte[] buffer, int pointer, int bytes) + { + if (file is Decompressor self) + { + self.State.Offset += (uint)bytes; + if (self.State.OutputFileHandle != null) + return self.System.Write(self.State.OutputFileHandle, buffer, pointer, bytes); + + return bytes; + } + else if (file is DefaultFileImpl impl) + { + return SystemImpl.DefaultSystem.Write(file, buffer, pointer, bytes); + } + + // Unknown file to write to + return 0; + } + + /// + /// Reads a whole data block from a cab file. The block may span more than + /// one cab file, if it does then the fragments will be reassembled + /// + private static Error SysReadBlock(SystemImpl sys, DecompressState d, ref int output, bool ignore_cksum, bool ignore_blocksize) + { + byte[] hdr = new byte[_DataBlockHeader.Size]; + int full_len; + + // Reset the input block pointer and end of block pointer + d.InputPointer = d.InputEnd = 0; + + do + { + // Read the block header + if (SystemImpl.DefaultSystem.Read(d.InputFileHandle, hdr, 0, _DataBlockHeader.Size) != _DataBlockHeader.Size) + return Error.MSPACK_ERR_READ; + + // Skip any reserved block headers + if (d.Data.Cab.Header.HeaderReserved != 0 && !sys.Seek(d.InputFileHandle, d.Data.Cab.Header.HeaderReserved, SeekMode.MSPACK_SYS_SEEK_CUR)) + return Error.MSPACK_ERR_SEEK; + + // Create a block header from the data + Error err = _DataBlockHeader.Create(hdr, out _DataBlockHeader dataBlockHeader); + if (err != Error.MSPACK_ERR_OK) + return err; + + // Blocks must not be over CAB_INPUTMAX in size + full_len = (d.InputEnd - d.InputPointer) + dataBlockHeader.CompressedSize; // Include cab-spanning blocks + if (full_len > CAB_INPUTMAX) + { + Console.WriteLine($"Block size {full_len} > CAB_INPUTMAX"); + + // In salvage mode, blocks can be 65535 bytes but no more than that + if (!ignore_blocksize || full_len > CAB_INPUTMAX_SALVAGE) + return Error.MSPACK_ERR_DATAFORMAT; + } + + // Blocks must not expand to more than CAB_BLOCKMAX + if (dataBlockHeader.UncompressedSize > CAB_BLOCKMAX) + { + Console.WriteLine("block size > CAB_BLOCKMAX"); + if (!ignore_blocksize) + return Error.MSPACK_ERR_DATAFORMAT; + } + + // Read the block data + if (SystemImpl.DefaultSystem.Read(d.InputFileHandle, d.Input, d.InputEnd, dataBlockHeader.CompressedSize) != dataBlockHeader.CompressedSize) + return Error.MSPACK_ERR_READ; + + // Perform checksum test on the block (if one is stored) + if (dataBlockHeader.CheckSum != 0) + { + uint sum2 = Checksum(d.Input, d.InputEnd, dataBlockHeader.CompressedSize, 0); + if (Checksum(hdr, 4, 4, sum2) != dataBlockHeader.CheckSum) + { + if (!ignore_cksum) + return Error.MSPACK_ERR_CHECKSUM; + + sys.Message(d.InputFileHandle, "WARNING; bad block checksum found"); + } + } + + // Advance end of block pointer to include newly read data + d.InputEnd += dataBlockHeader.CompressedSize; + + // Uncompressed size == 0 means this block was part of a split block + // and it continues as the first block of the next cabinet in the set. + // Otherwise, this is the last part of the block, and no more block + // reading needs to be done. + + // EXIT POINT OF LOOP -- uncompressed size != 0 + if ((output = dataBlockHeader.UncompressedSize) != 0) + return Error.MSPACK_ERR_OK; + + // Otherwise, advance to next cabinet + + // Close current file handle + sys.Close(d.InputFileHandle); + d.InputFileHandle = null; + + // Advance to next member in the cabinet set + if ((d.Data = d.Data.Next) == null) + { + sys.Message(d.InputFileHandle, "WARNING; ran out of cabinets in set. Are any missing?"); + return Error.MSPACK_ERR_DATAFORMAT; + } + + // Open next cab file + d.InputCabinet = d.Data.Cab; + if ((d.InputFileHandle = sys.Open(d.InputCabinet.Filename, OpenMode.MSPACK_SYS_OPEN_READ)) == null) + return Error.MSPACK_ERR_OPEN; + + // Seek to start of data blocks + if (!sys.Seek(d.InputFileHandle, d.Data.Offset, SeekMode.MSPACK_SYS_SEEK_START)) + return Error.MSPACK_ERR_SEEK; + } while (true); + } + + private static uint Checksum(byte[] data, int pointer, uint bytes, uint cksum) + { + uint len, ul = 0; + + for (len = bytes >> 2; len-- != 0; pointer += 4) + { + cksum ^= (uint)((data[pointer + 0]) | (data[pointer + 1] << 8) | (data[pointer + 2] << 16) | (data[pointer + 3] << 24)); + } + + switch (bytes & 3) + { + case 3: + ul |= (uint)(data[pointer++] << 16); + ul |= (uint)(data[pointer++] << 8); + ul |= data[pointer]; + break; + + case 2: + ul |= (uint)(data[pointer++] << 8); + ul |= data[pointer]; + break; + + case 1: + ul |= data[pointer]; + break; + } + + cksum ^= ul; + return cksum; + } + + #endregion + + #region Helpers + + /// + /// Decides if two folders are OK to merge + /// + private bool CanMergeFolders(Folder lfol, Folder rfol) + { + InternalFile lfi, rfi, l, r; + bool matching = true; + + // Check that both folders use the same compression method/settings + if (lfol.Header.CompType != rfol.Header.CompType) + { + Console.WriteLine("folder merge: compression type mismatch"); + return false; + } + + // Check there are not too many data blocks after merging + if ((lfol.Header.NumBlocks + rfol.Header.NumBlocks) > CAB_FOLDERMAX) + { + Console.WriteLine("folder merge: too many data blocks in merged folders"); + return false; + } + + if ((lfi = lfol.MergeNext) == null || (rfi = rfol.MergePrev) == null) + { + Console.WriteLine("folder merge: one cabinet has no files to merge"); + return false; + } + + // For all files in lfol (which is the last folder in whichever cab and + // only has files to merge), compare them to the files from rfol. They + // should be identical in number and order. to verify this, check the + // offset and length of each file. + for (l = lfi, r = rfi; l != null; l = l.Next, r = r.Next) + { + if (r == null || (l.Header.FolderOffset != r.Header.FolderOffset) || (l.Header.UncompressedSize != r.Header.UncompressedSize)) + { + matching = false; + break; + } + } + + if (matching) + return true; + + // If rfol does not begin with an identical copy of the files in lfol, make + // make a judgement call; if at least ONE file from lfol is in rfol, allow + // the merge with a warning about missing files. + matching = false; + for (l = lfi; l != null; l = l.Next) + { + for (r = rfi; r != null; r = r.Next) + { + if (l.Header.FolderOffset == r.Header.FolderOffset && l.Header.UncompressedSize == r.Header.UncompressedSize) + break; + } + + if (r != null) + matching = true; + else + System.Message(null, $"WARNING; merged file {l.Filename} not listed in both cabinets"); + } + + return matching; + } + + /// + /// The inner loop of , to make it easier to + /// break out of the loop and be sure that all resources are freed + /// + private Error Find(byte[] buf, DefaultFileImpl fh, string filename, long flen, ref long firstlen, out Cabinet firstcab) + { + firstcab = null; + Cabinet cab, link = null; + long caboff, offset, length; + long p, pend; + byte state = 0; + uint cablen_u32 = 0, foffset_u32 = 0; + int false_cabs = 0; + + // Search through the full file length + for (offset = 0; offset < flen; offset += length) + { + // Search length is either the full length of the search buffer, or the + // amount of data remaining to the end of the file, whichever is less. + length = flen - offset; + if (length > SearchBufferSize) + length = SearchBufferSize; + + // Fill the search buffer with data from disk + if (System.Read(fh, buf, 0, (int)length) != (int)length) + return Error.MSPACK_ERR_READ; + + // FAQ avoidance strategy + if (offset == 0 && BitConverter.ToUInt32(buf, 0) == 0x28635349) + System.Message(fh, "WARNING; found InstallShield header. Use unshield (https://github.com/twogood/unshield) to unpack this file"); + + // Read through the entire buffer. + for (p = 0, pend = length; p < pend;) + { + switch (state) + { + // Starting state + case 0: + // We spend most of our time in this while loop, looking for + // a leading 'M' of the 'MSCF' signature + while (p < pend && buf[p] != 0x4D) + { + p++; + } + + // If we found that 'M', advance state + if (p++ < pend) + state = 1; + + break; + + // Verify that the next 3 bytes are 'S', 'C' and 'F' + case 1: + state = (byte)(buf[p++] == 0x53 ? 2 : 0); + break; + case 2: + state = (byte)(buf[p++] == 0x43 ? 3 : 0); + break; + case 3: + state = (byte)(buf[p++] == 0x46 ? 4 : 0); + break; + + // We don't care about bytes 4-7 (see default: for action) + + // Bytes 8-11 are the overall length of the cabinet + case 8: + cablen_u32 = buf[p++]; + state++; + break; + case 9: + cablen_u32 |= (uint)buf[p++] << 8; + state++; + break; + case 10: + cablen_u32 |= (uint)buf[p++] << 16; + state++; + break; + case 11: + cablen_u32 |= (uint)buf[p++] << 24; + state++; + break; + + // We don't care about bytes 12-15 (see default: for action) + + // Bytes 16-19 are the offset within the cabinet of the filedata */ + case 16: + foffset_u32 = buf[p++]; + state++; + break; + case 17: + foffset_u32 |= (uint)buf[p++] << 8; + state++; + break; + case 18: + foffset_u32 |= (uint)buf[p++] << 16; + state++; + break; + case 19: + foffset_u32 |= (uint)buf[p++] << 24; + + // Now we have recieved 20 bytes of potential cab header. work out + // the offset in the file of this potential cabinet + caboff = offset + p - 20; + + // Should reading cabinet fail, restart search just after 'MSCF' + offset = caboff + 4; + + // Vapture the "length of cabinet" field if there is a cabinet at + // offset 0 in the file, regardless of whether the cabinet can be + // read correctly or not + if (caboff == 0) + firstlen = cablen_u32; + + // Check that the files offset is less than the alleged length of + // the cabinet, and that the offset + the alleged length are + // 'roughly' within the end of overall file length. In salvage + // mode, don't check the alleged length, allow it to be garbage */ + if ((foffset_u32 < cablen_u32) && + ((caboff + foffset_u32) < (flen + 32)) && + (((caboff + cablen_u32) < (flen + 32)) || Salvage)) + { + // Likely cabinet found -- try reading it + cab = new Cabinet() { Filename = filename }; + + if (ReadHeaders(fh, cab, caboff, Salvage, quiet: true) != Error.MSPACK_ERR_OK) + { + // Destroy the failed cabinet + Close(cab); + false_cabs++; + } + else + { + // Cabinet read correctly! + + // Link the cab into the list + if (link == null) + firstcab = cab; + else + link.Next = cab; + + link = cab; + + // Cause the search to restart after this cab's data. + offset = caboff + cablen_u32; + } + } + + // Restart search + if (offset >= flen) + return Error.MSPACK_ERR_OK; + + if (!System.Seek(fh, offset, SeekMode.MSPACK_SYS_SEEK_START)) + return Error.MSPACK_ERR_SEEK; + + length = 0; + p = pend; + state = 0; + break; + + // For bytes 4-7 and 12-15, just advance state/pointer + default: + p++; + state++; + break; + } + } + } + + if (false_cabs != 0) + Console.WriteLine($"{false_cabs} false cabinets found"); + + return Error.MSPACK_ERR_OK; + } + + /// + /// Joins cabinets together, also merges split folders between these two + /// cabinets only. This includes freeing the duplicate folder and file(s) + /// and allocating a further mscabd_folder_data structure to append to the + /// merged folder's data parts list. + /// + private Error Merge(Cabinet lcab, Cabinet rcab) + { + FolderData data, ndata; + Folder lfol, rfol; + InternalFile fi, rfi, lfi; + + // Basic args check + if (lcab == null || rcab == null || (lcab == rcab)) + { + Console.WriteLine("lcab null, rcab null or lcab = rcab"); + return Error = Error.MSPACK_ERR_ARGS; + } + + // Check there's not already a cabinet attached + if (lcab.NextCabinet != null || rcab.PreviousCabinet != null) + { + Console.WriteLine("Cabs already joined"); + return Error = Error.MSPACK_ERR_ARGS; + } + + // Do not create circular cabinet chains + Cabinet cab; + for (cab = lcab.PreviousCabinet; cab != null; cab = cab.PreviousCabinet) + { + if (cab == rcab) + { + Console.WriteLine("circular!"); + return Error = Error.MSPACK_ERR_ARGS; + } + } + for (cab = rcab.NextCabinet; cab != null; cab = cab.NextCabinet) + { + if (cab == lcab) + { + Console.WriteLine("circular!"); + return Error = Error.MSPACK_ERR_ARGS; + } + } + + // Warn about odd set IDs or indices + if (lcab.Header.SetID != rcab.Header.SetID) + System.Message(null, "WARNING; merged cabinets with differing Set IDs."); + + if (lcab.Header.CabinetIndex > rcab.Header.CabinetIndex) + System.Message(null, "WARNING; merged cabinets with odd order."); + + // Merging the last folder in lcab with the first folder in rcab + lfol = lcab.Folders; + rfol = rcab.Folders; + while (lfol.Next != null) + { + lfol = lfol.Next; + } + + // Do we need to merge folders? + if (lfol.MergeNext == null && rfol.MergePrev == null) + { + // No, at least one of the folders is not for merging + + // Attach cabs + lcab.NextCabinet = rcab; + rcab.PreviousCabinet = lcab; + + // Attach folders + lfol.Next = rfol; + + // Attach files + fi = lcab.Files; + while (fi.Next != null) + { + fi = fi.Next; + } + + fi.Next = rcab.Files; + } + else + { + // Folder merge required - do the files match? + if (!CanMergeFolders(lfol, rfol)) + return Error = Error.MSPACK_ERR_DATAFORMAT; + + // Allocate a new folder data structure + data = new FolderData(); + + // Attach cabs + lcab.NextCabinet = rcab; + rcab.PreviousCabinet = lcab; + + // Append rfol's data to lfol + ndata = lfol.Data; + while (ndata.Next != null) + { + ndata = ndata.Next; + } + + ndata.Next = data; + data = rfol.Data; + rfol.Data.Next = null; + + // lfol becomes rfol. + // NOTE: special case, don't merge if rfol is merge prev and next, + // rfol.MergeNext is going to be deleted, so keep lfol's version + // instead + lfol.Header.NumBlocks += (ushort)(rfol.Header.NumBlocks - 1); + if ((rfol.MergeNext == null) || (rfol.MergeNext.Folder != rfol)) + lfol.MergeNext = rfol.MergeNext; + + // Attach the rfol's folder (except the merge folder) + while (lfol.Next != null) + { + lfol = lfol.Next; + } + + lfol.Next = rfol.Next; + + // Attach rfol's files + fi = lcab.Files; + while (fi.Next != null) + { + fi = fi.Next; + } + + fi.Next = rcab.Files; + + // Delete all files from rfol's merge folder + lfi = null; + for (fi = lcab.Files; fi != null; fi = rfi) + { + rfi = fi.Next; + + // If file's folder matches the merge folder, unlink and free it + if (fi.Folder == rfol) + { + if (lfi != null) + lfi.Next = rfi; + else + lcab.Files = rfi; + } + else + { + lfi = fi; + } + } + } + + // All done! fix files and folders pointers in alsl cabs so they all + // point to the same list + for (cab = lcab.PreviousCabinet; cab != null; cab = cab.PreviousCabinet) + { + cab.Files = lcab.Files; + cab.Folders = lcab.Folders; + } + + for (cab = lcab.NextCabinet; cab != null; cab = cab.NextCabinet) + { + cab.Files = lcab.Files; + cab.Folders = lcab.Folders; + } + + return Error = Error.MSPACK_ERR_OK; + } + + /// + /// Reads the cabinet file header, folder list and file list. + /// Fills out a pre-existing Cabinet structure, allocates memory + /// for folders and files as necessary + /// + private Error ReadHeaders(DefaultFileImpl fh, Cabinet cab, long offset, bool salvage, bool quiet) + { + Error err = Error.MSPACK_ERR_OK; + Folder fol, linkfol = null; + InternalFile linkfile = null; + byte[] buf = new byte[64]; + + // Initialise pointers + if (cab == null) + cab = new Cabinet(); + + cab.Next = null; + cab.Files = null; + cab.Folders = null; + cab.PreviousCabinet = cab.NextCabinet = null; + cab.PreviousName = cab.NextName = null; + cab.PreviousInfo = cab.NextInfo = null; + + cab.BaseOffset = offset; + + // Seek to CFHEADER + if (!System.Seek(fh, offset, SeekMode.MSPACK_SYS_SEEK_START)) + return Error.MSPACK_ERR_SEEK; + + // Read in the CFHEADER + if (System.Read(fh, buf, 0, _CabinetHeader.Size) != _CabinetHeader.Size) + return Error.MSPACK_ERR_READ; + + // Create a new header based on that + err = _CabinetHeader.Create(buf, out _CabinetHeader cabinetHeader); + if (err != Error.MSPACK_ERR_OK) + return err; + + // Assign the header + cab.Header = cabinetHeader; + + // Check for the extended header + if (cab.Header.Flags.HasFlag(HeaderFlags.MSCAB_HDR_RESV)) + { + if (System.Read(fh, buf, 0, _CabinetHeader.ExtendedSize) != _CabinetHeader.ExtendedSize) + return Error.MSPACK_ERR_READ; + + // Populate the extended header + cabinetHeader.PopulateExtendedHeader(buf); + + // Skip the reserved header + if (cab.Header.HeaderReserved != 0) + { + if (!System.Seek(fh, cab.Header.HeaderReserved, SeekMode.MSPACK_SYS_SEEK_CUR)) + return Error.MSPACK_ERR_SEEK; + } + } + + // Read name and info of preceeding cabinet in set, if present + if (cab.Header.Flags.HasFlag(HeaderFlags.MSCAB_HDR_PREVCAB)) + { + cab.PreviousName = ReadString(fh, false, ref err); + if (err != Error.MSPACK_ERR_OK) + return err; + + cab.PreviousInfo = ReadString(fh, true, ref err); + if (err != Error.MSPACK_ERR_OK) + return err; + } + + // Read name and info of next cabinet in set, if present + if (cab.Header.Flags.HasFlag(HeaderFlags.MSCAB_HDR_NEXTCAB)) + { + cab.NextName = ReadString(fh, false, ref err); + if (err != Error.MSPACK_ERR_OK) + return err; + + cab.NextInfo = ReadString(fh, true, ref err); + if (err != Error.MSPACK_ERR_OK) + return err; + } + + // Read folders + for (int i = 0; i < cab.Header.NumFolders; i++) + { + // Read in the FOHEADER + if (System.Read(fh, buf, 0, _FolderHeader.Size) != _FolderHeader.Size) + return Error.MSPACK_ERR_READ; + + if (cab.Header.FolderReserved != 0) + { + if (!System.Seek(fh, cab.Header.FolderReserved, SeekMode.MSPACK_SYS_SEEK_CUR)) + return Error.MSPACK_ERR_SEEK; + } + + // Create an empty folder + fol = new Folder() + { + Next = null, + MergePrev = null, + MergeNext = null, + }; + + // Create a new header based on that + err = _FolderHeader.Create(buf, out _FolderHeader folderHeader); + if (err != Error.MSPACK_ERR_OK) + return err; + + // Assign the header + fol.Header = folderHeader; + + // Set the folder data fields + fol.Data = new FolderData(); + fol.Data.Next = null; + fol.Data.Cab = cab; + fol.Data.Offset = offset + fol.Header.DataOffset; + + // Link folder into list of folders + if (linkfol == null) + cab.Folders = fol; + else + linkfol.Next = fol; + + linkfol = fol; + } + + // Read files + for (int i = 0; i < cab.Header.NumFiles; i++) + { + // Read in the FIHEADER + if (System.Read(fh, buf, 0, _FileHeader.Size) != _FileHeader.Size) + return Error.MSPACK_ERR_READ; + + InternalFile file = new InternalFile() { Next = null }; + + // Create a new header based on that + err = _FileHeader.Create(buf, out _FileHeader fileHeader); + if (err != Error.MSPACK_ERR_OK) + return err; + + // Assign the header + file.Header = fileHeader; + + // Set folder pointer + if (file.Header.FolderIndex < FileFlags.CONTINUED_FROM_PREV) + { + // Normal folder index; count up to the correct folder + if ((int)file.Header.FolderIndex < cab.Header.NumFolders) + { + Folder ifol = cab.Folders; + while (file.Header.FolderIndex-- != 0) + { + if (ifol != null) + ifol = ifol.Next; + } + + file.Folder = ifol; + } + else + { + Console.WriteLine("Invalid folder index"); + file.Folder = null; + } + } + else + { + // Either CONTINUED_TO_NEXT, CONTINUED_FROM_PREV or CONTINUED_PREV_AND_NEXT + if (file.Header.FolderIndex == FileFlags.CONTINUED_TO_NEXT || file.Header.FolderIndex == FileFlags.CONTINUED_PREV_AND_NEXT) + { + // Get last folder + Folder ifol = cab.Folders; + while (ifol.Next != null) + { + ifol = ifol.Next; + } + + file.Folder = ifol; + + // Set "merge next" pointer + fol = ifol; + if (fol.MergeNext == null) + fol.MergeNext = file; + } + + if (file.Header.FolderIndex == FileFlags.CONTINUED_FROM_PREV || file.Header.FolderIndex == FileFlags.CONTINUED_PREV_AND_NEXT) + { + // Get first folder + file.Folder = cab.Folders; + + // Set "merge prev" pointer + fol = file.Folder; + if (fol.MergePrev == null) + fol.MergePrev = file; + } + } + + // Get filename + file.Filename = ReadString(fh, false, ref err); + + // If folder index or filename are bad, either skip it or fail + if (err != Error.MSPACK_ERR_OK || file.Folder == null) + { + if (salvage) + continue; + + return err != Error.MSPACK_ERR_OK ? err : Error.MSPACK_ERR_DATAFORMAT; + } + + // Link file entry into file list + if (linkfile == null) + cab.Files = file; + else + linkfile.Next = file; + + linkfile = file; + } + + if (cab.Files == null) + { + // We never actually added any files to the file list. Something went wrong. + // The file header may have been invalid */ + Console.WriteLine($"No files found, even though header claimed to have {cab.Header.NumFiles} files"); + return Error.MSPACK_ERR_DATAFORMAT; + } + + return Error.MSPACK_ERR_OK; + } + + private string ReadString(DefaultFileImpl fh, bool permitEmpty, ref Error error) + { + long position = System.Tell(fh); + byte[] buf = new byte[256]; + int len, i; + + // Read up to 256 bytes + if ((len = System.Read(fh, buf, 0, 256)) <= 0) + { + error = Error.MSPACK_ERR_READ; + return null; + } + + // Search for a null terminator in the buffer + bool ok = false; + for (i = 0; i < len; i++) + { + if (buf[i] == 0x00) + { + ok = true; + break; + } + } + + // Optionally reject empty strings + if (i == 0 && !permitEmpty) + ok = false; + + if (!ok) + { + error = Error.MSPACK_ERR_DATAFORMAT; + return null; + } + + len = i + 1; + + // Set the data stream to just after the string and return + if (!System.Seek(fh, position + len, SeekMode.MSPACK_SYS_SEEK_START)) + { + error = Error.MSPACK_ERR_SEEK; + return null; + } + + error = Error.MSPACK_ERR_OK; + return Encoding.ASCII.GetString(buf, 0, len); + } + + #endregion } } diff --git a/BurnOutSharp/External/libmspack/CAB/DecompressorImpl.cs b/BurnOutSharp/External/libmspack/CAB/DecompressorImpl.cs deleted file mode 100644 index 8a98b89d..00000000 --- a/BurnOutSharp/External/libmspack/CAB/DecompressorImpl.cs +++ /dev/null @@ -1,30 +0,0 @@ -/* This file is part of libmspack. - * (C) 2003-2018 Stuart Caie. - * - * libmspack is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License (LGPL) version 2.1 - * - * For further details, see the file COPYING.LIB distributed with libmspack - */ - -namespace LibMSPackSharp.CAB -{ - public class DecompressorImpl : Decompressor - { - public DecompressState State { get; set; } - - public SystemImpl System { get; set; } - - public int BufferSize { get; set; } - - public int SearchBufferSize { get; set; } - - public bool FixMSZip { get; set; } - - public bool Salvage { get; set; } - - public Error Error { get; set; } - - public Error ReadError { get; set; } - } -} diff --git a/BurnOutSharp/External/libmspack/CAB/Implementation.cs b/BurnOutSharp/External/libmspack/CAB/Implementation.cs deleted file mode 100644 index e1936576..00000000 --- a/BurnOutSharp/External/libmspack/CAB/Implementation.cs +++ /dev/null @@ -1,1586 +0,0 @@ -/* This file is part of libmspack. - * (C) 2003-2018 Stuart Caie. - * - * libmspack is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License (LGPL) version 2.1 - * - * For further details, see the file COPYING.LIB distributed with libmspack - */ - -/* Cabinet (.CAB) files are a form of file archive. Each cabinet contains - * "folders", which are compressed spans of data. Each cabinet has - * "files", whose metadata is in the cabinet header, but whose actual data - * is stored compressed in one of the "folders". Cabinets can span more - * than one physical file on disk, in which case they are a "cabinet set", - * and usually the last folder of each cabinet extends into the next - * cabinet. - * - * For a complete description of the format, see the MSDN site: - * http://msdn.microsoft.com/en-us/library/bb267310.aspx - */ - -/* Notes on compliance with cabinet specification: - * - * One of the main changes between cabextract 0.6 and libmspack's cab - * decompressor is the move from block-oriented decompression to - * stream-oriented decompression. - * - * cabextract would read one data block from disk, decompress it with the - * appropriate method, then write the decompressed data. The CAB - * specification is specifically designed to work like this, as it ensures - * compression matches do not span the maximum decompressed block size - * limit of 32kb. - * - * However, the compression algorithms used are stream oriented, with - * specific hacks added to them to enforce the "individual 32kb blocks" - * rule in CABs. In other file formats, they do not have this limitation. - * - * In order to make more generalised decompressors, libmspack's CAB - * decompressor has moved from being block-oriented to more stream - * oriented. This also makes decompression slightly faster. - * - * However, this leads to incompliance with the CAB specification. The - * CAB controller can no longer ensure each block of input given to the - * decompressors is matched with their output. The "decompressed size" of - * each individual block is thrown away. - * - * Each CAB block is supposed to be seen as individually compressed. This - * means each consecutive data block can have completely different - * "uncompressed" sizes, ranging from 1 to 32768 bytes. However, in - * reality, all data blocks in a folder decompress to exactly 32768 bytes, - * excepting the final block. - * - * Given this situation, the decompression algorithms are designed to - * realign their input bitstreams on 32768 output-byte boundaries, and - * various other special cases have been made. libmspack will not - * correctly decompress LZX or Quantum compressed folders where the blocks - * do not follow this "32768 bytes until last block" pattern. It could be - * implemented if needed, but hopefully this is not necessary -- it has - * not been seen in over 3Gb of CAB archives. - */ - -using System; -using System.Text; -using LibMSPackSharp.Compression; - -namespace LibMSPackSharp.CAB -{ - public static class Implementation - { - #region Generic CAB Definitions - - // CAB data blocks are <= 32768 bytes in uncompressed form.Uncompressed - // blocks have zero growth. MSZIP guarantees that it won't grow above - // uncompressed size by more than 12 bytes.LZX guarantees it won't grow - // more than 6144 bytes.Quantum has no documentation, but the largest - // block seen in the wild is 337 bytes above uncompressed size. - - public const int CAB_BLOCKMAX = 32768; - public const int CAB_INPUTMAX = CAB_BLOCKMAX + 6144; - - // input buffer needs to be CAB_INPUTMAX + 1 byte to allow for max-sized block - // plus 1 trailer byte added by cabd_sys_read_block() for Quantum alignment. - // - // When MSCABD_PARAM_SALVAGE is set, block size is not checked so can be - // up to 65535 bytes, so max input buffer size needed is 65535 + 1 - - public const int CAB_INPUTMAX_SALVAGE = 65535; - public const int CAB_INPUTBUF = CAB_INPUTMAX_SALVAGE + 1; - - // There are no more than 65535 data blocks per folder, so a folder cannot - // be more than 32768*65535 bytes in length.As files cannot span more than - // one folder, this is also their max offset, length and offset+length limit. - - public const int CAB_FOLDERMAX = 65535; - public const int CAB_LENGTHMAX = CAB_BLOCKMAX * CAB_FOLDERMAX; - - #endregion - - #region CABD_OPEN - - /// - /// Opens a file and tries to read it as a cabinet file - /// - public static Cabinet Open(Decompressor d, string filename) - { - DecompressorImpl self = d as DecompressorImpl; - Cabinet cab = null; - - if (self == null) - return null; - - SystemImpl system = self.System; - object fileHandle; - if ((fileHandle = system.Open(system, filename, OpenMode.MSPACK_SYS_OPEN_READ)) != null) - { - cab = new Cabinet(); - cab.Filename = filename; - Error error = ReadHeaders(system, fileHandle, cab, 0, self.Salvage, false); - if (error != Error.MSPACK_ERR_OK) - { - Close(self, cab); - cab = null; - } - - self.Error = error; - system.Close(fileHandle); - } - else - { - self.Error = Error.MSPACK_ERR_OPEN; - } - - return cab; - } - - #endregion - - #region CABD_CLOSE - - /// - /// Frees all memory associated with a given Cabinet. - /// - public static void Close(Decompressor d, Cabinet origcab) - { - DecompressorImpl self = d as DecompressorImpl; - - FolderData dat, ndat; - Cabinet cab, ncab; - Folder fol, nfol; - InternalFile fi, nfi; - - if (self == null) - return; - - SystemImpl sys = self.System; - - self.Error = Error.MSPACK_ERR_OK; - - while (origcab != null) - { - // Free files - for (fi = origcab.Files; fi != null; fi = nfi) - { - nfi = fi.Next; - sys.Free(fi.Filename); - sys.Free(fi); - } - - // Free folders - for (fol = origcab.Folders; fol != null; fol = nfol) - { - nfol = fol.Next; - - // Free folder decompression state if it has been decompressed - if (self.State != null && (self.State.Folder == fol)) - { - if (self.State.InputFileHandle != null) - sys.Close(self.State.InputFileHandle); - - FreeDecompressionState(self); - sys.Free(self.State); - self.State = null; - } - - // Free folder data segments - for (dat = fol.Data.Next; dat != null; dat = ndat) - { - ndat = dat.Next; - sys.Free(dat); - } - - sys.Free(fol); - } - - // Free predecessor cabinets (and the original cabinet's strings) - for (cab = origcab; cab == null; cab = ncab) - { - ncab = cab.PreviousCabinet; - sys.Free(cab.PreviousName); - sys.Free(cab.NextName); - sys.Free(cab.PreviousInfo); - sys.Free(cab.NextInfo); - if (cab != origcab) - sys.Free(cab); - } - - // Free successor cabinets - for (cab = origcab.NextCabinet; cab != null; cab = ncab) - { - ncab = cab.NextCabinet; - sys.Free(cab.PreviousName); - sys.Free(cab.NextName); - sys.Free(cab.PreviousInfo); - sys.Free(cab.NextInfo); - sys.Free(cab); - } - - // Free actual cabinet structure - cab = origcab.Next; - sys.Free(origcab); - - // Repeat full procedure again with the cab.Next pointer (if set) - origcab = cab; - } - } - - #endregion - - #region CABD_READ_HEADERS - - /// - /// Reads the cabinet file header, folder list and file list. - /// Fills out a pre-existing Cabinet structure, allocates memory - /// for folders and files as necessary - /// - public static Error ReadHeaders(SystemImpl sys, object fh, Cabinet cab, long offset, bool salvage, bool quiet) - { - int i, x; - Error err = Error.MSPACK_ERR_OK; - Folder fol, linkfol = null; - InternalFile file, linkfile = null; - byte[] buf = new byte[64]; - - // Initialise pointers - if (cab == null) - cab = new Cabinet(); - - cab.Next = null; - cab.Files = null; - cab.Folders = null; - cab.PreviousCabinet = cab.NextCabinet = null; - cab.PreviousName = cab.NextName = null; - cab.PreviousInfo = cab.NextInfo = null; - - cab.BaseOffset = offset; - - // Seek to CFHEADER - if (!sys.Seek(fh, offset, SeekMode.MSPACK_SYS_SEEK_START)) - return Error.MSPACK_ERR_SEEK; - - // Read in the CFHEADER - if (sys.Read(fh, buf, 0, _CabinetHeader.Size) != _CabinetHeader.Size) - return Error.MSPACK_ERR_READ; - - // Create a new header based on that - err = _CabinetHeader.Create(buf, out _CabinetHeader cabinetHeader); - if (err != Error.MSPACK_ERR_OK) - return err; - - // Assign the header - cab.Header = cabinetHeader; - - // Check for the extended header - if (cab.Header.Flags.HasFlag(HeaderFlags.MSCAB_HDR_RESV)) - { - if (sys.Read(fh, buf, 0, _CabinetHeader.ExtendedSize) != _CabinetHeader.ExtendedSize) - return Error.MSPACK_ERR_READ; - - // Populate the extended header - cabinetHeader.PopulateExtendedHeader(buf); - - // Skip the reserved header - if (cab.Header.HeaderReserved != 0) - { - if (!sys.Seek(fh, cab.Header.HeaderReserved, SeekMode.MSPACK_SYS_SEEK_CUR)) - return Error.MSPACK_ERR_SEEK; - } - } - - // Read name and info of preceeding cabinet in set, if present - if (cab.Header.Flags.HasFlag(HeaderFlags.MSCAB_HDR_PREVCAB)) - { - cab.PreviousName = ReadString(sys, fh, false, ref err); - if (err != Error.MSPACK_ERR_OK) - return err; - - cab.PreviousInfo = ReadString(sys, fh, true, ref err); - if (err != Error.MSPACK_ERR_OK) - return err; - } - - // Read name and info of next cabinet in set, if present - if (cab.Header.Flags.HasFlag(HeaderFlags.MSCAB_HDR_NEXTCAB)) - { - cab.NextName = ReadString(sys, fh, false, ref err); - if (err != Error.MSPACK_ERR_OK) - return err; - - cab.NextInfo = ReadString(sys, fh, true, ref err); - if (err != Error.MSPACK_ERR_OK) - return err; - } - - // Read folders - for (i = 0; i < cab.Header.NumFolders; i++) - { - // Read in the FOHEADER - if (sys.Read(fh, buf, 0, _FolderHeader.Size) != _FolderHeader.Size) - return Error.MSPACK_ERR_READ; - - if (cab.Header.FolderReserved != 0) - { - if (!sys.Seek(fh, cab.Header.FolderReserved, SeekMode.MSPACK_SYS_SEEK_CUR)) - return Error.MSPACK_ERR_SEEK; - } - - // Create an empty folder - fol = new Folder() - { - Next = null, - MergePrev = null, - MergeNext = null, - }; - - // Create a new header based on that - err = _FolderHeader.Create(buf, out _FolderHeader folderHeader); - if (err != Error.MSPACK_ERR_OK) - return err; - - // Assign the header - fol.Header = folderHeader; - - // Set the folder data fields - fol.Data = new FolderData(); - fol.Data.Next = null; - fol.Data.Cab = cab; - fol.Data.Offset = offset + fol.Header.DataOffset; - - // Link folder into list of folders - if (linkfol == null) - cab.Folders = fol; - else - linkfol.Next = fol; - - linkfol = fol; - } - - // Read files - for (i = 0; i < cab.Header.NumFiles; i++) - { - // Read in the FIHEADER - if (sys.Read(fh, buf, 0, _FileHeader.Size) != _FileHeader.Size) - return Error.MSPACK_ERR_READ; - - file = new InternalFile() { Next = null }; - - // Create a new header based on that - err = _FileHeader.Create(buf, out _FileHeader fileHeader); - if (err != Error.MSPACK_ERR_OK) - return err; - - // Assign the header - file.Header = fileHeader; - - // Set folder pointer - if (file.Header.FolderIndex < FileFlags.CONTINUED_FROM_PREV) - { - // Normal folder index; count up to the correct folder - if ((int)file.Header.FolderIndex < cab.Header.NumFolders) - { - Folder ifol = cab.Folders; - while (file.Header.FolderIndex-- != 0) - { - if (ifol != null) - ifol = ifol.Next; - } - - file.Folder = ifol; - } - else - { - Console.WriteLine("invalid folder index"); - file.Folder = null; - } - } - else - { - // Either CONTINUED_TO_NEXT, CONTINUED_FROM_PREV or CONTINUED_PREV_AND_NEXT - if (file.Header.FolderIndex == FileFlags.CONTINUED_TO_NEXT || file.Header.FolderIndex == FileFlags.CONTINUED_PREV_AND_NEXT) - { - // Get last folder - Folder ifol = cab.Folders; - while (ifol.Next != null) - { - ifol = ifol.Next; - } - - file.Folder = ifol; - - // Set "merge next" pointer - fol = ifol; - if (fol.MergeNext == null) - fol.MergeNext = file; - } - - if (file.Header.FolderIndex == FileFlags.CONTINUED_FROM_PREV || file.Header.FolderIndex == FileFlags.CONTINUED_PREV_AND_NEXT) - { - // Get first folder - file.Folder = cab.Folders; - - // Set "merge prev" pointer - fol = file.Folder; - if (fol.MergePrev == null) - fol.MergePrev = file; - } - } - - // Get filename - file.Filename = ReadString(sys, fh, false, ref err); - - // If folder index or filename are bad, either skip it or fail - if (err != Error.MSPACK_ERR_OK || file.Folder == null) - { - sys.Free(file.Filename); - sys.Free(file); - if (salvage) - continue; - - return err != Error.MSPACK_ERR_OK ? err : Error.MSPACK_ERR_DATAFORMAT; - } - - // Link file entry into file list - if (linkfile == null) - cab.Files = file; - else - linkfile.Next = file; - - linkfile = file; - } - - if (cab.Files == null) - { - // We never actually added any files to the file list. Something went wrong. - // The file header may have been invalid */ - Console.WriteLine($"No files found, even though header claimed to have {cab.Header.NumFiles} files"); - return Error.MSPACK_ERR_DATAFORMAT; - } - - return Error.MSPACK_ERR_OK; - } - - private static string ReadString(SystemImpl sys, object fh, bool permitEmpty, ref Error error) - { - long position = sys.Tell(fh); - byte[] buf = new byte[256]; - int len, i; - - // Read up to 256 bytes - if ((len = sys.Read(fh, buf, 0, 256)) <= 0) - { - error = Error.MSPACK_ERR_READ; - return null; - } - - // Search for a null terminator in the buffer - bool ok = false; - for (i = 0; i < len; i++) - { - if (buf[i] == 0x00) - { - ok = true; - break; - } - } - - // Optionally reject empty strings - if (i == 0 && !permitEmpty) - ok = false; - - if (!ok) - { - error = Error.MSPACK_ERR_DATAFORMAT; - return null; - } - - len = i + 1; - - // Set the data stream to just after the string and return - if (!sys.Seek(fh, position + len, SeekMode.MSPACK_SYS_SEEK_START)) - { - error = Error.MSPACK_ERR_SEEK; - return null; - } - - error = Error.MSPACK_ERR_OK; - return Encoding.ASCII.GetString(buf, 0, len); - } - - #endregion - - #region CABD_SEARCH, CABD_FIND - - /// - /// Opens a file, finds its extent, allocates a search buffer, - /// then reads through the whole file looking for possible cabinet headers. - /// If it finds any, it tries to read them as real cabinets. Returns a linked - /// list of results - /// - public static Cabinet Search(Decompressor d, string filename) - { - DecompressorImpl self = d as DecompressorImpl; - - if (self == null) - return null; - - SystemImpl sys = self.System; - - // Allocate a search buffer - byte[] search_buf = sys.Alloc(sys, self.SearchBufferSize); - if (search_buf == null) - { - self.Error = Error.MSPACK_ERR_NOMEMORY; - return null; - } - - // Open file and get its full file length - object fh; Cabinet cab = null; - if ((fh = sys.Open(sys, filename, OpenMode.MSPACK_SYS_OPEN_READ)) != null) - { - long firstlen = 0; - if ((self.Error = SystemImpl.GetFileLength(sys, fh, out long filelen)) == Error.MSPACK_ERR_OK) - self.Error = Find(self, search_buf, fh, filename, filelen, ref firstlen, out cab); - - // Truncated / extraneous data warning: - if (firstlen != 0 && (firstlen != filelen) && (cab == null || cab.BaseOffset == 0)) - { - if (firstlen < filelen) - sys.Message(fh, $"WARNING; possible {filelen - firstlen} extra bytes at end of file."); - else - sys.Message(fh, $"WARNING; file possibly truncated by {firstlen - filelen} bytes."); - } - - sys.Close(fh); - } - else - { - self.Error = Error.MSPACK_ERR_OPEN; - } - - // Free the search buffer - sys.Free(search_buf); - - return cab; - } - - /// - /// The inner loop of , to make it easier to - /// break out of the loop and be sure that all resources are freed - /// - public static Error Find(DecompressorImpl self, byte[] buf, object fh, string filename, long flen, ref long firstlen, out Cabinet firstcab) - { - firstcab = null; - Cabinet cab, link = null; - long caboff, offset, length; - SystemImpl sys = self.System; - long p, pend; - byte state = 0; - uint cablen_u32 = 0, foffset_u32 = 0; - int false_cabs = 0; - - // Search through the full file length - for (offset = 0; offset < flen; offset += length) - { - // Search length is either the full length of the search buffer, or the - // amount of data remaining to the end of the file, whichever is less. - length = flen - offset; - if (length > self.SearchBufferSize) - length = self.SearchBufferSize; - - // Fill the search buffer with data from disk - if (sys.Read(fh, buf, 0, (int)length) != (int)length) - return Error.MSPACK_ERR_READ; - - // FAQ avoidance strategy - if (offset == 0 && BitConverter.ToUInt32(buf, 0) == 0x28635349) - sys.Message(fh, "WARNING; found InstallShield header. Use unshield (https://github.com/twogood/unshield) to unpack this file"); - - // Read through the entire buffer. - for (p = 0, pend = length; p < pend;) - { - switch (state) - { - // Starting state - case 0: - // We spend most of our time in this while loop, looking for - // a leading 'M' of the 'MSCF' signature - while (p < pend && buf[p] != 0x4D) - { - p++; - } - - // If we found that 'M', advance state - if (p++ < pend) - state = 1; - - break; - - // Verify that the next 3 bytes are 'S', 'C' and 'F' - case 1: - state = (byte)(buf[p++] == 0x53 ? 2 : 0); - break; - case 2: - state = (byte)(buf[p++] == 0x43 ? 3 : 0); - break; - case 3: - state = (byte)(buf[p++] == 0x46 ? 4 : 0); - break; - - // We don't care about bytes 4-7 (see default: for action) - - // Bytes 8-11 are the overall length of the cabinet - case 8: - cablen_u32 = buf[p++]; - state++; - break; - case 9: - cablen_u32 |= (uint)buf[p++] << 8; - state++; - break; - case 10: - cablen_u32 |= (uint)buf[p++] << 16; - state++; - break; - case 11: - cablen_u32 |= (uint)buf[p++] << 24; - state++; - break; - - // We don't care about bytes 12-15 (see default: for action) - - // Bytes 16-19 are the offset within the cabinet of the filedata */ - case 16: - foffset_u32 = buf[p++]; - state++; - break; - case 17: - foffset_u32 |= (uint)buf[p++] << 8; - state++; - break; - case 18: - foffset_u32 |= (uint)buf[p++] << 16; - state++; - break; - case 19: - foffset_u32 |= (uint)buf[p++] << 24; - - // Now we have recieved 20 bytes of potential cab header. work out - // the offset in the file of this potential cabinet - caboff = offset + p - 20; - - // Should reading cabinet fail, restart search just after 'MSCF' - offset = caboff + 4; - - // Vapture the "length of cabinet" field if there is a cabinet at - // offset 0 in the file, regardless of whether the cabinet can be - // read correctly or not - if (caboff == 0) - firstlen = cablen_u32; - - // Check that the files offset is less than the alleged length of - // the cabinet, and that the offset + the alleged length are - // 'roughly' within the end of overall file length. In salvage - // mode, don't check the alleged length, allow it to be garbage */ - if ((foffset_u32 < cablen_u32) && - ((caboff + foffset_u32) < (flen + 32)) && - (((caboff + cablen_u32) < (flen + 32)) || self.Salvage)) - { - // Likely cabinet found -- try reading it - cab = new Cabinet() { Filename = filename }; - - if (ReadHeaders(sys, fh, cab, caboff, self.Salvage, quiet: true) != Error.MSPACK_ERR_OK) - { - // Destroy the failed cabinet - Close(self, cab); - false_cabs++; - } - else - { - // Cabinet read correctly! - - // Link the cab into the list - if (link == null) - firstcab = cab; - else - link.Next = cab; - - link = cab; - - // Cause the search to restart after this cab's data. - offset = caboff + cablen_u32; - } - } - - // Restart search - if (offset >= flen) - return Error.MSPACK_ERR_OK; - - if (!sys.Seek(fh, offset, SeekMode.MSPACK_SYS_SEEK_START)) - return Error.MSPACK_ERR_SEEK; - - length = 0; - p = pend; - state = 0; - break; - - // For bytes 4-7 and 12-15, just advance state/pointer - default: - p++; - state++; - break; - } - } - } - - if (false_cabs != 0) - Console.WriteLine($"{false_cabs} false cabinets found"); - - return Error.MSPACK_ERR_OK; - } - - #endregion - - #region CABD_MERGE, CABD_PREPEND, CABD_APPEND - - /// - public static Error Prepend(Decompressor d, Cabinet cab, Cabinet prevcab) - { - return Merge(d, prevcab, cab); - } - - /// - public static Error Append(Decompressor d, Cabinet cab, Cabinet nextcab) - { - return Merge(d, cab, nextcab); - } - - /// - /// Joins cabinets together, also merges split folders between these two - /// cabinets only. This includes freeing the duplicate folder and file(s) - /// and allocating a further mscabd_folder_data structure to append to the - /// merged folder's data parts list. - /// - public static Error Merge(Decompressor d, Cabinet lcab, Cabinet rcab) - { - DecompressorImpl self = d as DecompressorImpl; - - FolderData data, ndata; - Folder lfol, rfol; - InternalFile fi, rfi, lfi; - - if (self == null) - return Error.MSPACK_ERR_ARGS; - - SystemImpl sys = self.System; - - // Basic args check - if (lcab == null || rcab == null || (lcab == rcab)) - { - Console.WriteLine("lcab null, rcab null or lcab = rcab"); - return self.Error = Error.MSPACK_ERR_ARGS; - } - - // Check there's not already a cabinet attached - if (lcab.NextCabinet != null || rcab.PreviousCabinet != null) - { - Console.WriteLine("cabs already joined"); - return self.Error = Error.MSPACK_ERR_ARGS; - } - - // Do not create circular cabinet chains - Cabinet cab; - for (cab = lcab.PreviousCabinet; cab != null; cab = cab.PreviousCabinet) - { - if (cab == rcab) - { - Console.WriteLine("circular!"); - return self.Error = Error.MSPACK_ERR_ARGS; - } - } - for (cab = rcab.NextCabinet; cab != null; cab = cab.NextCabinet) - { - if (cab == lcab) - { - Console.WriteLine("circular!"); - return self.Error = Error.MSPACK_ERR_ARGS; - } - } - - // Warn about odd set IDs or indices - if (lcab.Header.SetID != rcab.Header.SetID) - sys.Message(null, "WARNING; merged cabinets with differing Set IDs."); - - if (lcab.Header.CabinetIndex > rcab.Header.CabinetIndex) - sys.Message(null, "WARNING; merged cabinets with odd order."); - - // Merging the last folder in lcab with the first folder in rcab - lfol = lcab.Folders; - rfol = rcab.Folders; - while (lfol.Next != null) - { - lfol = lfol.Next; - } - - // Do we need to merge folders? - if (lfol.MergeNext == null && rfol.MergePrev == null) - { - // No, at least one of the folders is not for merging - - // Attach cabs - lcab.NextCabinet = rcab; - rcab.PreviousCabinet = lcab; - - // Attach folders - lfol.Next = rfol; - - // Attach files - fi = lcab.Files; - while (fi.Next != null) - { - fi = fi.Next; - } - - fi.Next = rcab.Files; - } - else - { - // Folder merge required - do the files match? - if (!CanMergeFolders(sys, lfol, rfol)) - return self.Error = Error.MSPACK_ERR_DATAFORMAT; - - // Allocate a new folder data structure - data = new FolderData(); - - // Attach cabs - lcab.NextCabinet = rcab; - rcab.PreviousCabinet = lcab; - - // Append rfol's data to lfol - ndata = lfol.Data; - while (ndata.Next != null) - { - ndata = ndata.Next; - } - - ndata.Next = data; - data = rfol.Data; - rfol.Data.Next = null; - - // lfol becomes rfol. - // NOTE: special case, don't merge if rfol is merge prev and next, - // rfol.MergeNext is going to be deleted, so keep lfol's version - // instead - lfol.Header.NumBlocks += (ushort)(rfol.Header.NumBlocks - 1); - if ((rfol.MergeNext == null) || (rfol.MergeNext.Folder != rfol)) - lfol.MergeNext = rfol.MergeNext; - - // Attach the rfol's folder (except the merge folder) - while (lfol.Next != null) - { - lfol = lfol.Next; - } - - lfol.Next = rfol.Next; - - // Free disused merge folder - sys.Free(rfol); - - // Attach rfol's files - fi = lcab.Files; - while (fi.Next != null) - { - fi = fi.Next; - } - - fi.Next = rcab.Files; - - // Delete all files from rfol's merge folder - lfi = null; - for (fi = lcab.Files; fi != null; fi = rfi) - { - rfi = fi.Next; - - // If file's folder matches the merge folder, unlink and free it - if (fi.Folder == rfol) - { - if (lfi != null) - lfi.Next = rfi; - else - lcab.Files = rfi; - - sys.Free(fi.Filename); - sys.Free(fi); - } - else - { - lfi = fi; - } - } - } - - // All done! fix files and folders pointers in alsl cabs so they all - // point to the same list - for (cab = lcab.PreviousCabinet; cab != null; cab = cab.PreviousCabinet) - { - cab.Files = lcab.Files; - cab.Folders = lcab.Folders; - } - - for (cab = lcab.NextCabinet; cab != null; cab = cab.NextCabinet) - { - cab.Files = lcab.Files; - cab.Folders = lcab.Folders; - } - - return self.Error = Error.MSPACK_ERR_OK; - } - - /// - /// Decides if two folders are OK to merge - /// - private static bool CanMergeFolders(SystemImpl sys, Folder lfol, Folder rfol) - { - InternalFile lfi, rfi, l, r; - bool matching = true; - - // Check that both folders use the same compression method/settings - if (lfol.Header.CompType != rfol.Header.CompType) - { - Console.WriteLine("folder merge: compression type mismatch"); - return false; - } - - // Check there are not too many data blocks after merging - if ((lfol.Header.NumBlocks + rfol.Header.NumBlocks) > CAB_FOLDERMAX) - { - Console.WriteLine("folder merge: too many data blocks in merged folders"); - return false; - } - - if ((lfi = lfol.MergeNext) == null || (rfi = rfol.MergePrev) == null) - { - Console.WriteLine("folder merge: one cabinet has no files to merge"); - return false; - } - - // For all files in lfol (which is the last folder in whichever cab and - // only has files to merge), compare them to the files from rfol. They - // should be identical in number and order. to verify this, check the - // offset and length of each file. - for (l = lfi, r = rfi; l != null; l = l.Next, r = r.Next) - { - if (r == null || (l.Header.FolderOffset != r.Header.FolderOffset) || (l.Header.UncompressedSize != r.Header.UncompressedSize)) - { - matching = false; - break; - } - } - - if (matching) - return true; - - // If rfol does not begin with an identical copy of the files in lfol, make - // make a judgement call; if at least ONE file from lfol is in rfol, allow - // the merge with a warning about missing files. - matching = false; - for (l = lfi; l != null; l = l.Next) - { - for (r = rfi; r != null; r = r.Next) - { - if (l.Header.FolderOffset == r.Header.FolderOffset && l.Header.UncompressedSize == r.Header.UncompressedSize) - break; - } - - if (r != null) - matching = true; - else - sys.Message(null, $"WARNING; merged file {l.Filename} not listed in both cabinets"); - } - - return matching; - } - - #endregion - - #region CABD_EXTRACT - - /// - /// Extracts a file from a cabinet - /// - public static Error Extract(Decompressor d, InternalFile file, string filename) - { - DecompressorImpl self = d as DecompressorImpl; - object fh; - - if (self == null) - return Error.MSPACK_ERR_ARGS; - if (file == null) - return self.Error = Error.MSPACK_ERR_ARGS; - - SystemImpl sys = self.System; - Folder fol = file.Folder; - - // If offset is beyond 2GB, nothing can be extracted - if (file.Header.FolderOffset > CAB_LENGTHMAX) - return self.Error = Error.MSPACK_ERR_DATAFORMAT; - - // If file claims to go beyond 2GB either error out, - // or in salvage mode reduce file length so it fits 2GB limit - long filelen = file.Header.UncompressedSize; - if (filelen > CAB_LENGTHMAX || (file.Header.FolderOffset + filelen) > CAB_LENGTHMAX) - { - if (self.Salvage) - filelen = CAB_LENGTHMAX - file.Header.FolderOffset; - else - return self.Error = Error.MSPACK_ERR_DATAFORMAT; - } - - // Extraction impossible if no folder, or folder needs predecessor - if (fol == null || fol.MergePrev != null) - { - sys.Message(null, $"ERROR; file \"{file.Filename}\" cannot be extracted, cabinet set is incomplete"); - return self.Error = Error.MSPACK_ERR_DECRUNCH; - } - - // If file goes beyond what can be decoded, given an error. - // In salvage mode, don't assume block sizes, just try decoding - if (!self.Salvage) - { - long maxlen = fol.Header.NumBlocks * CAB_BLOCKMAX; - if ((file.Header.FolderOffset + filelen) > maxlen) - { - sys.Message(null, $"ERROR; file \"{file.Filename}\" cannot be extracted, cabinet set is incomplete"); - return self.Error = Error.MSPACK_ERR_DECRUNCH; - } - } - - // Allocate generic decompression state - if (self.State == null) - { - self.State = new DecompressState(); - self.State.Folder = null; - self.State.Data = null; - self.State.Sys = sys; - self.State.Sys.Read = SysRead; - self.State.Sys.Write = SysWrite; - self.State.DecompressorState = null; - self.State.InputFileHandle = null; - self.State.InputCabinet = null; - } - - // Do we need to change folder or reset the current folder? - if ((self.State.Folder != fol) || (self.State.Offset > file.Header.FolderOffset) || self.State.DecompressorState == null) - { - // Free any existing decompressor - FreeDecompressionState(self); - - // Do we need to open a new cab file? - if (self.State.InputFileHandle == null || (fol.Data.Cab != self.State.InputCabinet)) - { - // Close previous file handle if from a different cab - if (self.State.InputFileHandle != null) - sys.Close(self.State.InputFileHandle); - - self.State.InputCabinet = fol.Data.Cab; - self.State.InputFileHandle = sys.Open(sys, fol.Data.Cab.Filename, OpenMode.MSPACK_SYS_OPEN_READ); - if (self.State.InputFileHandle == null) - return self.Error = Error.MSPACK_ERR_OPEN; - } - - // Seek to start of data blocks - if (!sys.Seek(self.State.InputFileHandle, fol.Data.Offset, SeekMode.MSPACK_SYS_SEEK_START)) - return self.Error = Error.MSPACK_ERR_SEEK; - - // Set up decompressor - if (InitDecompressionState(self, fol.Header.CompType) != Error.MSPACK_ERR_OK) - return self.Error; - - // Initialise new folder state - self.State.Folder = fol; - self.State.Data = fol.Data; - self.State.Offset = 0; - self.State.Block = 0; - self.State.Outlen = 0; - self.State.InputPointer = self.State.InputEnd = 0; - - // read_error lasts for the lifetime of a decompressor - self.ReadError = Error.MSPACK_ERR_OK; - } - - // Open file for output - if ((fh = sys.Open(sys, filename, OpenMode.MSPACK_SYS_OPEN_WRITE)) == null) - return self.Error = Error.MSPACK_ERR_OPEN; - - self.Error = Error.MSPACK_ERR_OK; - - // If file has more than 0 bytes - if (filelen != 0) - { - long bytes; - Error error; - // Get to correct offset. - // - use null fh to say 'no writing' to cabd_sys_write() - // - if cabd_sys_read() has an error, it will set self.ReadError - // and pass back MSPACK_ERR_READ - self.State.OutputFileHandle = null; - if ((bytes = file.Header.FolderOffset - self.State.Offset) != 0) - { - error = self.State.Decompress(self.State.DecompressorState, bytes); - self.Error = (error == Error.MSPACK_ERR_READ) ? self.ReadError : error; - } - - // If getting to the correct offset was error free, unpack file - if (self.Error == Error.MSPACK_ERR_OK) - { - self.State.OutputFileHandle = fh; - error = self.State.Decompress(self.State.DecompressorState, filelen); - self.Error = (error == Error.MSPACK_ERR_READ) ? self.ReadError : error; - } - } - - // Close output file - sys.Close(fh); - self.State.OutputFileHandle = null; - - return self.Error; - } - - #endregion - - #region CABD_INIT_DECOMP, CABD_FREE_DECOMP - - /// - /// Initialises decompression state, according to which - /// decompression method was used. relies on self.State.Folder being the same - /// as when initialised. - /// - public static Error InitDecompressionState(DecompressorImpl self, CompressionType ct) - { - object fh = self; - - self.State.CompressionType = ct; - - switch (ct & CompressionType.COMPTYPE_MASK) - { - case CompressionType.COMPTYPE_NONE: - self.State.Decompress = NoneDecompress; - self.State.DecompressorState = NoneInit(self.State.Sys, fh, fh, self.BufferSize); - break; - - case CompressionType.COMPTYPE_MSZIP: - self.State.Decompress = MSZIP.Decompress; - self.State.DecompressorState = MSZIP.Init(self.State.Sys, fh, fh, self.BufferSize, self.FixMSZip); - break; - - case CompressionType.COMPTYPE_QUANTUM: - self.State.Decompress = QTM.Decompress; - self.State.DecompressorState = QTM.Init(self.State.Sys, fh, fh, ((ushort)ct >> 8) & 0x1f, self.BufferSize); - break; - - case CompressionType.COMPTYPE_LZX: - self.State.Decompress = LZX.Decompress; - self.State.DecompressorState = LZX.Init(self.State.Sys, fh, fh, ((ushort)ct >> 8) & 0x1f, 0, self.BufferSize, 0, false); - break; - - default: - return self.Error = Error.MSPACK_ERR_DATAFORMAT; - } - - return self.Error = (self.State.DecompressorState != null) ? Error.MSPACK_ERR_OK : Error.MSPACK_ERR_NOMEMORY; - } - - /// - /// Frees decompression state, according to which method was used. - /// - /// - public static void FreeDecompressionState(DecompressorImpl self) - { - if (self == null || self.State == null || self.State.DecompressorState == null) - return; - - switch (self.State.CompressionType & CompressionType.COMPTYPE_MASK) - { - case CompressionType.COMPTYPE_NONE: - NoneFree(self.State.DecompressorState); - break; - - case CompressionType.COMPTYPE_MSZIP: - MSZIP.Free(self.State.DecompressorState); - break; - - case CompressionType.COMPTYPE_QUANTUM: - QTM.Free(self.State.DecompressorState); - break; - - case CompressionType.COMPTYPE_LZX: - LZX.Free(self.State.DecompressorState); - break; - } - - self.State.Decompress = null; - self.State.DecompressorState = null; - } - - #endregion - - #region CABD_SYS_READ, CABD_SYS_WRITE - - /// - /// The internal reader function which the decompressors - /// use. will read data blocks (and merge split blocks) from the cabinet - /// and serve the read bytes to the decompressors - /// - private static int SysRead(object file, byte[] buffer, int pointer, int bytes) - { - DecompressorImpl self = file as DecompressorImpl; - if (self == null) - return 0; - - SystemImpl sys = self.System; - - int avail, todo, outlen = 0; - - bool ignore_cksum = self.Salvage || - (self.FixMSZip && - ((self.State.CompressionType & CompressionType.COMPTYPE_MASK) == CompressionType.COMPTYPE_MSZIP)); - bool ignore_blocksize = self.Salvage; - - todo = bytes; - while (todo > 0) - { - avail = self.State.InputEnd - self.State.InputPointer; - - // If out of input data, read a new block - if (avail != 0) - { - // Copy as many input bytes available as possible - if (avail > todo) - avail = todo; - - sys.Copy(self.State.Input, self.State.InputPointer, buffer, pointer, avail); - self.State.InputPointer += avail; - pointer += avail; - todo -= avail; - } - else - { - // Out of data, read a new block - - // Check if we're out of input blocks, advance block counter - if (self.State.Block++ >= self.State.Folder.Header.NumBlocks) - { - if (!self.Salvage) - self.ReadError = Error.MSPACK_ERR_DATAFORMAT; - else - Console.WriteLine("Ran out of CAB input blocks prematurely"); - - break; - } - - // Read a block - self.ReadError = SysReadBlock(sys, self.State, ref outlen, ignore_cksum, ignore_blocksize); - if (self.ReadError != Error.MSPACK_ERR_OK) - return -1; - - self.State.Outlen += outlen; - - // Special Quantum hack -- trailer byte to allow the decompressor - // to realign itself. CAB Quantum blocks, unlike LZX blocks, can have - // anything from 0 to 4 trailing null bytes. - if ((self.State.CompressionType & CompressionType.COMPTYPE_MASK) == CompressionType.COMPTYPE_QUANTUM) - self.State.Input[self.State.InputEnd++] = 0xFF; - - // Is this the last block? - if (self.State.Block >= self.State.Folder.Header.NumBlocks) - { - if ((self.State.CompressionType & CompressionType.COMPTYPE_MASK) == CompressionType.COMPTYPE_LZX) - { - // Special LZX hack -- on the last block, inform LZX of the - // size of the output data stream. - LZX.SetOutputLength(self.State.DecompressorState as LZXDStream, self.State.Outlen); - } - } - } - } - - return bytes - todo; - } - - /// - /// The internal writer function which the decompressors - /// use. it either writes data to disk (self.State.OutputFileHandle) with the real - /// sys.write() function, or does nothing with the data when - /// self.State.OutputFileHandle == null. advances self.State.Offset - /// - private static int SysWrite(object file, byte[] buffer, int pointer, int bytes) - { - if (file is DecompressorImpl self) - { - self.State.Offset += (uint)bytes; - if (self.State.OutputFileHandle != null) - return self.System.Write(self.State.OutputFileHandle, buffer, pointer, bytes); - - return bytes; - } - else if (file is DefaultFileImpl impl) - { - return SystemImpl.DefaultSystem.Write(file, buffer, pointer, bytes); - } - - // Unknown file to write to - return 0; - } - - #endregion - - #region CABD_SYS_READ_BLOCK - - /// - /// Reads a whole data block from a cab file. The block may span more than - /// one cab file, if it does then the fragments will be reassembled - /// - private static Error SysReadBlock(SystemImpl sys, DecompressState d, ref int output, bool ignore_cksum, bool ignore_blocksize) - { - byte[] hdr = new byte[_DataBlockHeader.Size]; - int full_len; - - // Reset the input block pointer and end of block pointer - d.InputPointer = d.InputEnd = 0; - - do - { - // Read the block header - if (SystemImpl.DefaultSystem.Read(d.InputFileHandle, hdr, 0, _DataBlockHeader.Size) != _DataBlockHeader.Size) - return Error.MSPACK_ERR_READ; - - // Skip any reserved block headers - if (d.Data.Cab.Header.HeaderReserved != 0 && !sys.Seek(d.InputFileHandle, d.Data.Cab.Header.HeaderReserved, SeekMode.MSPACK_SYS_SEEK_CUR)) - return Error.MSPACK_ERR_SEEK; - - // Create a block header from the data - Error err = _DataBlockHeader.Create(hdr, out _DataBlockHeader dataBlockHeader); - if (err != Error.MSPACK_ERR_OK) - return err; - - // Blocks must not be over CAB_INPUTMAX in size - full_len = (d.InputEnd - d.InputPointer) + dataBlockHeader.CompressedSize; // Include cab-spanning blocks - if (full_len > CAB_INPUTMAX) - { - Console.WriteLine($"Block size {full_len} > CAB_INPUTMAX"); - - // In salvage mode, blocks can be 65535 bytes but no more than that - if (!ignore_blocksize || full_len > CAB_INPUTMAX_SALVAGE) - return Error.MSPACK_ERR_DATAFORMAT; - } - - // Blocks must not expand to more than CAB_BLOCKMAX - if (dataBlockHeader.UncompressedSize > CAB_BLOCKMAX) - { - Console.WriteLine("block size > CAB_BLOCKMAX"); - if (!ignore_blocksize) - return Error.MSPACK_ERR_DATAFORMAT; - } - - // Read the block data - if (SystemImpl.DefaultSystem.Read(d.InputFileHandle, d.Input, d.InputEnd, dataBlockHeader.CompressedSize) != dataBlockHeader.CompressedSize) - return Error.MSPACK_ERR_READ; - - // Perform checksum test on the block (if one is stored) - if (dataBlockHeader.CheckSum != 0) - { - uint sum2 = Checksum(d.Input, d.InputEnd, dataBlockHeader.CompressedSize, 0); - if (Checksum(hdr, 4, 4, sum2) != dataBlockHeader.CheckSum) - { - if (!ignore_cksum) - return Error.MSPACK_ERR_CHECKSUM; - - sys.Message(d.InputFileHandle, "WARNING; bad block checksum found"); - } - } - - // Advance end of block pointer to include newly read data - d.InputEnd += dataBlockHeader.CompressedSize; - - // Uncompressed size == 0 means this block was part of a split block - // and it continues as the first block of the next cabinet in the set. - // Otherwise, this is the last part of the block, and no more block - // reading needs to be done. - - // EXIT POINT OF LOOP -- uncompressed size != 0 - if ((output = dataBlockHeader.UncompressedSize) != 0) - return Error.MSPACK_ERR_OK; - - // Otherwise, advance to next cabinet - - // Close current file handle - sys.Close(d.InputFileHandle); - d.InputFileHandle = null; - - // Advance to next member in the cabinet set - if ((d.Data = d.Data.Next) == null) - { - sys.Message(d.InputFileHandle, "WARNING; ran out of cabinets in set. Are any missing?"); - return Error.MSPACK_ERR_DATAFORMAT; - } - - // Open next cab file - d.InputCabinet = d.Data.Cab; - if ((d.InputFileHandle = sys.Open(sys, d.InputCabinet.Filename, OpenMode.MSPACK_SYS_OPEN_READ)) == null) - return Error.MSPACK_ERR_OPEN; - - // Seek to start of data blocks - if (!sys.Seek(d.InputFileHandle, d.Data.Offset, SeekMode.MSPACK_SYS_SEEK_START)) - return Error.MSPACK_ERR_SEEK; - } while (true); - } - - private static uint Checksum(byte[] data, int pointer, uint bytes, uint cksum) - { - uint len, ul = 0; - - for (len = bytes >> 2; len-- != 0; pointer += 4) - { - cksum ^= (uint)((data[pointer + 0]) | (data[pointer + 1] << 8) | (data[pointer + 2] << 16) | (data[pointer + 3] << 24)); - } - - switch (bytes & 3) - { - case 3: - ul |= (uint)(data[pointer++] << 16); - ul |= (uint)(data[pointer++] << 8); - ul |= data[pointer]; - break; - - case 2: - ul |= (uint)(data[pointer++] << 8); - ul |= data[pointer]; - break; - - case 1: - ul |= data[pointer]; - break; - } - - cksum ^= ul; - return cksum; - } - - #endregion - - #region NONED_INIT, NONED_DECOMPRESS, NONED_FREE - - internal static NoneState NoneInit(SystemImpl sys, object input, object output, int bufsize) - { - NoneState state = new NoneState(); - byte[] buf = sys.Alloc(sys, bufsize); - if (state != null && buf != null) - { - state.Sys = sys; - state.Input = input; - state.Output = output; - state.Buffer = buf; - state.BufferSize = bufsize; - } - else - { - sys.Free(buf); - sys.Free(state); - state = null; - } - - return state; - } - - internal static Error NoneDecompress(object s, long bytes) - { - NoneState state = (NoneState)s; - if (state == null) - return Error.MSPACK_ERR_ARGS; - - int run; - while (bytes > 0) - { - run = (bytes > state.BufferSize) ? state.BufferSize : (int)bytes; - - if (state.Sys.Read(state.Input, state.Buffer, 0, run) != run) - return Error.MSPACK_ERR_READ; - - if (state.Sys.Write(state.Output, state.Buffer, 0, run) != run) - return Error.MSPACK_ERR_WRITE; - - bytes -= run; - } - return Error.MSPACK_ERR_OK; - } - - internal static void NoneFree(object s) - { - NoneState state = s as NoneState; - if (state != null) - { - SystemImpl sys = state.Sys; - sys.Free(state.Buffer); - sys.Free(state); - } - } - - #endregion - - #region CABD_PARAM - - /// - /// Allows a parameter to be set - /// - public static Error Param(Decompressor d, Parameters param, int value) - { - DecompressorImpl self = d as DecompressorImpl; - if (self == null) - return Error.MSPACK_ERR_ARGS; - - switch (param) - { - case Parameters.MSCABD_PARAM_SEARCHBUF: - if (value < 4) - return Error.MSPACK_ERR_ARGS; - - self.SearchBufferSize = value; - break; - - case Parameters.MSCABD_PARAM_FIXMSZIP: - self.FixMSZip = value != 0; - break; - - case Parameters.MSCABD_PARAM_DECOMPBUF: - if (value < 4) - return Error.MSPACK_ERR_ARGS; - - self.BufferSize = value; - break; - - case Parameters.MSCABD_PARAM_SALVAGE: - self.Salvage = value != 0; - break; - - default: - return Error.MSPACK_ERR_ARGS; - } - - return Error.MSPACK_ERR_OK; - } - - #endregion - - #region CABD_ERROR - - /// - /// Returns the last error that occurred - /// - public static Error LastError(Decompressor d) - { - DecompressorImpl self = d as DecompressorImpl; - return (self != null) ? self.Error : Error.MSPACK_ERR_ARGS; - } - - #endregion - } -} diff --git a/BurnOutSharp/External/libmspack/CHM/DecompressState.cs b/BurnOutSharp/External/libmspack/CHM/DecompressState.cs index 24786506..6cab3a6c 100644 --- a/BurnOutSharp/External/libmspack/CHM/DecompressState.cs +++ b/BurnOutSharp/External/libmspack/CHM/DecompressState.cs @@ -41,11 +41,11 @@ namespace LibMSPackSharp.CHM /// /// Input file handle /// - public object InputFileHandle { get; set; } + public DefaultFileImpl InputFileHandle { get; set; } /// /// Output file handle /// - public object OutputFileHandle { get; set; } + public DefaultFileImpl OutputFileHandle { get; set; } } } diff --git a/BurnOutSharp/External/libmspack/CHM/Implementation.cs b/BurnOutSharp/External/libmspack/CHM/Implementation.cs index 096d468d..c5cbe079 100644 --- a/BurnOutSharp/External/libmspack/CHM/Implementation.cs +++ b/BurnOutSharp/External/libmspack/CHM/Implementation.cs @@ -152,8 +152,8 @@ namespace LibMSPackSharp.CHM SystemImpl sys = self.System; - object fh; - if ((fh = sys.Open(sys, filename, OpenMode.MSPACK_SYS_OPEN_READ)) != null) + DefaultFileImpl fh; + if ((fh = sys.Open(filename, OpenMode.MSPACK_SYS_OPEN_READ)) != null) { chm = new Header(); chm.Filename = filename; @@ -209,12 +209,11 @@ namespace LibMSPackSharp.CHM for (fi = chm.Files; fi != null; fi = nfi) { nfi = fi.Next; - sys.Free(fi); } + for (fi = chm.SysFiles; fi != null; fi = nfi) { nfi = fi.Next; - sys.Free(fi); } // If this CHM was being decompressed, free decompression state @@ -223,25 +222,8 @@ namespace LibMSPackSharp.CHM if (self.State.InputFileHandle != null) sys.Close(self.State.InputFileHandle); - if (self.State.State != null) - LZX.Free(self.State.State); - - sys.Free(self.State); self.State = null; } - - // If this CHM had a chunk cache, free it and contents - if (chm.ChunkCache != null) - { - for (i = 0; i < chm.NumChunks; i++) - { - sys.Free(chm.ChunkCache[i]); - } - - sys.Free(chm.ChunkCache); - } - - sys.Free(chm); } #endregion @@ -267,10 +249,10 @@ namespace LibMSPackSharp.CHM /// non-zero, all file entries will also be read. fills out a pre-existing /// mschmd_header structure, allocates memory for files as necessary /// - public static Error ReadHeaders(SystemImpl sys, object fh, Header chm, bool entire) + public static Error ReadHeaders(SystemImpl sys, DefaultFileImpl fh, Header chm, bool entire) { uint section, nameLen, x, errors, numChunks; - byte[] buf = new byte[0x54], chunk = null; + byte[] buf = new byte[0x54]; int name, p, end; DecompressFile fi, link = null; long offset, length; @@ -431,8 +413,7 @@ namespace LibMSPackSharp.CHM numChunks = chm.LastPMGL - x + 1; - if ((chunk = sys.Alloc(sys, (int)chm.ChunkSize)) == null) - return Error.MSPACK_ERR_NOMEMORY; + byte[] chunk = new byte[chm.ChunkSize]; // Read and process all chunks from FirstPMGL to LastPMGL errors = 0; @@ -440,10 +421,7 @@ namespace LibMSPackSharp.CHM { // Read next chunk if (sys.Read(fh, chunk, 0, (int)chm.ChunkSize) != (int)chm.ChunkSize) - { - sys.Free(chunk); return Error.MSPACK_ERR_READ; - } // Process only directory (PMGL) chunks if (BitConverter.ToUInt32(chunk, pmgl_Signature) != 0x4C474D50) @@ -573,7 +551,6 @@ namespace LibMSPackSharp.CHM } } - sys.Free(chunk); return (errors > 0) ? Error.MSPACK_ERR_DATAFORMAT : Error.MSPACK_ERR_OK; } @@ -592,7 +569,7 @@ namespace LibMSPackSharp.CHM { DecompressorImpl self = d as DecompressorImpl; SystemImpl sys; - object fh; + DefaultFileImpl fh; // p and end are initialised to prevent MSVC warning about "potentially" // uninitialised usage. This is provably untrue, but MS won't fix: @@ -610,7 +587,7 @@ namespace LibMSPackSharp.CHM // Clear the results structure f_ptr = new DecompressFile(); - if ((fh = sys.Open(sys, chm.Filename, OpenMode.MSPACK_SYS_OPEN_READ)) == null) + if ((fh = sys.Open(chm.Filename, OpenMode.MSPACK_SYS_OPEN_READ)) == null) return Error.MSPACK_ERR_OPEN; // Go through PMGI chunk hierarchy to reach PMGL chunk @@ -722,10 +699,9 @@ namespace LibMSPackSharp.CHM /// Reads the given chunk into memory, storing it in a chunk cache /// so it doesn't need to be read from disk more than once /// - public static byte[] ReadChunk(DecompressorImpl self, Header chm, object fh, uint chunkNum) + public static byte[] ReadChunk(DecompressorImpl self, Header chm, DefaultFileImpl fh, uint chunkNum) { SystemImpl sys = self.System; - byte[] buf; // Check arguments - most are already checked by chmd_fast_find if (chunkNum >= chm.NumChunks) @@ -740,24 +716,18 @@ namespace LibMSPackSharp.CHM return chm.ChunkCache[chunkNum]; // Need to read chunk - allocate memory for it - if ((buf = sys.Alloc(sys, (int)chm.ChunkSize)) == null) - { - self.Error = Error.MSPACK_ERR_NOMEMORY; - return null; - } + byte[] buf = new byte[chm.ChunkSize]; // Seek to block and read it if (!sys.Seek(fh, (chm.DirOffset + (chunkNum * chm.ChunkSize)), SeekMode.MSPACK_SYS_SEEK_START)) { self.Error = Error.MSPACK_ERR_SEEK; - sys.Free(buf); return null; } if (sys.Read(fh, buf, 0, (int)chm.ChunkSize) != (int)chm.ChunkSize) { self.Error = Error.MSPACK_ERR_READ; - sys.Free(buf); return null; } @@ -765,7 +735,6 @@ namespace LibMSPackSharp.CHM if (!((buf[0] == 0x50) && (buf[1] == 0x4D) && (buf[2] == 0x47) && ((buf[3] == 0x4C) || (buf[3] == 0x49)))) { self.Error = Error.MSPACK_ERR_SEEK; - sys.Free(buf); return null; } @@ -1025,20 +994,17 @@ namespace LibMSPackSharp.CHM if (self.State.InputFileHandle != null) sys.Close(self.State.InputFileHandle); - if (self.State.State != null) - LZX.Free(self.State.State); - self.State.Header = chm; self.State.Offset = 0; self.State.State = null; - self.State.InputFileHandle = sys.Open(sys, chm.Filename, OpenMode.MSPACK_SYS_OPEN_READ); + self.State.InputFileHandle = sys.Open(chm.Filename, OpenMode.MSPACK_SYS_OPEN_READ); if (self.State.InputFileHandle == null) return self.Error = Error.MSPACK_ERR_OPEN; } // Open file for output - object fh; - if ((fh = sys.Open(sys, filename, OpenMode.MSPACK_SYS_OPEN_WRITE)) == null) + DefaultFileImpl fh; + if ((fh = sys.Open(filename, OpenMode.MSPACK_SYS_OPEN_WRITE)) == null) return self.Error = Error.MSPACK_ERR_OPEN; // If file is empty, simply creating it is enough @@ -1093,10 +1059,7 @@ namespace LibMSPackSharp.CHM if (self.State.State == null || (file.Offset < self.State.Offset)) { if (self.State.State != null) - { - LZX.Free(self.State.State); self.State.State = null; - } if (InitDecompressor(self, file) != Error.MSPACK_ERR_OK) break; @@ -1128,12 +1091,7 @@ namespace LibMSPackSharp.CHM // If an LZX error occured, the LZX decompressor is now useless if (self.Error != Error.MSPACK_ERR_OK) - { - if (self.State.State != null) - LZX.Free(self.State.State); - self.State.State = null; - } break; } @@ -1220,7 +1178,6 @@ namespace LibMSPackSharp.CHM // Check LZXC signature if (BitConverter.ToUInt32(data, lzxcd_Signature) != 0x43585A4C) { - sys.Free(data); return self.Error = Error.MSPACK_ERR_SIGNATURE; } @@ -1237,13 +1194,9 @@ namespace LibMSPackSharp.CHM break; default: Console.WriteLine("bad controldata version"); - sys.Free(data); return self.Error = Error.MSPACK_ERR_DATAFORMAT; } - // Free ControlData - sys.Free(data); - // Find window_bits from window_size switch (window_size) { @@ -1304,7 +1257,7 @@ namespace LibMSPackSharp.CHM length -= self.State.Offset; // Initialise LZX stream - self.State.State = LZX.Init(self.State.Sys, self.State.InputFileHandle, self, window_bits, reset_interval / LZX.LZX_FRAME_SIZE, 4096, length, false); + self.State.State = LZX.Init(self.State.Sys, self.State.InputFileHandle, self.State.OutputFileHandle, window_bits, reset_interval / LZX.LZX_FRAME_SIZE, 4096, length, false); if (self.State.State == null) self.Error = Error.MSPACK_ERR_NOMEMORY; @@ -1359,16 +1312,12 @@ namespace LibMSPackSharp.CHM if (BitConverter.ToUInt32(data, lzxrt_FrameLen) != LZX.LZX_FRAME_SIZE) { Console.WriteLine(("bad reset table frame length")); - sys.Free(data); return false; } // Get the uncompressed length of the LZX stream if ((length_ptr = BitConverter.ToInt64(data, lzxrt_UncompLen)) == 0) - { - sys.Free(data); return false; - } uint entrysize = BitConverter.ToUInt32(data, lzxrt_EntrySize); uint pos = BitConverter.ToUInt32(data, lzxrt_TableOffset) + (entry * entrysize); @@ -1397,9 +1346,6 @@ namespace LibMSPackSharp.CHM err = Error.MSPACK_ERR_ARGS; } - // Free the reset table - sys.Free(data); - // Return success return (err == Error.MSPACK_ERR_OK); } @@ -1442,10 +1388,6 @@ namespace LibMSPackSharp.CHM // Get the uncompressed length of the LZX stream length_ptr = BitConverter.ToInt64(data, 0); - sys.Free(data); - if (err != Error.MSPACK_ERR_OK) - return Error.MSPACK_ERR_DATAFORMAT; - if (length_ptr <= 0) { Console.WriteLine("output length is invalid"); @@ -1514,14 +1456,12 @@ namespace LibMSPackSharp.CHM if (sys.Seek(self.State.InputFileHandle, file.Section.Header.Sec0.Offset + file.Offset, SeekMode.MSPACK_SYS_SEEK_START)) { self.Error = Error.MSPACK_ERR_SEEK; - sys.Free(data); return null; } if (sys.Read(self.State.InputFileHandle, data, 0, len) != len) { self.Error = Error.MSPACK_ERR_READ; - sys.Free(data); return null; } diff --git a/BurnOutSharp/External/libmspack/Compression/LZSS.cs b/BurnOutSharp/External/libmspack/Compression/LZSS.cs index a40a4071..d667a206 100644 --- a/BurnOutSharp/External/libmspack/Compression/LZSS.cs +++ b/BurnOutSharp/External/libmspack/Compression/LZSS.cs @@ -79,59 +79,50 @@ namespace LibMSPackSharp.Compression uint pos = LZSS_WINDOW_SIZE - (uint)((mode == LZSSMode.LZSS_MODE_QBASIC) ? 18 : 16); uint invert = (uint)((mode == LZSSMode.LZSS_MODE_MSHELP) ? ~0 : 0); - int iPtr = 0, iEnd = 0; + int i_ptr = 0, i_end = 0; // Loop forever; exit condition is in ENSURE_BYTES macro for (; ; ) { //ENSURE_BYTES - if (iPtr >= iEnd) + if (i_ptr >= i_end) { read = system.Read(input, window, inbuf, inputBufferSize); if (read <= 0) - { - system.Free(window); return (read < 0) ? Error.MSPACK_ERR_READ : Error.MSPACK_ERR_OK; - } - iPtr = 0; - iEnd = read; + i_ptr = 0; + i_end = read; } - c = window[iPtr++] ^ invert; + c = window[i_ptr++] ^ invert; for (i = 0x01; (i & 0xFF) != 0; i <<= 1) { if (c != 0 & i != 0) { // Literal //ENSURE_BYTES - if (iPtr >= iEnd) + if (i_ptr >= i_end) { read = system.Read(input, window, inbuf, inputBufferSize); if (read <= 0) - { - system.Free(window); return (read < 0) ? Error.MSPACK_ERR_READ : Error.MSPACK_ERR_OK; - } - iPtr = 0; - iEnd = read; + i_ptr = 0; + i_end = read; } - window[pos] = window[iPtr++]; + window[pos] = window[i_ptr++]; //ENSURE_BYTES - if (iPtr >= iEnd) + if (i_ptr >= i_end) { read = system.Read(input, window, inbuf, inputBufferSize); if (read <= 0) - { - system.Free(window); return (read < 0) ? Error.MSPACK_ERR_READ : Error.MSPACK_ERR_OK; - } - iPtr = 0; - iEnd = read; + i_ptr = 0; + i_end = read; } pos++; pos &= LZSS_WINDOW_SIZE - 1; @@ -140,47 +131,38 @@ namespace LibMSPackSharp.Compression { // Match //ENSURE_BYTES - if (iPtr >= iEnd) + if (i_ptr >= i_end) { read = system.Read(input, window, inbuf, inputBufferSize); if (read <= 0) - { - system.Free(window); return (read < 0) ? Error.MSPACK_ERR_READ : Error.MSPACK_ERR_OK; - } - iPtr = 0; - iEnd = read; + i_ptr = 0; + i_end = read; } - mpos = window[iPtr++]; + mpos = window[i_ptr++]; //ENSURE_BYTES - if (iPtr >= iEnd) + if (i_ptr >= i_end) { read = system.Read(input, window, inbuf, inputBufferSize); if (read <= 0) - { - system.Free(window); return (read < 0) ? Error.MSPACK_ERR_READ : Error.MSPACK_ERR_OK; - } - iPtr = 0; - iEnd = read; + i_ptr = 0; + i_end = read; } - mpos |= (uint)(window[iPtr] & 0xF0) << 4; - len = (uint)(window[iPtr++] & 0x0F) + 3; + mpos |= (uint)(window[i_ptr] & 0xF0) << 4; + len = (uint)(window[i_ptr++] & 0x0F) + 3; while (len-- != 0) { window[pos] = window[mpos]; //WRITE_BYTE; if (system.Write(output, window, (int)pos, 1) != 1) - { - system.Free(window); return Error.MSPACK_ERR_WRITE; - } pos++; pos &= LZSS_WINDOW_SIZE - 1; diff --git a/BurnOutSharp/External/libmspack/Compression/LZX.cs b/BurnOutSharp/External/libmspack/Compression/LZX.cs index 5b8fe037..6e305897 100644 --- a/BurnOutSharp/External/libmspack/Compression/LZX.cs +++ b/BurnOutSharp/External/libmspack/Compression/LZX.cs @@ -267,7 +267,7 @@ namespace LibMSPackSharp.Compression /// a pointer to an initialised LZXDStream structure, or null if /// there was not enough memory or parameters to the function were wrong. /// - public static LZXDStream Init(SystemImpl system, object input, object output, int windowBits, int resetInterval, int inputBufferSize, long outputLength, bool isDelta) + public static LZXDStream Init(SystemImpl system, DefaultFileImpl input, DefaultFileImpl output, int windowBits, int resetInterval, int inputBufferSize, long outputLength, bool isDelta) { uint windowSize = (uint)(1 << windowBits); @@ -984,7 +984,7 @@ namespace LibMSPackSharp.Compression if (i > this_run) i = this_run; - lzx.Sys.Copy(lzx.InputBuffer, i_ptr, window, rundest, i); + Array.Copy(lzx.InputBuffer, i_ptr, window, rundest, i); rundest += i; i_ptr += i; this_run -= i; @@ -1044,7 +1044,7 @@ namespace LibMSPackSharp.Compression int abs_off, rel_off; lzx.OutputPointer = data; - lzx.Sys.Copy(lzx.Window, (int)lzx.FramePosition, lzx.e8_buf, data, (int)frame_size); + Array.Copy(lzx.Window, (int)lzx.FramePosition, lzx.e8_buf, data, (int)frame_size); while (data < dataend) { @@ -1112,23 +1112,6 @@ namespace LibMSPackSharp.Compression return Error.MSPACK_ERR_OK; } - /// - /// Frees all state associated with an LZX data stream. This will call - /// system.free() using the system pointer given in lzxd_init(). - /// - /// LZX decompression state to free. - public static void Free(object s) - { - LZXDStream lzx = s as LZXDStream; - if (lzx != null) - { - SystemImpl sys = lzx.Sys; - sys.Free(lzx.InputBuffer); - sys.Free(lzx.Window); - sys.Free(lzx); - } - } - private static Error ReadLens(LZXDStream lzx, byte[] lens, uint first, uint last) { // Bit buffer and huffman symbol decode variables diff --git a/BurnOutSharp/External/libmspack/Compression/MSZIP.cs b/BurnOutSharp/External/libmspack/Compression/MSZIP.cs index ff4f6fc1..d3394628 100644 --- a/BurnOutSharp/External/libmspack/Compression/MSZIP.cs +++ b/BurnOutSharp/External/libmspack/Compression/MSZIP.cs @@ -93,7 +93,7 @@ namespace LibMSPackSharp.Compression /// and 'holes' left will be filled with zero bytes. This allows at least /// a partial recovery of erroneous data. /// - public static MSZIPDStream Init(SystemImpl system, object input, object output, int input_buffer_size, bool repair_mode) + public static MSZIPDStream Init(SystemImpl system, DefaultFileImpl input, DefaultFileImpl output, int input_buffer_size, bool repair_mode) { if (system == null) return null; @@ -329,22 +329,6 @@ namespace LibMSPackSharp.Compression return Error.MSPACK_ERR_OK; } - /// - /// Frees all stream associated with an MS-ZIP data stream - /// - /// - calls system.free() using the system pointer given in mszipd_init() - /// - public static void Free(object s) - { - MSZIPDStream zip = s as MSZIPDStream; - if (zip != null) - { - SystemImpl sys = zip.Sys; - sys.Free(zip.InputBuffer); - sys.Free(zip); - } - } - private static InflateErrorCode ReadLens(MSZIPDStream zip) { // For the bit buffer and huffman decoding @@ -467,14 +451,14 @@ namespace LibMSPackSharp.Compression // Copy LITERAL code lengths and clear any remaining i = lit_codes; - zip.Sys.Copy(lens, 0, zip.LITERAL_len, 0, i); + Array.Copy(lens, 0, zip.LITERAL_len, 0, i); while (i < MSZIP_LITERAL_MAXSYMBOLS) { zip.LITERAL_len[i++] = 0; } i = dist_codes; - zip.Sys.Copy(lens, lit_codes, zip.DISTANCE_len, 0, i); + Array.Copy(lens, lit_codes, zip.DISTANCE_len, 0, i); while (i < MSZIP_DISTANCE_MAXSYMBOLS) { zip.DISTANCE_len[i++] = 0; @@ -561,7 +545,7 @@ namespace LibMSPackSharp.Compression if (this_run > (MSZIP_FRAME_SIZE - zip.WindowPosition)) this_run = (int)(MSZIP_FRAME_SIZE - zip.WindowPosition); - zip.Sys.Copy(zip.InputBuffer, i_ptr, zip.Window, (int)zip.WindowPosition, this_run); + Array.Copy(zip.InputBuffer, i_ptr, zip.Window, (int)zip.WindowPosition, this_run); zip.WindowPosition += (uint)this_run; i_ptr += this_run; length -= this_run; diff --git a/BurnOutSharp/External/libmspack/Compression/None.cs b/BurnOutSharp/External/libmspack/Compression/None.cs new file mode 100644 index 00000000..d298779d --- /dev/null +++ b/BurnOutSharp/External/libmspack/Compression/None.cs @@ -0,0 +1,56 @@ +/* This file is part of libmspack. + * (C) 2003-2018 Stuart Caie. + * + * libmspack is free software; you can redistribute it and/or modify it under + * the terms of the GNU Lesser General Public License (LGPL) version 2.1 + * + * For further details, see the file COPYING.LIB distributed with libmspack + */ + +namespace LibMSPackSharp.Compression +{ + public class None + { + public static NoneState Init(SystemImpl sys, DefaultFileImpl input, DefaultFileImpl output, int bufsize) + { + NoneState state = new NoneState(); + byte[] buf = new byte[bufsize]; + if (state != null && buf != null) + { + state.Sys = sys; + state.Input = input; + state.Output = output; + state.Buffer = buf; + state.BufferSize = bufsize; + } + else + { + state = null; + } + + return state; + } + + public static Error Decompress(object s, long bytes) + { + NoneState state = (NoneState)s; + if (state == null) + return Error.MSPACK_ERR_ARGS; + + int run; + while (bytes > 0) + { + run = (bytes > state.BufferSize) ? state.BufferSize : (int)bytes; + + if (state.Sys.Read(state.Input, state.Buffer, 0, run) != run) + return Error.MSPACK_ERR_READ; + + if (state.Sys.Write(state.Output, state.Buffer, 0, run) != run) + return Error.MSPACK_ERR_WRITE; + + bytes -= run; + } + return Error.MSPACK_ERR_OK; + } + } +} diff --git a/BurnOutSharp/External/libmspack/CAB/NoneState.cs b/BurnOutSharp/External/libmspack/Compression/NoneState.cs similarity index 94% rename from BurnOutSharp/External/libmspack/CAB/NoneState.cs rename to BurnOutSharp/External/libmspack/Compression/NoneState.cs index 64a279a5..17d047c5 100644 --- a/BurnOutSharp/External/libmspack/CAB/NoneState.cs +++ b/BurnOutSharp/External/libmspack/Compression/NoneState.cs @@ -7,7 +7,7 @@ * For further details, see the file COPYING.LIB distributed with libmspack */ -namespace LibMSPackSharp.CAB +namespace LibMSPackSharp.Compression { /// /// The "not compressed" method decompressor diff --git a/BurnOutSharp/External/libmspack/Compression/QTM.cs b/BurnOutSharp/External/libmspack/Compression/QTM.cs index 3a254ab5..82e75ac8 100644 --- a/BurnOutSharp/External/libmspack/Compression/QTM.cs +++ b/BurnOutSharp/External/libmspack/Compression/QTM.cs @@ -91,7 +91,7 @@ namespace LibMSPackSharp.Compression /// - window_bits is the size of the Quantum window, from 1Kb(10) to 2Mb(21). /// - input_buffer_size is the number of bytes to use to store bitstream data. /// - public static QTMDStream Init(SystemImpl system, object input, object output, int window_bits, int input_buffer_size) + public static QTMDStream Init(SystemImpl system, DefaultFileImpl input, DefaultFileImpl output, int window_bits, int input_buffer_size) { uint window_size = (uint)(1 << window_bits); @@ -474,22 +474,6 @@ namespace LibMSPackSharp.Compression return Error.MSPACK_ERR_OK; } - /// - /// Frees all state associated with a Quantum data stream - /// - calls system.free() using the system pointer given in qtmd_init() - /// - public static void Free(object s) - { - QTMDStream qtm = s as QTMDStream; - if (qtm != null) - { - SystemImpl sys = qtm.Sys; - sys.Free(qtm.Window); - sys.Free(qtm.InputBuffer); - sys.Free(qtm); - } - } - /// /// Arithmetic decoder: /// diff --git a/BurnOutSharp/External/libmspack/KWAJ/HeaderImpl.cs b/BurnOutSharp/External/libmspack/KWAJ/HeaderImpl.cs index b64bb420..7589c20e 100644 --- a/BurnOutSharp/External/libmspack/KWAJ/HeaderImpl.cs +++ b/BurnOutSharp/External/libmspack/KWAJ/HeaderImpl.cs @@ -11,6 +11,6 @@ namespace LibMSPackSharp.KWAJ { public class HeaderImpl : Header { - public object FileHandle { get; set; } + public DefaultFileImpl FileHandle { get; set; } } } diff --git a/BurnOutSharp/External/libmspack/KWAJ/Implementation.cs b/BurnOutSharp/External/libmspack/KWAJ/Implementation.cs index 14a5604c..0049e20a 100644 --- a/BurnOutSharp/External/libmspack/KWAJ/Implementation.cs +++ b/BurnOutSharp/External/libmspack/KWAJ/Implementation.cs @@ -76,7 +76,7 @@ namespace LibMSPackSharp.KWAJ SystemImpl sys = self.System; - object fh = sys.Open(sys, filename, OpenMode.MSPACK_SYS_OPEN_READ); + DefaultFileImpl fh = sys.Open(filename, OpenMode.MSPACK_SYS_OPEN_READ); HeaderImpl hdr = new HeaderImpl(); if (fh != null && hdr != null) { @@ -96,7 +96,6 @@ namespace LibMSPackSharp.KWAJ if (fh != null) sys.Close(fh); - sys.Free(hdr); hdr = null; } @@ -121,9 +120,6 @@ namespace LibMSPackSharp.KWAJ // Close the file handle associated self.System.Close(hdr_p.FileHandle); - // Free the memory associated - self.System.Free(hdr); - self.Error = Error.MSPACK_ERR_OK; } @@ -134,7 +130,7 @@ namespace LibMSPackSharp.KWAJ /// /// Reads the headers of a KWAJ format file /// - public static Error ReadHeaders(SystemImpl sys, object fh, Header hdr) + public static Error ReadHeaders(SystemImpl sys, DefaultFileImpl fh, Header hdr) { int i; @@ -287,7 +283,7 @@ namespace LibMSPackSharp.KWAJ return self.Error = Error.MSPACK_ERR_ARGS; SystemImpl sys = self.System; - object fh = (hdr as HeaderImpl)?.FileHandle; + DefaultFileImpl fh = (hdr as HeaderImpl)?.FileHandle; if (fh == null) return Error.MSPACK_ERR_ARGS; @@ -296,8 +292,8 @@ namespace LibMSPackSharp.KWAJ return self.Error = Error.MSPACK_ERR_SEEK; // Open file for output - object outfh; - if ((outfh = sys.Open(sys, filename, OpenMode.MSPACK_SYS_OPEN_WRITE)) == null) + DefaultFileImpl outfh; + if ((outfh = sys.Open(filename, OpenMode.MSPACK_SYS_OPEN_WRITE)) == null) return self.Error = Error.MSPACK_ERR_OPEN; self.Error = Error.MSPACK_ERR_OK; @@ -329,8 +325,6 @@ namespace LibMSPackSharp.KWAJ if (read < 0) self.Error = Error.MSPACK_ERR_READ; - - sys.Free(buf); } else if (hdr.CompressionType == CompressionType.MSKWAJ_COMP_SZDD) { @@ -340,13 +334,11 @@ namespace LibMSPackSharp.KWAJ { InternalStream lzh = LZHInit(sys, fh, outfh); self.Error = (lzh != null) ? LZHDecompress(lzh) : Error.MSPACK_ERR_NOMEMORY; - LZHFree(lzh); } else if (hdr.CompressionType == CompressionType.MSKWAJ_COMP_MSZIP) { MSZIPDStream zip = MSZIP.Init(sys, fh, outfh, KWAJ_INPUT_SIZE, false); self.Error = (zip != null) ? MSZIP.DecompressKWAJ(zip) : Error.MSPACK_ERR_NOMEMORY; - MSZIP.Free(zip); } else { @@ -588,15 +580,6 @@ namespace LibMSPackSharp.KWAJ return Error.MSPACK_ERR_OK; } - private static void LZHFree(InternalStream lzh) - { - if (lzh == null || lzh.Sys == null) - return; - - SystemImpl sys = lzh.Sys; - sys.Free(lzh); - } - public static Error LZHReadLens(InternalStream lzh, uint type, uint numsyms, byte[] lens) { uint bit_buffer = 0, bits_left = 0; diff --git a/BurnOutSharp/External/libmspack/Library.cs b/BurnOutSharp/External/libmspack/Library.cs index f24e5c80..53dc2800 100644 --- a/BurnOutSharp/External/libmspack/Library.cs +++ b/BurnOutSharp/External/libmspack/Library.cs @@ -129,8 +129,6 @@ * use of extract(), and any other methods, with the mutex */ -using LibMSPackSharp.Compression; - namespace LibMSPackSharp { public static class Library @@ -160,16 +158,8 @@ namespace LibMSPackSharp if (!SystemImpl.ValidSystem(sys)) return null; - return new CAB.DecompressorImpl() + return new CAB.Decompressor() { - Open = CAB.Implementation.Open, - Close = CAB.Implementation.Close, - Search = CAB.Implementation.Search, - Extract = CAB.Implementation.Extract, - Prepend = CAB.Implementation.Prepend, - Append = CAB.Implementation.Append, - SetParam = CAB.Implementation.Param, - LastError = CAB.Implementation.LastError, System = sys, State = null, Error = Error.MSPACK_ERR_OK, @@ -184,8 +174,8 @@ namespace LibMSPackSharp /// /// Destroys an existing CAB compressor. /// - /// the to destroy - public static void DestroyCABCompressor(CAB.Compressor c) + /// the to destroy + public static void DestroyCABCompressor(CAB.Compressor self) { // TODO } @@ -193,10 +183,9 @@ namespace LibMSPackSharp /// /// Destroys an existing CAB decompressor. /// - /// the to destroy - public static void DestroyCABDecompressor(CAB.Decompressor d) + /// the to destroy + public static void DestroyCABDecompressor(CAB.Decompressor self) { - CAB.DecompressorImpl self = d as CAB.DecompressorImpl; if (self != null) { SystemImpl sys = self.System; @@ -205,11 +194,8 @@ namespace LibMSPackSharp if (self.State.InputFileHandle != null) sys.Close(self.State.InputFileHandle); - CAB.Implementation.FreeDecompressionState(self); - sys.Free(self.State); + self.FreeDecompressionState(); } - - sys.Free(self); } } @@ -277,13 +263,7 @@ namespace LibMSPackSharp { if (self.State.InputFileHandle != null) sys.Close(self.State.InputFileHandle); - - if (self.State.State != null) - LZX.Free(self.State.State); - - sys.Free(self.State); } - sys.Free(self); } } @@ -427,15 +407,7 @@ namespace LibMSPackSharp /// Destroys an existing SZDD decompressor. /// /// the to destroy - public static void DestroySZDDDecompressor(SZDD.Decompressor d) - { - SZDD.DecompressorImpl self = d as SZDD.DecompressorImpl; - if (self != null) - { - SystemImpl sys = self.System; - sys.Free(self); - } - } + public static void DestroySZDDDecompressor(SZDD.Decompressor d) { } #endregion @@ -489,15 +461,7 @@ namespace LibMSPackSharp /// Destroys an existing KWAJ decompressor. /// /// the to destroy - public static void DestroyKWAJDecompressor(KWAJ.Decompressor d) - { - KWAJ.DecompressorImpl self = d as KWAJ.DecompressorImpl; - if (self != null) - { - SystemImpl sys = self.System; - sys.Free(self); - } - } + public static void DestroyKWAJDecompressor(KWAJ.Decompressor d) { } #endregion @@ -549,15 +513,7 @@ namespace LibMSPackSharp /// Destroys an existing OAB decompressor. /// /// the to destroy - public static void DestroyOABDecompressor(OAB.Decompressor d) - { - OAB.DecompressorImpl self = d as OAB.DecompressorImpl; - if (self != null) - { - SystemImpl sys = self.System; - sys.Free(self); - } - } + public static void DestroyOABDecompressor(OAB.Decompressor d) { } #endregion } diff --git a/BurnOutSharp/External/libmspack/OAB/Implementation.cs b/BurnOutSharp/External/libmspack/OAB/Implementation.cs index ce70c758..5023e235 100644 --- a/BurnOutSharp/External/libmspack/OAB/Implementation.cs +++ b/BurnOutSharp/External/libmspack/OAB/Implementation.cs @@ -118,12 +118,10 @@ namespace LibMSPackSharp.OAB SystemImpl sys = self.System; - object infh = sys.Open(sys, input, OpenMode.MSPACK_SYS_OPEN_READ); + DefaultFileImpl infh = sys.Open(input, OpenMode.MSPACK_SYS_OPEN_READ); if (infh == null) { ret = Error.MSPACK_ERR_OPEN; - if (lzx != null) - LZX.Free(lzx); if (infh != null) sys.Close(infh); @@ -133,8 +131,6 @@ namespace LibMSPackSharp.OAB if (sys.Read(infh, hdrbuf, 0, oabhead_SIZEOF) != oabhead_SIZEOF) { ret = Error.MSPACK_ERR_READ; - if (lzx != null) - LZX.Free(lzx); if (infh != null) sys.Close(infh); @@ -145,8 +141,6 @@ namespace LibMSPackSharp.OAB BitConverter.ToUInt32(hdrbuf, oabhead_VersionLo) != 1) { ret = Error.MSPACK_ERR_SIGNATURE; - if (lzx != null) - LZX.Free(lzx); if (infh != null) sys.Close(infh); @@ -156,12 +150,10 @@ namespace LibMSPackSharp.OAB uint block_max = BitConverter.ToUInt32(hdrbuf, oabhead_BlockMax); uint target_size = BitConverter.ToUInt32(hdrbuf, oabhead_TargetSize); - object outfh = sys.Open(sys, output, OpenMode.MSPACK_SYS_OPEN_WRITE); + DefaultFileImpl outfh = sys.Open(output, OpenMode.MSPACK_SYS_OPEN_WRITE); if (outfh == null) { ret = Error.MSPACK_ERR_OPEN; - if (lzx != null) - LZX.Free(lzx); if (outfh != null) sys.Close(outfh); if (infh != null) @@ -189,14 +181,11 @@ namespace LibMSPackSharp.OAB if (sys.Read(infh, buf, 0, oabblk_SIZEOF) != oabblk_SIZEOF) { ret = Error.MSPACK_ERR_READ; - if (lzx != null) - LZX.Free(lzx); if (outfh != null) sys.Close(outfh); if (infh != null) sys.Close(infh); - sys.Free(buf); return ret; } @@ -208,14 +197,11 @@ namespace LibMSPackSharp.OAB if (blk_dsize > block_max || blk_dsize > target_size || blk_flags > 1) { ret = Error.MSPACK_ERR_DATAFORMAT; - if (lzx != null) - LZX.Free(lzx); if (outfh != null) sys.Close(outfh); if (infh != null) sys.Close(infh); - sys.Free(buf); return ret; } @@ -225,28 +211,22 @@ namespace LibMSPackSharp.OAB if (blk_dsize != blk_csize) { ret = Error.MSPACK_ERR_DATAFORMAT; - if (lzx != null) - LZX.Free(lzx); if (outfh != null) sys.Close(outfh); if (infh != null) sys.Close(infh); - sys.Free(buf); return ret; } ret = CopyFileHandle(sys, infh, outfh, (int)blk_dsize, buf, self.BufferSize); if (ret != Error.MSPACK_ERR_OK) { - if (lzx != null) - LZX.Free(lzx); if (outfh != null) sys.Close(outfh); if (infh != null) sys.Close(infh); - sys.Free(buf); return ret; } } @@ -272,53 +252,42 @@ namespace LibMSPackSharp.OAB if (infh != null) sys.Close(infh); - sys.Free(buf); return ret; } ret = LZX.Decompress(lzx, blk_dsize); if (ret != Error.MSPACK_ERR_OK) { - if (lzx != null) - LZX.Free(lzx); if (outfh != null) sys.Close(outfh); if (infh != null) sys.Close(infh); - sys.Free(buf); return ret; } - LZX.Free(lzx); lzx = null; // Consume any trailing padding bytes before the next block ret = CopyFileHandle(sys, infh, null, in_ofh.Available, buf, self.BufferSize); if (ret != Error.MSPACK_ERR_OK) { - if (lzx != null) - LZX.Free(lzx); if (outfh != null) sys.Close(outfh); if (infh != null) sys.Close(infh); - sys.Free(buf); return ret; } if (out_ofh.CRC != blk_crc) { ret = Error.MSPACK_ERR_CHECKSUM; - if (lzx != null) - LZX.Free(lzx); if (outfh != null) sys.Close(outfh); if (infh != null) sys.Close(infh); - sys.Free(buf); return ret; } } @@ -326,14 +295,11 @@ namespace LibMSPackSharp.OAB target_size -= blk_dsize; } - if (lzx != null) - LZX.Free(lzx); if (outfh != null) sys.Close(outfh); if (infh != null) sys.Close(infh); - sys.Free(buf); return ret; } @@ -355,12 +321,10 @@ namespace LibMSPackSharp.OAB SystemImpl sys = self.System; - object infh = sys.Open(sys, input, OpenMode.MSPACK_SYS_OPEN_READ); + DefaultFileImpl infh = sys.Open(input, OpenMode.MSPACK_SYS_OPEN_READ); if (infh == null) { ret = Error.MSPACK_ERR_OPEN; - if (lzx != null) - LZX.Free(lzx); if (infh != null) sys.Close(infh); @@ -370,8 +334,6 @@ namespace LibMSPackSharp.OAB if (sys.Read(infh, hdrbuf, 0, patchhead_SIZEOF) != patchhead_SIZEOF) { ret = Error.MSPACK_ERR_READ; - if (lzx != null) - LZX.Free(lzx); if (infh != null) sys.Close(infh); @@ -382,8 +344,6 @@ namespace LibMSPackSharp.OAB BitConverter.ToUInt32(hdrbuf, patchhead_VersionLo) != 2) { ret = Error.MSPACK_ERR_SIGNATURE; - if (lzx != null) - LZX.Free(lzx); if (infh != null) sys.Close(infh); @@ -397,12 +357,10 @@ namespace LibMSPackSharp.OAB if (block_max < patchblk_SIZEOF) block_max = patchblk_SIZEOF; - object basefh = sys.Open(sys, basePath, OpenMode.MSPACK_SYS_OPEN_READ); + DefaultFileImpl basefh = sys.Open(basePath, OpenMode.MSPACK_SYS_OPEN_READ); if (basefh == null) { ret = Error.MSPACK_ERR_OPEN; - if (lzx != null) - LZX.Free(lzx); if (basefh != null) sys.Close(basefh); if (infh != null) @@ -411,12 +369,10 @@ namespace LibMSPackSharp.OAB return ret; } - object outfh = sys.Open(sys, output, OpenMode.MSPACK_SYS_OPEN_WRITE); + DefaultFileImpl outfh = sys.Open(output, OpenMode.MSPACK_SYS_OPEN_WRITE); if (outfh == null) { ret = Error.MSPACK_ERR_OPEN; - if (lzx != null) - LZX.Free(lzx); if (outfh != null) sys.Close(outfh); if (basefh != null) @@ -446,8 +402,6 @@ namespace LibMSPackSharp.OAB if (sys.Read(infh, buf, 0, patchblk_SIZEOF) != patchblk_SIZEOF) { ret = Error.MSPACK_ERR_READ; - if (lzx != null) - LZX.Free(lzx); if (outfh != null) sys.Close(outfh); if (basefh != null) @@ -455,7 +409,6 @@ namespace LibMSPackSharp.OAB if (infh != null) sys.Close(infh); - sys.Free(buf); return ret; } @@ -467,8 +420,6 @@ namespace LibMSPackSharp.OAB if (blk_dsize > block_max || blk_dsize > target_size || blk_ssize > block_max) { ret = Error.MSPACK_ERR_DATAFORMAT; - if (lzx != null) - LZX.Free(lzx); if (outfh != null) sys.Close(outfh); if (basefh != null) @@ -476,7 +427,6 @@ namespace LibMSPackSharp.OAB if (infh != null) sys.Close(infh); - sys.Free(buf); return ret; } @@ -501,15 +451,12 @@ namespace LibMSPackSharp.OAB if (infh != null) sys.Close(infh); - sys.Free(buf); return ret; } ret = LZX.SetReferenceData(lzx, sys, basefh, blk_ssize); if (ret != Error.MSPACK_ERR_OK) { - if (lzx != null) - LZX.Free(lzx); if (outfh != null) sys.Close(outfh); if (basefh != null) @@ -517,15 +464,12 @@ namespace LibMSPackSharp.OAB if (infh != null) sys.Close(infh); - sys.Free(buf); return ret; } ret = LZX.Decompress(lzx, blk_dsize); if (ret != Error.MSPACK_ERR_OK) { - if (lzx != null) - LZX.Free(lzx); if (outfh != null) sys.Close(outfh); if (basefh != null) @@ -533,19 +477,15 @@ namespace LibMSPackSharp.OAB if (infh != null) sys.Close(infh); - sys.Free(buf); return ret; } - LZX.Free(lzx); lzx = null; // Consume any trailing padding bytes before the next block ret = CopyFileHandle(sys, infh, null, in_ofh.Available, buf, self.BufferSize); if (ret != Error.MSPACK_ERR_OK) { - if (lzx != null) - LZX.Free(lzx); if (outfh != null) sys.Close(outfh); if (basefh != null) @@ -553,15 +493,12 @@ namespace LibMSPackSharp.OAB if (infh != null) sys.Close(infh); - sys.Free(buf); return ret; } if (out_ofh.CRC != blk_crc) { ret = Error.MSPACK_ERR_CHECKSUM; - if (lzx != null) - LZX.Free(lzx); if (outfh != null) sys.Close(outfh); if (basefh != null) @@ -569,15 +506,12 @@ namespace LibMSPackSharp.OAB if (infh != null) sys.Close(infh); - sys.Free(buf); return ret; } target_size -= blk_dsize; } - if (lzx != null) - LZX.Free(lzx); if (outfh != null) sys.Close(outfh); if (basefh != null) @@ -585,7 +519,6 @@ namespace LibMSPackSharp.OAB if (infh != null) sys.Close(infh); - sys.Free(buf); return ret; } diff --git a/BurnOutSharp/External/libmspack/OAB/InternalFile.cs b/BurnOutSharp/External/libmspack/OAB/InternalFile.cs index 0a99a7f6..1d04e89b 100644 --- a/BurnOutSharp/External/libmspack/OAB/InternalFile.cs +++ b/BurnOutSharp/External/libmspack/OAB/InternalFile.cs @@ -16,7 +16,7 @@ namespace LibMSPackSharp.OAB { - public class InternalFile + public class InternalFile : DefaultFileImpl { public SystemImpl OrigSys { get; set; } diff --git a/BurnOutSharp/External/libmspack/SZDD/HeaderImpl.cs b/BurnOutSharp/External/libmspack/SZDD/HeaderImpl.cs index 185d0699..a0a1e310 100644 --- a/BurnOutSharp/External/libmspack/SZDD/HeaderImpl.cs +++ b/BurnOutSharp/External/libmspack/SZDD/HeaderImpl.cs @@ -11,6 +11,6 @@ namespace LibMSPackSharp.SZDD { public class HeaderImpl : Header { - public object FileHandle { get; set; } + public DefaultFileImpl FileHandle { get; set; } } } diff --git a/BurnOutSharp/External/libmspack/SZDD/Implementation.cs b/BurnOutSharp/External/libmspack/SZDD/Implementation.cs index c069b14e..e019b422 100644 --- a/BurnOutSharp/External/libmspack/SZDD/Implementation.cs +++ b/BurnOutSharp/External/libmspack/SZDD/Implementation.cs @@ -33,7 +33,7 @@ namespace LibMSPackSharp.SZDD SystemImpl sys = self.System; - object fh = sys.Open(sys, filename, OpenMode.MSPACK_SYS_OPEN_READ); + DefaultFileImpl fh = sys.Open(filename, OpenMode.MSPACK_SYS_OPEN_READ); HeaderImpl hdr = new HeaderImpl(); if (fh != null && hdr != null) { @@ -53,7 +53,6 @@ namespace LibMSPackSharp.SZDD if (fh != null) sys.Close(fh); - sys.Free(hdr); hdr = null; } @@ -78,9 +77,6 @@ namespace LibMSPackSharp.SZDD // Close the file handle associated self.System.Close(hdr_p.FileHandle); - // Free the memory associated - self.System.Free(hdr); - self.Error = Error.MSPACK_ERR_OK; } @@ -158,7 +154,7 @@ namespace LibMSPackSharp.SZDD SystemImpl sys = self.System; - object fh = (hdr as HeaderImpl)?.FileHandle; + DefaultFileImpl fh = (hdr as HeaderImpl)?.FileHandle; if (fh == null) return Error.MSPACK_ERR_ARGS; @@ -168,8 +164,8 @@ namespace LibMSPackSharp.SZDD return self.Error = Error.MSPACK_ERR_SEEK; // Open file for output - object outfh; - if ((outfh = sys.Open(sys, filename, OpenMode.MSPACK_SYS_OPEN_WRITE)) == null) + DefaultFileImpl outfh; + if ((outfh = sys.Open(filename, OpenMode.MSPACK_SYS_OPEN_WRITE)) == null) return self.Error = Error.MSPACK_ERR_OPEN; // Decompress the data diff --git a/BurnOutSharp/External/libmspack/SystemImpl.cs b/BurnOutSharp/External/libmspack/SystemImpl.cs index 1831e2de..9938c561 100644 --- a/BurnOutSharp/External/libmspack/SystemImpl.cs +++ b/BurnOutSharp/External/libmspack/SystemImpl.cs @@ -43,13 +43,6 @@ namespace LibMSPackSharp /// /// Opens a file for reading, writing, appending or updating. /// - /// - /// a self-referential pointer to the SystemImpl - /// structure whose Open() method is being called. If - /// this pointer is required by Close(), Read(), Write(), - /// Seek() or Tell(), it should be stored in the result - /// structure at this time. - /// /// /// the file to be opened. It is passed directly from the /// library caller without being modified, so it is up to @@ -57,21 +50,55 @@ namespace LibMSPackSharp /// /// One of the values /// - /// a pointer to a mspack_file structure. This structure officially + /// A pointer to a DefaultFileImpl structure. This structure officially /// contains no members, its true contents are up to the /// SystemImpl implementor. It should contain whatever is needed /// for other SystemImpl methods to operate. Returning the null /// pointer indicates an error condition. /// - public Func Open; + public DefaultFileImpl Open(string filename, OpenMode mode) + { + try + { + DefaultFileImpl fileHandle = new DefaultFileImpl(); + switch (mode) + { + case OpenMode.MSPACK_SYS_OPEN_READ: + fileHandle.FileHandle = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + break; + + case OpenMode.MSPACK_SYS_OPEN_WRITE: + fileHandle.FileHandle = File.Open(filename, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite); + break; + + case OpenMode.MSPACK_SYS_OPEN_UPDATE: + fileHandle.FileHandle = File.Open(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite); + break; + + case OpenMode.MSPACK_SYS_OPEN_APPEND: + fileHandle.FileHandle = File.Open(filename, FileMode.Append, FileAccess.ReadWrite, FileShare.ReadWrite); + break; + + default: + return null; + } + + return fileHandle; + } + catch (Exception ex) + { + Message(null, $"Could not open {filename}: {ex}"); + return null; + } + } /// /// Closes a previously opened file. If any memory was allocated for this /// particular file handle, it should be freed at this time. /// /// the file to close - /// - public Action Close; + /// + public void Close(DefaultFileImpl file) => file?.FileHandle?.Close(); /// /// Reads a given number of bytes from an open file. @@ -86,7 +113,7 @@ namespace LibMSPackSharp /// reads and assumes short reads are due to EOF, so you should /// avoid returning short reads because of transient errors. /// - /// + /// /// public Func Read; @@ -103,7 +130,7 @@ namespace LibMSPackSharp /// bytes were written than requested are considered by the library /// to be an error. /// - /// + /// /// public Func Write; @@ -124,18 +151,40 @@ namespace LibMSPackSharp /// an offset to seek, measured in bytes /// One of the values /// zero for success, non-zero for an error - /// - /// - public Func Seek; + /// + /// + public bool Seek(DefaultFileImpl self, long offset, SeekMode mode) + { + if (self == null) + return false; + + switch (mode) + { + case SeekMode.MSPACK_SYS_SEEK_START: + try { self.FileHandle.Seek(offset, SeekOrigin.Begin); return true; } + catch { return false; } + + case SeekMode.MSPACK_SYS_SEEK_CUR: + try { self.FileHandle.Seek(offset, SeekOrigin.Current); return true; } + catch { return false; } + + case SeekMode.MSPACK_SYS_SEEK_END: + try { self.FileHandle.Seek(offset, SeekOrigin.End); return true; } + catch { return false; } + + default: + return false; + } + } /// /// Returns the current file position (in bytes) of the given file. /// /// the file whose file position is wanted /// the current file position of the file - /// - /// - public Func Tell; + /// + /// + public long Tell(DefaultFileImpl self) => (self != null ? self.FileHandle.Position : 0); /// /// Used to send messages from the library to the user. @@ -150,80 +199,32 @@ namespace LibMSPackSharp /// a specific file. /// /// a printf() style format string. It does NOT include a trailing newline. - /// - public Action Message; + /// + public void Message(DefaultFileImpl file, string format) + { + if (file != null) + Console.Error.Write($"{file.Name}: "); - /// - /// Allocates memory. - /// - /// - /// a self-referential pointer to the SystemImpl - /// structure whose Alloc() method is being called. - /// - /// the number of bytes to allocate - /// - /// a pointer to the requested number of bytes, or null if - /// not enough memory is available - /// - /// - public Func Alloc; - - /// - /// Frees memory. - /// - /// the memory to be freed. null is accepted and ignored. - /// - public Action Free; - - /// - /// Copies from one region of memory to another. - /// - /// The regions of memory are guaranteed not to overlap, are usually less - /// than 256 bytes, and may not be aligned. Please note that the source - /// parameter comes before the destination parameter, unlike the standard - /// C function memcpy(). - /// - /// the region of memory to copy from - /// the region of memory to copy to - /// the size of the memory region, in bytes - public Action Copy; - - /// - /// A null pointer to mark the end of SystemImpl. It must equal null. - /// - /// Should the SystemImpl structure extend in the future, this null - /// will be seen, rather than have an invalid method pointer called. - /// - public readonly object NullPtr = null; + Console.Error.Write($"{format}\n"); + } #region Helpers /// /// Returns the length of a file opened for reading /// - public static Error GetFileLength(SystemImpl system, object file, out long length) + public Error GetFileLength(DefaultFileImpl file, out long length) { - length = 0; - long current; - - if (system == null || file == null) - return Error.MSPACK_ERR_OPEN; - - // Get current offset - current = system.Tell(file); - - // Seek to end of file - if (!system.Seek(file, 0, SeekMode.MSPACK_SYS_SEEK_END)) + try + { + length = file?.FileHandle?.Length ?? 0; + return Error.MSPACK_ERR_OK; + } + catch + { + length = 0; return Error.MSPACK_ERR_SEEK; - - // Get offset of end of file - length = system.Tell(file); - - // Seek back to original offset - if (!system.Seek(file, current, SeekMode.MSPACK_SYS_SEEK_START)) - return Error.MSPACK_ERR_SEEK; - - return Error.MSPACK_ERR_OK; + } } /// @@ -231,10 +232,7 @@ namespace LibMSPackSharp /// public static bool ValidSystem(SystemImpl sys) { - return (sys != null) && (sys.Open != null) && (sys.Close != null) && - (sys.Read != null) && (sys.Write != null) && (sys.Seek != null) && - (sys.Tell != null) && (sys.Message != null) && (sys.Alloc != null) && - (sys.Free != null) && (sys.Copy != null) && (sys.NullPtr == null); + return (sys != null) && (sys.Read != null) && (sys.Write != null); } #endregion @@ -243,53 +241,10 @@ namespace LibMSPackSharp public static SystemImpl DefaultSystem => new SystemImpl() { - Open = DefaultOpen, - Close = DefaultClose, Read = DefaultRead, Write = DefaultWrite, - Seek = DefaultSeek, - Tell = DefaultTell, - Message = DefaultMessage, - Alloc = DefaultAlloc, - Free = DefaultFree, - Copy = DefaultCopy, }; - private static object DefaultOpen(SystemImpl self, string filename, OpenMode mode) - { - DefaultFileImpl fileHandle = new DefaultFileImpl(); - switch (mode) - { - case OpenMode.MSPACK_SYS_OPEN_READ: - fileHandle.FileHandle = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); - break; - - case OpenMode.MSPACK_SYS_OPEN_WRITE: - fileHandle.FileHandle = File.Open(filename, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite); - break; - - case OpenMode.MSPACK_SYS_OPEN_UPDATE: - fileHandle.FileHandle = File.Open(filename, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite); - break; - - case OpenMode.MSPACK_SYS_OPEN_APPEND: - fileHandle.FileHandle = File.Open(filename, FileMode.Append, FileAccess.ReadWrite, FileShare.ReadWrite); - break; - - default: - return null; - } - - return fileHandle; - } - - private static void DefaultClose(object file) - { - DefaultFileImpl self = file as DefaultFileImpl; - if (self != null) - self.FileHandle.Close(); - } - private static int DefaultRead(object file, byte[] buffer, int pointer, int bytes) { DefaultFileImpl self = file as DefaultFileImpl; @@ -314,62 +269,6 @@ namespace LibMSPackSharp return -1; } - private static bool DefaultSeek(object file, long offset, SeekMode mode) - { - DefaultFileImpl self = file as DefaultFileImpl; - if (self != null) - { - switch (mode) - { - case SeekMode.MSPACK_SYS_SEEK_START: - try { self.FileHandle.Seek(offset, SeekOrigin.Begin); return true; } - catch { return false; } - - case SeekMode.MSPACK_SYS_SEEK_CUR: - try { self.FileHandle.Seek(offset, SeekOrigin.Current); return true; } - catch { return false; } - - case SeekMode.MSPACK_SYS_SEEK_END: - try { self.FileHandle.Seek(offset, SeekOrigin.End); return true; } - catch { return false; } - - default: - return false; - } - } - - return false; - } - - private static long DefaultTell(object file) - { - DefaultFileImpl self = file as DefaultFileImpl; - return (self != null ? (int)self.FileHandle.Position : 0); - } - - private static void DefaultMessage(object file, string format) - { - if (file != null) - Console.Error.Write($"{(file as DefaultFileImpl)?.Name}: "); - - Console.Error.Write($"{format}\n"); - } - - private static byte[] DefaultAlloc(SystemImpl self, int bytes) - { - return new byte[bytes]; - } - - private static void DefaultFree(object buffer) - { - buffer = null; - } - - private static void DefaultCopy(byte[] src, int srcPtr, byte[] dest, int destPtr, int bytes) - { - Array.Copy(src, srcPtr, dest, destPtr, bytes); - } - #endregion } } \ No newline at end of file diff --git a/BurnOutSharp/FileType/MicrosoftCAB.cs b/BurnOutSharp/FileType/MicrosoftCAB.cs index df860e45..83c42593 100644 --- a/BurnOutSharp/FileType/MicrosoftCAB.cs +++ b/BurnOutSharp/FileType/MicrosoftCAB.cs @@ -3,7 +3,7 @@ using System.Collections.Concurrent; using System.IO; using BurnOutSharp.Interfaces; using BurnOutSharp.Tools; -using WixToolset.Dtf.Compression.Cab; +using LibMSPackSharp; namespace BurnOutSharp.FileType { @@ -41,20 +41,32 @@ namespace BurnOutSharp.FileType string tempPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); Directory.CreateDirectory(tempPath); - CabInfo cabfile = new CabInfo(file); - foreach (var sub in cabfile.GetFiles()) + var decompressor = Library.CreateCABDecompressor(null); + //decompressor.SetParam(LibMSPackSharp.CAB.Parameters.MSCABD_PARAM_FIXMSZIP, 1); + //decompressor.SetParam(LibMSPackSharp.CAB.Parameters.MSCABD_PARAM_SALVAGE, 1); + + var cabFile = decompressor.Open(file); + + var sub = cabFile.Files; + while (sub != null) { // If an individual entry fails try { // The trim here is for some very odd and stubborn files - string tempFile = Path.Combine(tempPath, sub.Name.TrimEnd('.')); - sub.CopyTo(tempFile); + string tempFile = Path.Combine(tempPath, sub.Filename.TrimEnd('\0', ' ', '.')); + Error error = decompressor.Extract(sub, tempFile); + if (error != Error.MSPACK_ERR_OK) + { + if (scanner.IncludeDebug) Console.WriteLine($"Error occurred during extraction of '{sub.Filename}': {error}"); + } } catch (Exception ex) { if (scanner.IncludeDebug) Console.WriteLine(ex); } + + sub = sub.Next; } // Collect and format all found protections