Class: Cabriolet::Decompressors::LZSS

Inherits:
Base
  • Object
show all
Defined in:
lib/cabriolet/decompressors/lzss.rb

Overview

LZSS decompressor for LZSS-compressed CAB data

LZSS (Lempel-Ziv-Storer-Szymanski) is a derivative of LZ77 compression. It uses a 4096-byte sliding window with a control byte mechanism to indicate whether the next operation is a literal byte copy or a match from the window history.

Constant Summary collapse

WINDOW_SIZE =

LZSS algorithm constants

4096
WINDOW_FILL =
0x20
MODE_EXPAND =

LZSS modes

0
MODE_MSHELP =
1
MODE_QBASIC =
2

Instance Attribute Summary collapse

Attributes inherited from Base

#buffer_size, #input, #io_system, #output

Instance Method Summary collapse

Methods inherited from Base

#free

Constructor Details

#initialize(io_system, input, output, buffer_size, mode = MODE_EXPAND) ⇒ LZSS

Initialize LZSS decompressor

Parameters:



30
31
32
33
34
35
36
37
38
39
# File 'lib/cabriolet/decompressors/lzss.rb', line 30

def initialize(io_system, input, output, buffer_size,
               mode = MODE_EXPAND)
  super(io_system, input, output, buffer_size)
  @mode = mode
  @window = Array.new(WINDOW_SIZE, WINDOW_FILL)
  @window_pos = initialize_window_position
  @input_buffer = ""
  @input_pos = 0
  @invert = mode == MODE_MSHELP ? 0xFF : 0x00
end

Instance Attribute Details

#modeObject (readonly)

Returns the value of attribute mode.



21
22
23
# File 'lib/cabriolet/decompressors/lzss.rb', line 21

def mode
  @mode
end

#windowObject (readonly)

Returns the value of attribute window.



21
22
23
# File 'lib/cabriolet/decompressors/lzss.rb', line 21

def window
  @window
end

#window_posObject (readonly)

Returns the value of attribute window_pos.



21
22
23
# File 'lib/cabriolet/decompressors/lzss.rb', line 21

def window_pos
  @window_pos
end

Instance Method Details

#decompress(_bytes) ⇒ Integer

Decompress LZSS data

Parameters:

  • bytes (Integer)

    Number of bytes to decompress (unused, reads until EOF)

Returns:

  • (Integer)

    Number of bytes decompressed



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/cabriolet/decompressors/lzss.rb', line 46

def decompress(_bytes)
  bytes_written = 0

  loop do
    # Read control byte
    control_byte = read_input_byte
    break if control_byte.nil?

    control_byte ^= @invert

    # Process each bit in the control byte
    8.times do |bit_index|
      mask = 1 << bit_index

      if control_byte.anybits?(mask)
        # Bit is 1: literal byte
        literal = read_input_byte
        break if literal.nil?

        @window[@window_pos] = literal
        write_output_byte(literal)
        bytes_written += 1

        @window_pos = (@window_pos + 1) & (WINDOW_SIZE - 1)
      else
        # Bit is 0: match from window
        offset_low = read_input_byte
        break if offset_low.nil?

        offset_high_and_length = read_input_byte
        break if offset_high_and_length.nil?

        # Decode match position and length
        match_pos = offset_low | ((offset_high_and_length & 0xF0) << 4)
        length = (offset_high_and_length & 0x0F) + 3

        # Copy from window
        length.times do
          byte = @window[match_pos]
          @window[@window_pos] = byte
          write_output_byte(byte)
          bytes_written += 1

          @window_pos = (@window_pos + 1) & (WINDOW_SIZE - 1)
          match_pos = (match_pos + 1) & (WINDOW_SIZE - 1)
        end
      end
    end
  end

  bytes_written
end