lib/libmspack/mscab.rb
module LibMsPack
module MsCab
module Constants
# Offset from start of cabinet to the reserved header data (if present).
MSCAB_HDR_RESV_OFFSET = 0x28
# Cabinet header flag: cabinet has a predecessor.
MSCAB_HDR_PREVCAB = 0x01
# Cabinet header flag: cabinet has a successor.
MSCAB_HDR_NEXTCAB = 0x02
# Cabinet header flag: cabinet has reserved header space.
MSCAB_HDR_RESV = 0x04
# Compression mode: no compression.
MSCAB_COMP_NONE = 0
# Compression mode: MSZIP (deflate) compression.
MSCAB_COMP_MSZIP = 1
# Compression mode: Quantum compression.
MSCAB_COMP_QUANTUM = 2
# Compression mode: LZX compression.
MSCAB_COMP_LZX = 3
# attribute: file is read-only.
MSCAB_ATTRIB_RDONLY = 0x01
# attribute: file is hidden.
MSCAB_ATTRIB_HIDDEN = 0x02
# attribute: file is an operating system file.
MSCAB_ATTRIB_SYSTEM = 0x04
# attribute: file is "archived".
MSCAB_ATTRIB_ARCH = 0x20
# attribute: file is an executable program.
MSCAB_ATTRIB_EXEC = 0x40
# attribute: filename is UTF8, not ISO-8859-1.
MSCAB_ATTRIB_UTF_NAME = 0x80
# CabDecompressor#setParam parameter: search buffer size.
MSCABD_PARAM_SEARCHBUF = 0
# CabDecompressor#setParam parameter: repair MS-ZIP streams?
MSCABD_PARAM_FIXMSZIP = 1
# CabDecompressor#setParam parameter: size of decompression buffer
MSCABD_PARAM_DECOMPBUF = 2
end
# Returns the compression method used by a folder.
# @param [Fixnum] compType a MsCabdFolder.comp_type value
# @return [Fixnum] one of MSCAB_COMP_NONE, MSCAB_COMP_MSZIP, MSCAB_COMP_QUANTUM or MSCAB_COMP_LZX
def self.MsCabdCompMethod(compType)
compType & 0x0F
end
# Returns the compression level used by a folder.
# @param [Fixnum] compType a MsCabdFolder.comp_type value
# @return [Fixnum] the compression level. This is only defined by LZX and Quantum compression
def self.MsCabdCompLevel(compType)
((comp_type) >> 8) & 0x1F
end
# A structure which represents a single folder in a cabinet or cabinet set.
#
# A folder is a single compressed stream of data. When uncompressed, it holds the data of one or more files. A folder may be split across more than one cabinet.
class MsCabdFolder < FFI::Struct
layout({
:next => MsCabdFolder.ptr,
:comp_type => :int,
:num_blocks => :uint
})
# A next folder in this cabinet or cabinet set, or nil if this is the final folder.
#
# @return [MsCabdFolder, nil]
def next
return nil if self[:next].pointer.address.zero?
self[:next]
end
# The compression format used by this folder.
#
# `#MsCabdCompMethod` should be used on this field to get the algorithm used. `#MsCabdCompLevel` should be used to get the "compression level".
#
# @return [Fixnum]
def comp_type
self[:comp_type]
end
# The total number of data blocks used by this folder.
#
# This includes data blocks present in other files, if this folder spans more than one cabinet.
#
# @return [Fixnum]
def num_blocks
self[:num_blocks]
end
# Returns the compression method used by a folder.
#
# @return [Fixnum] one of MSCAB_COMP_NONE, MSCAB_COMP_MSZIP, MSCAB_COMP_QUANTUM or MSCAB_COMP_LZX
def compressionMethod
MsCab::MsCabdCompMethod(comp_type)
end
# Returns the compression level used by a folder.
#
# @return [Fixnum] the compression level. This is only defined by LZX and Quantum compression
def compressionLevel
MsCab::MsCabdCompLevel(comp_type)
end
end
# A structure which represents a single file in a cabinet or cabinet set.
class MsCabdFile < FFI::Struct
layout({
:next => MsCabdFile.ptr,
:filename => :string,
:length => :uint,
:attribs => :int,
:time_h => :char,
:time_m => :char,
:time_s => :char,
:date_d => :char,
:date_m => :char,
:date_y => :int,
:folder => MsCabdFolder.ptr,
:offset => :uint
})
# The next file in the cabinet or cabinet set, or nil if this is the final file.
#
# @return [MsCabdFile, nil]
def next
return nil if self[:next].pointer.address.zero?
self[:next]
end
# The filename of the file.
#
# String of up to 255 bytes in length, it may be in either ISO-8859-1 or UTF8 format, depending on the file attributes.
#
# @return [String]
def filename
self[:filename]
end
# The filename of the file.
#
# @return [String]
def getFilename
str = filename.dup
if (attribs & Constants::MSCAB_ATTRIB_UTF_NAME) == Constants::MSCAB_ATTRIB_UTF_NAME
str.force_encoding(Encoding::UTF_8)
else
str.force_encoding(Encoding::ISO_8859_1)
end
str
end
# The uncompressed length of the file, in bytes.
#
# @return [Fixnum]
def length
self[:length]
end
# File attributes.
#
# The following attributes are defined:
#
# * MSCAB_ATTRIB_RDONLY indicates the file is write protected.
# * MSCAB_ATTRIB_HIDDEN indicates the file is hidden.
# * MSCAB_ATTRIB_SYSTEM indicates the file is a operating system file.
# * MSCAB_ATTRIB_ARCH indicates the file is "archived".
# * MSCAB_ATTRIB_EXEC indicates the file is an executable program.
# * MSCAB_ATTRIB_UTF_NAME indicates the filename is in UTF8 format rather than ISO-8859-1.
#
# @return [Fixnum]
def attribs
self[:attribs]
end
# File's last modified datetime.
#
# @return [Time]
def datetime
Time.gm(self[:date_y], self[:date_m], self[:date_d], self[:time_h], self[:time_m], self[:time_s])
end
# Folder that contains this file.
#
# @return [MsCabdFolder, nil]
def folder
return nil if self[:folder].pointer.address.zero?
self[:folder]
end
# The uncompressed offset of this file in its folder.
#
# @return [Fixnum]
def offset
self[:offset]
end
end
# A structure which represents a single cabinet file.
#
# 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.
class MsCabdCabinet < FFI::Struct
layout({
:next => MsCabdCabinet.ptr,
:filename => :string,
:base_offset => :off_t,
:length => :uint,
:prevcab => MsCabdCabinet.ptr,
:nextcab => MsCabdCabinet.ptr,
:prevname => :string,
:nextname => :string,
:previnfo => :string,
:nextinfo => :string,
:files => MsCabdFile.ptr,
:folders => MsCabdFolder.ptr,
:set_id => :ushort,
:set_index => :ushort,
:header_resv => :ushort,
:flags => :int
})
# The next cabinet in a chained list, if this cabinet was opened with MsCabDecompressor#search
#
# May be nil to mark the end of the list.
#
# @return [MsCabdCabinet, nil]
def next
return nil if self[:next].pointer.address.zero?
self[:next]
end
# The filename of the cabinet.
#
# More correctly, the filename of the physical file that the cabinet resides in. This is given by the library user and may be in any format.
#
# @return [String]
def filename
self[:filename]
end
# The file offset of cabinet within the physical file it resides in.
#
# @return [Fixnum]
def base_offset
self[:base_offset]
end
# The length of the cabinet file in bytes.
#
# @return [Fixnum]
def length
self[:length]
end
# The previous cabinet in a cabinet set, or nil.
#
# @return [MsCabdCabinet, nil]
def prevcab
return nil if self[:prevcab].pointer.address.zero?
self[:prevcab]
end
# The next cabinet in a cabinet set, or nil.
#
# @return [MsCabdCabinet, nil]
def nextcab
return nil if self[:nextcab].pointer.address.zero?
self[:nextcab]
end
# The filename of the previous cabinet in a cabinet set, or nil.
#
# @return [String]
def prevname
self[:prevname]
end
# The filename of the next cabinet in a cabinet set, or nil.
#
# @return [String]
def nextname
self[:nextname]
end
# The name of the disk containing the previous cabinet in a cabinet set, or nil.
#
# @return [String]
def previnfo
self[:previnfo]
end
# The name of the disk containing the next cabinet in a cabinet set, or nil.
#
# @return [String]
def nextinfo
self[:nextinfo]
end
# A list of all files in the cabinet or cabinet set.
#
# @return [MsCabdFile, nil]
def files
return nil if self[:files].pointer.address.zero?
self[:files]
end
# A list of all folders in the cabinet or cabinet set.
#
# @return [MsCabdFolder, nil]
def folders
return nil if self[:folders].pointer.address.zero?
self[:folders]
end
# The set ID of the cabinet.
#
# All cabinets in the same set should have the same set ID.
#
# @return [Fixnum]
def set_id
self[:set_id]
end
# The index number of the cabinet within the set.
#
# Numbering should start from 0 for the first cabinet in the set, and increment by 1 for each following cabinet.
#
# @return [Fixnum]
def set_index
self[:set_index]
end
# The number of bytes reserved in the header area of the cabinet.
#
# If this is non-zero and flags has MSCAB_HDR_RESV set, this data can be read by the calling application. It is of the given length, located at offset (base_offset + MSCAB_HDR_RESV_OFFSET) in the cabinet file.
#
# @see #flags flags
# @return [Fixnum]
def header_resv
self[:header_resv]
end
# Header flags.
#
# * MSCAB_HDR_PREVCAB indicates the cabinet is part of a cabinet set, and has a predecessor cabinet.
# * MSCAB_HDR_NEXTCAB indicates the cabinet is part of a cabinet set, and has a successor cabinet.
# * MSCAB_HDR_RESV indicates the cabinet has reserved header space.
#
# @see #prevname prevname
# @see #previnfo previnfo
# @see #nextname nextname
# @see #nextinfo nextinfo
# @see #header_resv header_resv
# @return [Fixnum]
def flags
self[:flags]
end
end
# CAB compressor
class MsCabCompressor < FFI::Struct
layout({:dummy => :int})
end
# CAB decompressor
class MsCabDecompressor < FFI::Struct
layout({
:open => callback([ MsCabDecompressor.ptr, :string ], MsCabdCabinet.ptr),
:close => callback([ MsCabDecompressor.ptr, MsCabdCabinet.ptr ], :void),
:search => callback([ MsCabDecompressor.ptr, :string ], MsCabdCabinet.ptr),
:append => callback([ MsCabDecompressor.ptr, MsCabdCabinet.ptr, MsCabdCabinet.ptr ], :int),
:prepend => callback([ MsCabDecompressor.ptr, MsCabdCabinet.ptr, MsCabdCabinet.ptr ], :int),
:extract => callback([ MsCabDecompressor.ptr, MsCabdFile.ptr, :string ], :int),
:set_param => callback([ MsCabDecompressor.ptr, :int, :int ], :int),
:last_error => callback([ MsCabDecompressor.ptr ], :int)
})
# Opens a cabinet file and reads its contents.
#
# If the file opened is a valid cabinet file, all headers will be read and a MsCabdCabinet structure will be returned, with a full list of folders and files.
#
# In the case of an error occuring, nil is returned and the error code is available from #last_error
#
# @param [MsCabDecompressor] decompressor MsCabDecompressor instance being called
# @param [String] filename name of the cabinet file
# @return [MsCabdCabinet, nil]
# @see #close close
# @see #search search
# @see #last_error last_error
def open(decompressor, filename)
self[:open].call(decompressor, filename)
end
# Closes a previously opened cabinet or cabinet set.
#
# This closes a cabinet, all cabinets associated with it via the MsCabdCabinet#next, MsCabdCabinet#prevcab and MsCabdCabinet#nextcab, and all folders and files. All memory used by these entities is freed.
#
# The cabinet is now invalid and cannot be used again. All MsCabdFolder and MsCabdFile from that cabinet or cabinet set are also now invalid, and cannot be used again.
#
# If the cabinet given was created using #search, it MUST be the cabinet returned by #search and not one of the later cabinet pointers further along the MsCabdCabinet::next chain.
#
# If extra cabinets have been added using #append or #prepend, these will all be freed, even if the cabinet given is not the first cabinet in the set. Do NOT #close more than one cabinet in the set.
#
# @param [MsCabDecompressor] decompressor MsCabDecompressor instance being called
# @param [MsCabdCabinet] cabinet the cabinet to close
# @see #open open
# @see #search search
# @see #append append
# @see #prepend prepend
def close(decompressor, cabinet)
self[:close].call(decompressor, cabinet)
end
# Searches a regular file for embedded cabinets.
#
# This opens a normal file with the given filename and will search the entire file for embedded cabinet files
#
# If any cabinets are found, the equivalent of #open is called on each potential cabinet file at the offset it was found. All successfully #open'ed cabinets are kept in a list.
#
# The first cabinet found will be returned directly as the result of this method. Any further cabinets found will be chained in a list using the MsCabdCabinet#next field.
#
# In the case of an error occuring anywhere other than the simulated #open, nil is returned and the error code is available from #last_error
#
# If no error occurs, but no cabinets can be found in the file, nil is returned and #last_error returns MSPACK_ERR_OK
#
# `#close` should only be called on the result of #search, not on any subsequent cabinets in the MsCabdCabinet#next chain.
#
# @param [MsCabDecompressor] decompressor MsCabDecompressor instance being called
# @param [String] filename the filename of the file to search for cabinets
# @return [MsCabdCabinet, nil]
# @see #close close
# @see #open open
# @see #last_error last_error
def search(decompressor, filename)
self[:search].call(decompressor, filename)
end
# Appends one MsCabdCabinet to another, forming or extending a cabinet set.
#
# This will attempt to append one cabinet to another such that (cab.nextcab == nextcab) && (nextcab.prevcab == cab) and any folders split between the two cabinets are merged.
#
# The cabinets MUST be part of a cabinet set -- a cabinet set is a cabinet that spans more than one physical cabinet file on disk -- and must be appropriately matched.
#
# It can be determined if a cabinet has further parts to load by examining the MsCabdCabinet.flags field:
#
# * if (flags & MSCAB_HDR_PREVCAB) is non-zero, there is a predecessor cabinet to #open and #prepend. Its MS-DOS case-insensitive filename is MsCabdCabinet.prevname
# * if (flags & MSCAB_HDR_NEXTCAB) is non-zero, there is a successor cabinet to #open and #append. Its MS-DOS case-insensitive filename is MsCabdCabinet.nextname
#
# If the cabinets do not match, an error code will be returned. Neither cabinet has been altered, and both should be closed seperately.
#
# Files and folders in a cabinet set are a single entity. All cabinets in a set use the same file list, which is updated as cabinets in the set are added.
#
# @param [MsCabDecompressor] decompressor MsCabDecompressor instance being called
# @param [MsCabdCabinet] cab the cabinet which will be appended to, predecessor of nextcab
# @param [MsCabdCabinet] nextcab the cabinet which will be appended, successor of cab
# @return [Fixnum] an error code, or MSPACK_ERR_OK if successful
# @see #prepend prepend
# @see #open open
# @see #close close
def append(decompressor, cab, nextcab)
self[:append].call(decompressor, cab, nextcab)
end
# Prepends one MsCabdCabinet to another, forming or extending a cabinet set.
#
# This will attempt to prepend one cabinet to another, such that (cab.prevcab == prevcab) && (prevcab.nextcab == cab).
# In all other respects, it is identical to #append.
#
# @see #append #append for the full documentation
# @param [MsCabDecompressor] decompressor MsCabDecompressor instance being called
# @param [MsCabdCabinet] cab the cabinet which will be prepended to, successor of prevcab
# @param [MsCabdCabinet] prevcab the cabinet which will be prepended, predecessor of cab
# @see #append append
# @see #open open
# @see #close close
def prepend(decompressor, cab, prevcab)
self[:prepend].call(decompressor, cab, prevcab)
end
# Extracts a file from a cabinet or cabinet set.
#
# This extracts a compressed file in a cabinet and writes it to the given filename.
#
# The MS-DOS filename of the file, MsCabdCabinet.filename, is NOT USED by #extract. The caller must examine this MS-DOS filename, copy and change it as necessary, create directories as necessary, and provide the correct filename as a parameter, which will be passed unchanged to the decompressor's MsPackSystem#open
#
# If the file belongs to a split folder in a multi-part cabinet set, and not enough parts of the cabinet set have been loaded and appended or prepended, an error will be returned immediately.
#
# @param [MsCabDecompressor] decompressor MsCabDecompressor instance being called
# @param [MsCabdFile] cabfile the file to be decompressed
# @param [String] filename the filename of the file being written to
# @return [Fixnum] an error code, or MSPACK_ERR_OK if successful
def extract(decompressor, cabfile, filename)
self[:extract].call(decompressor, cabfile, filename)
end
# Sets a CAB decompression engine parameter.
#
# The following parameters are defined:
#
# * MSCABD_PARAM_SEARCHBUF: How many bytes should be allocated as a buffer when using #search ? The minimum value is 4. The default value is 32768.
# * MSCABD_PARAM_FIXMSZIP: If non-zero, #extract will ignore bad checksums and recover from decompression errors in MS-ZIP compressed folders. The default value is 0 (don't recover).
# * MSCABD_PARAM_DECOMPBUF: How many bytes should be used as an input bit buffer by decompressors? The minimum value is 4. The default value is 4096.
#
# @param [MsCabDecompressor] decompressor MsCabDecompressor instance being called
# @param [Fixnum] param the parameter to set
# @param [Fixnum] value the value to set the parameter to
# @see #close close
# @see #open open
def set_param(decompressor, param, value)
self[:set_param].call(decompressor, param, value)
end
# Returns the error code set by the most recently called method
# @param [MsCabDecompressor] decompressor MsCabDecompressor instance being called
# @return [Fixnum] the most recent error code
# @see #open open
# @see #search search
def last_error(decompressor)
self[:last_error].call(decompressor)
end
end
# CAB Compressor
class CabCompressor
attr_reader :Compressor
# Creates a new CAB compressor.
#
# @param [MsPack::MsPackSystem, nil] system is a custom mspack system, or nil to use the default
def initialize(system = nil)
@Compressor = nil
init(system)
end
# @private
def init(system = MsPack::RubyPackSystem)
raise Exceptions::AlreadyInitializedError if @Compressor
@Compressor = LibMsPack.CreateCabCompressor(system)
end
# Destroys an existing CAB compressor
def destroy
raise Exceptions::NotInitializedError unless @Compressor
LibMsPack.DestroyCabCompressor(@Compressor)
@Compressor = nil
end
end
# CAB decompressor
class CabDecompressor
attr_reader :Decompressor
# Creates a new CAB decompressor.
#
# @param [MsPack::MsPackSystem, nil] system a custom mspack system, or nil to use the default
def initialize(system = nil)
@Decompressor = nil
init(system)
end
# @private
def init(system = MsPack::RubyPackSystem)
raise Exceptions::AlreadyInitializedError if @Decompressor
@Decompressor = LibMsPack.CreateCabDecompressor(system)
end
# Opens a cabinet file and reads its contents.
#
# If the file opened is a valid cabinet file, all headers will be read and a MsCabdCabinet structure will be returned, with a full list of folders and files.
#
# In the case of an error occuring, exception will be raised.
#
# @param [String] filename name of the cabinet file
# @return [MsCabdCabinet]
# @raise [Exceptions::LibMsPackError]
# @see #close close
# @see #search search
def open(filename)
raise Exceptions::NotInitializedError unless @Decompressor
cabinet = @Decompressor.open(@Decompressor, filename)
error = lastError
raise Exceptions::LibMsPackError.new(error) unless error == LibMsPack::MSPACK_ERR_OK
cabinet
end
# Closes a previously opened cabinet or cabinet set.
#
# This closes a cabinet, all cabinets associated with it via the MsCabdCabinet#next, MsCabdCabinet#prevcab and MsCabdCabinet#nextcab, and all folders and files. All memory used by these entities is freed.
#
# The cabinet is now invalid and cannot be used again. All MsCabdFolder and MsCabdFile from that cabinet or cabinet set are also now invalid, and cannot be used again.
#
# If the cabinet given was created using #search, it MUST be the cabinet returned by #search and not one of the later cabinet pointers further along the MsCabdCabinet::next chain.
#
# If extra cabinets have been added using #append or #prepend, these will all be freed, even if the cabinet given is not the first cabinet in the set. Do NOT #close more than one cabinet in the set.
#
# @param [MsCabdCabinet] cabinet the cabinet to close
# @see #open open
# @see #search search
# @see #append append
# @see #prepend prepend
def close(cabinet)
raise Exceptions::NotInitializedError unless @Decompressor
@Decompressor.close(@Decompressor, cabinet)
end
# Searches a regular file for embedded cabinets.
#
# This opens a normal file with the given filename and will search the entire file for embedded cabinet files
#
# If any cabinets are found, the equivalent of #open is called on each potential cabinet file at the offset it was found. All successfully #open'ed cabinets are kept in a list.
#
# The first cabinet found will be returned directly as the result of this method. Any further cabinets found will be chained in a list using the MsCabdCabinet#next field.
#
# In the case of an error occuring anywhere other than the simulated #open, exception will be raised.
#
# If no error occurs, but no cabinets can be found in the file, nil is returned.
#
# `#close` should only be called on the result of #search, not on any subsequent cabinets in the MsCabdCabinet#next chain.
#
# @param [String] filename the filename of the file to search for cabinets
# @return [MsCabdCabinet]
# @raise [Exceptions::LibMsPackError]
# @see #close close
# @see #open open
def search(filename)
raise Exceptions::NotInitializedError unless @Decompressor
cabinet = @Decompressor.search(@Decompressor, filename)
error = lastError
raise Exceptions::LibMsPackError.new(error) unless error == LibMsPack::MSPACK_ERR_OK
cabinet
end
# Appends one MsCabdCabinet to another, forming or extending a cabinet set.
#
# This will attempt to append one cabinet to another such that (cab.nextcab == nextcab) && (nextcab.prevcab == cab) and any folders split between the two cabinets are merged.
#
# The cabinets MUST be part of a cabinet set -- a cabinet set is a cabinet that spans more than one physical cabinet file on disk -- and must be appropriately matched.
#
# It can be determined if a cabinet has further parts to load by examining the MsCabdCabinet.flags field:
#
# * if (flags & MSCAB_HDR_PREVCAB) is non-zero, there is a predecessor cabinet to #open and #prepend. Its MS-DOS case-insensitive filename is MsCabdCabinet.prevname
# * if (flags & MSCAB_HDR_NEXTCAB) is non-zero, there is a successor cabinet to #open and #append. Its MS-DOS case-insensitive filename is MsCabdCabinet.nextname
#
# If the cabinets do not match, an exception will be raised. Neither cabinet has been altered, and both should be closed seperately.
#
# Files and folders in a cabinet set are a single entity. All cabinets in a set use the same file list, which is updated as cabinets in the set are added.
#
# @param [MsCabdCabinet] cab the cabinet which will be appended to, predecessor of nextcab
# @param [MsCabdCabinet] nextcab the cabinet which will be appended, successor of cab
# @raise [Exceptions::LibMsPackError]
# @see #prepend prepend
# @see #open open
# @see #close close
def append(cab, nextcab)
raise Exceptions::NotInitializedError unless @Decompressor
error = @Decompressor.append(@Decompressor, cab, nextcab)
raise Exceptions::LibMsPackError.new(error) unless error == LibMsPack::MSPACK_ERR_OK
error
end
# Prepends one MsCabdCabinet to another, forming or extending a cabinet set.
#
# This will attempt to prepend one cabinet to another, such that (cab.prevcab == prevcab) && (prevcab.nextcab == cab).
# In all other respects, it is identical to #append.
#
# @see #append #append for the full documentation
# @param [MsCabdCabinet] cab the cabinet which will be prepended to, successor of prevcab
# @param [MsCabdCabinet] prevcab the cabinet which will be prepended, predecessor of cab
# @raise [Exceptions::LibMsPackError]
# @see #append append
# @see #open open
# @see #close close
def prepend(cab, prevcab)
raise Exceptions::NotInitializedError unless @Decompressor
error = @Decompressor.prepend(@Decompressor, cab, prevcab)
raise Exceptions::LibMsPackError.new(error) unless error == LibMsPack::MSPACK_ERR_OK
error
end
# Extracts a file from a cabinet or cabinet set.
#
# This extracts a compressed file in a cabinet and writes it to the given filename.
#
# The MS-DOS filename of the file, MsCabdCabinet.filename, is NOT USED by #extract. The caller must examine this MS-DOS filename, copy and change it as necessary, create directories as necessary, and provide the correct filename as a parameter, which will be passed unchanged to the decompressor's MsPackSystem#open
#
# If the file belongs to a split folder in a multi-part cabinet set, and not enough parts of the cabinet set have been loaded and appended or prepended, an exception will be raised immediately.
#
# @param [MsCabdFile] cabfile the file to be decompressed
# @param [String] filename the filename of the file being written to
# @raise [Exceptions::LibMsPackError]
def extract(cabfile, filename)
raise Exceptions::NotInitializedError unless @Decompressor
error = @Decompressor.extract(@Decompressor, cabfile, filename)
raise Exceptions::LibMsPackError.new(error) unless error == LibMsPack::MSPACK_ERR_OK
error
end
# Sets a CAB decompression engine parameter.
#
# The following parameters are defined:
#
# * MSCABD_PARAM_SEARCHBUF: How many bytes should be allocated as a buffer when using #search ? The minimum value is 4. The default value is 32768.
# * MSCABD_PARAM_FIXMSZIP: If non-zero, #extract will ignore bad checksums and recover from decompression errors in MS-ZIP compressed folders. The default value is 0 (don't recover).
# * MSCABD_PARAM_DECOMPBUF: How many bytes should be used as an input bit buffer by decompressors? The minimum value is 4. The default value is 4096.
#
# @param [Fixnum] param the parameter to set
# @param [Fixnum] value the value to set the parameter to
# @raise [Exceptions::LibMsPackError]
# @see #close close
# @see #open open
def setParam(param, value)
raise Exceptions::NotInitializedError unless @Decompressor
error = @Decompressor.set_param(@Decompressor, param, value)
raise Exceptions::LibMsPackError.new(error) unless error == LibMsPack::MSPACK_ERR_OK
error
end
# Returns the error code set by the most recently called method
# @return [Fixnum] the most recent error code
def lastError
raise Exceptions::NotInitializedError unless @Decompressor
@Decompressor.last_error(@Decompressor)
end
# Destroys an existing CAB decompressor
def destroy
raise Exceptions::NotInitializedError unless @Decompressor
LibMsPack.DestroyCabDecompressor(@Decompressor)
@Decompressor = nil
end
end
end
end