cabriolet 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/cabriolet/cab/extractor.rb +168 -86
- data/lib/cabriolet/decompressors/lzx.rb +59 -1
- data/lib/cabriolet/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9d2875b9afed58332c6e9823f3f383a60d802bac8514cb2015fdbd6a7f1559dc
|
|
4
|
+
data.tar.gz: 6979ce57ad3d47867bed19330d70b5db483bc33ac8920ee2986faa35828d6cbc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: edd7b1345bee36e75fb5796a3840f087a9dfa51e41c599fa767938e8d64ab0abc985e563b9fb246fc86fc4798eb3f98539f774023eaf613f50a1c9e4a2a6d518
|
|
7
|
+
data.tar.gz: c5cfc76ae8dc5239efa9e90d0956d2518eab56c669d4b7e4087debc4955f509cc97440296c53084152cb30059b25044b6231cd9a84ee94e85f446a20f3306e08
|
|
@@ -34,25 +34,7 @@ module Cabriolet
|
|
|
34
34
|
def extract_file(file, output_path, **options)
|
|
35
35
|
salvage = options[:salvage] || @decompressor.salvage
|
|
36
36
|
folder = file.folder
|
|
37
|
-
|
|
38
|
-
# Validate file
|
|
39
|
-
raise Cabriolet::ArgumentError, "File has no folder" unless folder
|
|
40
|
-
|
|
41
|
-
if file.offset > Constants::LENGTH_MAX
|
|
42
|
-
raise DecompressionError,
|
|
43
|
-
"File offset beyond 2GB limit"
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
# Check file length
|
|
47
|
-
filelen = file.length
|
|
48
|
-
if filelen > (Constants::LENGTH_MAX - file.offset)
|
|
49
|
-
unless salvage
|
|
50
|
-
raise DecompressionError,
|
|
51
|
-
"File length exceeds 2GB limit"
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
filelen = Constants::LENGTH_MAX - file.offset
|
|
55
|
-
end
|
|
37
|
+
filelen = validate_file_for_extraction(file, folder, salvage)
|
|
56
38
|
|
|
57
39
|
# Check for merge requirements
|
|
58
40
|
if folder.needs_prev_merge?
|
|
@@ -60,81 +42,22 @@ module Cabriolet
|
|
|
60
42
|
"File requires previous cabinet, cabinet set is incomplete"
|
|
61
43
|
end
|
|
62
44
|
|
|
63
|
-
|
|
64
|
-
unless salvage
|
|
65
|
-
max_len = folder.num_blocks * Constants::BLOCK_MAX
|
|
66
|
-
if file.offset > max_len || filelen > (max_len - file.offset)
|
|
67
|
-
raise DecompressionError, "File extends beyond folder data"
|
|
68
|
-
end
|
|
69
|
-
end
|
|
45
|
+
validate_file_in_folder(folder, file.offset, filelen, salvage)
|
|
70
46
|
|
|
71
47
|
# Create output directory if needed
|
|
72
48
|
output_dir = ::File.dirname(output_path)
|
|
73
49
|
FileUtils.mkdir_p(output_dir) unless ::File.directory?(output_dir)
|
|
74
50
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
warn "DEBUG extract_file: Checking reset condition for file #{file.filename} (offset=#{file.offset}, length=#{file.length})"
|
|
78
|
-
warn " @current_folder == folder: #{@current_folder == folder} (current=#{@current_folder.object_id}, new=#{folder.object_id})"
|
|
79
|
-
warn " @current_offset (#{@current_offset}) > file.offset (#{file.offset}): #{@current_offset > file.offset}"
|
|
80
|
-
warn " @current_decomp.nil?: #{@current_decomp.nil?}"
|
|
81
|
-
warn " Reset needed?: #{@current_folder != folder || @current_offset > file.offset || !@current_decomp}"
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
if @current_folder != folder || @current_offset > file.offset || !@current_decomp
|
|
85
|
-
if ENV["DEBUG_BLOCK"]
|
|
86
|
-
warn "DEBUG extract_file: RESETTING state (creating new BlockReader)"
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
# Reset state
|
|
90
|
-
@current_input&.close
|
|
91
|
-
@current_input = nil
|
|
92
|
-
@current_decomp = nil
|
|
93
|
-
|
|
94
|
-
# Create new input (libmspack lines 1092-1095)
|
|
95
|
-
# This BlockReader will be REUSED across all files in this folder
|
|
96
|
-
@current_input = BlockReader.new(@io_system, folder.data,
|
|
97
|
-
folder.num_blocks, salvage)
|
|
98
|
-
@current_folder = folder
|
|
99
|
-
@current_offset = 0
|
|
100
|
-
|
|
101
|
-
# Create decompressor ONCE and reuse it (this is the key fix!)
|
|
102
|
-
# The decompressor maintains bitstream state across files
|
|
103
|
-
@current_decomp = @decompressor.create_decompressor(folder,
|
|
104
|
-
@current_input, nil)
|
|
105
|
-
elsif ENV["DEBUG_BLOCK"]
|
|
106
|
-
warn "DEBUG extract_file: NOT resetting (reusing existing BlockReader and decompressor)"
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
# Skip ahead if needed (libmspack lines 1130-1134)
|
|
110
|
-
if file.offset > @current_offset
|
|
111
|
-
skip_bytes = file.offset - @current_offset
|
|
112
|
-
|
|
113
|
-
# Decompress with NULL output to skip (libmspack line 1130: self->d->outfh = NULL)
|
|
114
|
-
null_output = System::MemoryHandle.new("", Constants::MODE_WRITE)
|
|
115
|
-
|
|
116
|
-
# Reuse existing decompressor, change output to NULL
|
|
117
|
-
@current_decomp.instance_variable_set(:@output, null_output)
|
|
118
|
-
|
|
119
|
-
# Set output length for LZX frame limiting
|
|
120
|
-
@current_decomp.set_output_length(skip_bytes) if @current_decomp.respond_to?(:set_output_length)
|
|
121
|
-
|
|
122
|
-
@current_decomp.decompress(skip_bytes)
|
|
123
|
-
@current_offset += skip_bytes
|
|
124
|
-
end
|
|
51
|
+
setup_decompressor_for_folder(folder, salvage, file.offset)
|
|
52
|
+
skip_to_file_offset(file.offset, salvage, file.filename)
|
|
125
53
|
|
|
126
54
|
# Extract actual file (libmspack lines 1137-1141)
|
|
127
55
|
output_fh = @io_system.open(output_path, Constants::MODE_WRITE)
|
|
128
56
|
|
|
129
57
|
begin
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
# Set output length for LZX frame limiting
|
|
134
|
-
@current_decomp.set_output_length(filelen) if @current_decomp.respond_to?(:set_output_length)
|
|
135
|
-
|
|
136
|
-
@current_decomp.decompress(filelen)
|
|
137
|
-
@current_offset += filelen
|
|
58
|
+
write_file_data(output_fh, filelen)
|
|
59
|
+
rescue DecompressionError
|
|
60
|
+
handle_extraction_error(output_fh, output_path, file.filename, salvage, filelen)
|
|
138
61
|
ensure
|
|
139
62
|
output_fh.close
|
|
140
63
|
end
|
|
@@ -142,6 +65,15 @@ module Cabriolet
|
|
|
142
65
|
filelen
|
|
143
66
|
end
|
|
144
67
|
|
|
68
|
+
# Reset extraction state (used in salvage mode to recover from errors)
|
|
69
|
+
def reset_state
|
|
70
|
+
@current_input&.close
|
|
71
|
+
@current_input = nil
|
|
72
|
+
@current_decomp = nil
|
|
73
|
+
@current_folder = nil
|
|
74
|
+
@current_offset = 0
|
|
75
|
+
end
|
|
76
|
+
|
|
145
77
|
# Extract all files from a cabinet
|
|
146
78
|
#
|
|
147
79
|
# @param cabinet [Models::Cabinet] Cabinet to extract from
|
|
@@ -150,16 +82,19 @@ module Cabriolet
|
|
|
150
82
|
# @option options [Boolean] :preserve_paths Preserve directory structure (default: true)
|
|
151
83
|
# @option options [Boolean] :set_timestamps Set file modification times (default: true)
|
|
152
84
|
# @option options [Proc] :progress Progress callback
|
|
85
|
+
# @option options [Boolean] :salvage Skip files that fail to extract (default: false)
|
|
153
86
|
# @return [Integer] Number of files extracted
|
|
154
87
|
def extract_all(cabinet, output_dir, **options)
|
|
155
88
|
preserve_paths = options.fetch(:preserve_paths, true)
|
|
156
89
|
set_timestamps = options.fetch(:set_timestamps, true)
|
|
157
90
|
progress = options[:progress]
|
|
91
|
+
salvage = options[:salvage] || false
|
|
158
92
|
|
|
159
93
|
# Create output directory
|
|
160
94
|
FileUtils.mkdir_p(output_dir) unless ::File.directory?(output_dir)
|
|
161
95
|
|
|
162
96
|
count = 0
|
|
97
|
+
failed_count = 0
|
|
163
98
|
cabinet.files.each do |file|
|
|
164
99
|
# Determine output path
|
|
165
100
|
output_path = if preserve_paths
|
|
@@ -169,8 +104,18 @@ module Cabriolet
|
|
|
169
104
|
::File.basename(file.filename))
|
|
170
105
|
end
|
|
171
106
|
|
|
172
|
-
# Extract file
|
|
173
|
-
|
|
107
|
+
# Extract file (skip if salvage mode and extraction fails)
|
|
108
|
+
begin
|
|
109
|
+
extract_file(file, output_path, **options)
|
|
110
|
+
rescue DecompressionError => e
|
|
111
|
+
if salvage
|
|
112
|
+
warn "Salvage: skipping #{file.filename}: #{e.message}"
|
|
113
|
+
failed_count += 1
|
|
114
|
+
next
|
|
115
|
+
else
|
|
116
|
+
raise
|
|
117
|
+
end
|
|
118
|
+
end
|
|
174
119
|
|
|
175
120
|
# Set timestamp if requested
|
|
176
121
|
if set_timestamps && file.modification_time
|
|
@@ -185,11 +130,148 @@ module Cabriolet
|
|
|
185
130
|
progress&.call(file, count, cabinet.files.size)
|
|
186
131
|
end
|
|
187
132
|
|
|
133
|
+
warn "Salvage: #{failed_count} file(s) skipped due to extraction errors" if failed_count.positive?
|
|
134
|
+
|
|
188
135
|
count
|
|
189
136
|
end
|
|
190
137
|
|
|
191
138
|
private
|
|
192
139
|
|
|
140
|
+
# Validate file for extraction
|
|
141
|
+
#
|
|
142
|
+
# @param file [Models::File] File to validate
|
|
143
|
+
# @param folder [Models::Folder] Folder containing the file
|
|
144
|
+
# @param salvage [Boolean] Salvage mode flag
|
|
145
|
+
# @return [Integer] Validated file length
|
|
146
|
+
def validate_file_for_extraction(file, folder, salvage)
|
|
147
|
+
raise Cabriolet::ArgumentError, "File has no folder" unless folder
|
|
148
|
+
|
|
149
|
+
if file.offset > Constants::LENGTH_MAX
|
|
150
|
+
raise DecompressionError,
|
|
151
|
+
"File offset beyond 2GB limit"
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
filelen = file.length
|
|
155
|
+
if filelen > (Constants::LENGTH_MAX - file.offset)
|
|
156
|
+
unless salvage
|
|
157
|
+
raise DecompressionError,
|
|
158
|
+
"File length exceeds 2GB limit"
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
filelen = Constants::LENGTH_MAX - file.offset
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
filelen
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Validate file fits within folder
|
|
168
|
+
#
|
|
169
|
+
# @param folder [Models::Folder] Folder to check
|
|
170
|
+
# @param file_offset [Integer] File offset
|
|
171
|
+
# @param filelen [Integer] File length
|
|
172
|
+
# @param salvage [Boolean] Salvage mode flag
|
|
173
|
+
def validate_file_in_folder(folder, file_offset, filelen, salvage)
|
|
174
|
+
return if salvage
|
|
175
|
+
|
|
176
|
+
max_len = folder.num_blocks * Constants::BLOCK_MAX
|
|
177
|
+
return unless file_offset > max_len || filelen > (max_len - file_offset)
|
|
178
|
+
|
|
179
|
+
raise DecompressionError, "File extends beyond folder data"
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Setup decompressor for folder
|
|
183
|
+
#
|
|
184
|
+
# @param folder [Models::Folder] Folder to setup for
|
|
185
|
+
# @param salvage [Boolean] Salvage mode flag
|
|
186
|
+
# @param file_offset [Integer] File offset for reset condition check
|
|
187
|
+
def setup_decompressor_for_folder(folder, salvage, file_offset)
|
|
188
|
+
if ENV["DEBUG_BLOCK"]
|
|
189
|
+
warn "DEBUG extract_file: Checking reset condition"
|
|
190
|
+
warn " @current_folder == folder: #{@current_folder == folder}"
|
|
191
|
+
warn " @current_offset (#{@current_offset}) > file_offset (#{file_offset})"
|
|
192
|
+
warn " @current_decomp.nil?: #{@current_decomp.nil?}"
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
if @current_folder != folder || @current_offset > file_offset || !@current_decomp
|
|
196
|
+
if ENV["DEBUG_BLOCK"]
|
|
197
|
+
warn "DEBUG extract_file: RESETTING state (creating new BlockReader)"
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# Reset state
|
|
201
|
+
@current_input&.close
|
|
202
|
+
@current_input = nil
|
|
203
|
+
@current_decomp = nil
|
|
204
|
+
|
|
205
|
+
# Create new input (libmspack lines 1092-1095)
|
|
206
|
+
@current_input = BlockReader.new(@io_system, folder.data,
|
|
207
|
+
folder.num_blocks, salvage)
|
|
208
|
+
@current_folder = folder
|
|
209
|
+
@current_offset = 0
|
|
210
|
+
|
|
211
|
+
# Create decompressor ONCE and reuse it
|
|
212
|
+
@current_decomp = @decompressor.create_decompressor(folder,
|
|
213
|
+
@current_input, nil)
|
|
214
|
+
elsif ENV["DEBUG_BLOCK"]
|
|
215
|
+
warn "DEBUG extract_file: NOT resetting (reusing existing BlockReader)"
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
# Skip to file offset
|
|
220
|
+
#
|
|
221
|
+
# @param file_offset [Integer] Target offset
|
|
222
|
+
# @param salvage [Boolean] Salvage mode flag
|
|
223
|
+
# @param filename [String] Filename for error messages
|
|
224
|
+
def skip_to_file_offset(file_offset, salvage, filename)
|
|
225
|
+
return unless file_offset > @current_offset
|
|
226
|
+
|
|
227
|
+
skip_bytes = file_offset - @current_offset
|
|
228
|
+
null_output = System::MemoryHandle.new("", Constants::MODE_WRITE)
|
|
229
|
+
|
|
230
|
+
@current_decomp.instance_variable_set(:@output, null_output)
|
|
231
|
+
@current_decomp.set_output_length(skip_bytes) if @current_decomp.respond_to?(:set_output_length)
|
|
232
|
+
|
|
233
|
+
begin
|
|
234
|
+
@current_decomp.decompress(skip_bytes)
|
|
235
|
+
rescue DecompressionError
|
|
236
|
+
if salvage
|
|
237
|
+
warn "Salvage: unable to skip to file #{filename}, resetting state"
|
|
238
|
+
reset_state
|
|
239
|
+
else
|
|
240
|
+
raise
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
@current_offset += skip_bytes
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# Write file data using decompressor
|
|
247
|
+
#
|
|
248
|
+
# @param output_fh [System::FileHandle] Output file handle
|
|
249
|
+
# @param filelen [Integer] Number of bytes to write
|
|
250
|
+
def write_file_data(output_fh, filelen)
|
|
251
|
+
@current_decomp.instance_variable_set(:@output, output_fh)
|
|
252
|
+
@current_decomp.set_output_length(filelen) if @current_decomp.respond_to?(:set_output_length)
|
|
253
|
+
@current_decomp.decompress(filelen)
|
|
254
|
+
@current_offset += filelen
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
# Handle extraction error
|
|
258
|
+
#
|
|
259
|
+
# @param output_fh [System::FileHandle] Output file handle
|
|
260
|
+
# @param output_path [String] Output file path
|
|
261
|
+
# @param filename [String] Filename for error messages
|
|
262
|
+
# @param salvage [Boolean] Salvage mode flag
|
|
263
|
+
# @raise [DecompressionError] If not in salvage mode
|
|
264
|
+
def handle_extraction_error(output_fh, output_path, filename, salvage, _filelen)
|
|
265
|
+
output_fh.close
|
|
266
|
+
if salvage
|
|
267
|
+
::File.write(output_path, "", mode: "wb")
|
|
268
|
+
warn "Salvage: created empty file for #{filename} due to decompression error"
|
|
269
|
+
reset_state
|
|
270
|
+
else
|
|
271
|
+
raise
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
|
|
193
275
|
# Set file attributes based on CAB attributes
|
|
194
276
|
#
|
|
195
277
|
# @param path [String] File path
|
|
@@ -105,6 +105,9 @@ module Cabriolet
|
|
|
105
105
|
reset_interval: 0, output_length: 0, is_delta: false, salvage: false, **_kwargs)
|
|
106
106
|
super(io_system, input, output, buffer_size)
|
|
107
107
|
|
|
108
|
+
# Store salvage flag for error handling
|
|
109
|
+
@salvage = salvage
|
|
110
|
+
|
|
108
111
|
# Validate window_bits
|
|
109
112
|
if is_delta
|
|
110
113
|
unless (17..25).cover?(window_bits)
|
|
@@ -195,7 +198,17 @@ module Cabriolet
|
|
|
195
198
|
frame_size = calculate_frame_size
|
|
196
199
|
|
|
197
200
|
# Decode blocks until frame is complete
|
|
198
|
-
|
|
201
|
+
begin
|
|
202
|
+
decode_frame(frame_size)
|
|
203
|
+
rescue DecompressionError => e
|
|
204
|
+
# In salvage mode, if decompression fails, return what we have so far
|
|
205
|
+
if @salvage
|
|
206
|
+
warn "Salvage: LZX decompression failed at frame #{@frame}: #{e.message}"
|
|
207
|
+
return total_written
|
|
208
|
+
else
|
|
209
|
+
raise
|
|
210
|
+
end
|
|
211
|
+
end
|
|
199
212
|
|
|
200
213
|
# Apply Intel E8 transformation if needed
|
|
201
214
|
frame_data = if should_apply_e8_transform?(frame_size)
|
|
@@ -391,6 +404,35 @@ module Cabriolet
|
|
|
391
404
|
@maintree = Huffman::Tree.new(@maintree_lengths, @maintree_maxsymbols,
|
|
392
405
|
bit_order: :msb)
|
|
393
406
|
unless @maintree.build_table(LENGTH_TABLEBITS)
|
|
407
|
+
# In salvage mode, try to build with a default distribution
|
|
408
|
+
if @salvage
|
|
409
|
+
# For a valid Huffman tree with @maintree_maxsymbols symbols and LENGTH_TABLEBITS=12,
|
|
410
|
+
# we need sum(2^(12-len)) = 4096 (for complete tree) or <= 4096 (for partial).
|
|
411
|
+
# For @maintree_maxsymbols = 784, we need to distribute symbols across lengths 8-10:
|
|
412
|
+
# Using: 128 at len8 (2048 slots) + 384 at len9 (768 slots) + 272 at len10 (256 slots)
|
|
413
|
+
# Total: 2048 + 768 + 256 = 3072 slots, leaving 1024 for longer codes
|
|
414
|
+
# Simpler: use lengths that sum to exactly 4096
|
|
415
|
+
# 784 symbols: distribute as 192 at len9, 592 at len10 = 384 + 592 = 976 (not enough)
|
|
416
|
+
# 784 symbols: distribute as 64 at len8, 576 at len9, 144 at len10 = 128 + 1152 + 144 = 1424
|
|
417
|
+
# Final: 784 symbols across lengths 8-11 to fill 4096 slots
|
|
418
|
+
# Verify: 64*128 + 384*64 + 256*32 + 80*16 = 8192 + 24576 + 8192 + 1280 = 42240 (wrong)
|
|
419
|
+
|
|
420
|
+
# Recalculate: 2^(12-len) slots needed per symbol
|
|
421
|
+
# len8: 16 slots/symbol, len9: 8 slots/symbol, len10: 4 slots/symbol, len11: 2 slots/symbol
|
|
422
|
+
# Total slots = sum(2^(12-len) for each symbol) must <= 4096
|
|
423
|
+
# Simple valid distribution for 784 symbols:
|
|
424
|
+
# 256 at len10 = 256*4 = 1024
|
|
425
|
+
# 528 at len12 = 528*1 = 528
|
|
426
|
+
# Total = 1552 (valid but incomplete tree)
|
|
427
|
+
|
|
428
|
+
default_main_lengths = []
|
|
429
|
+
256.times { default_main_lengths << 10 }
|
|
430
|
+
528.times { default_main_lengths << 12 }
|
|
431
|
+
@maintree_lengths = default_main_lengths
|
|
432
|
+
@maintree = Huffman::Tree.new(default_main_lengths, @maintree_maxsymbols,
|
|
433
|
+
bit_order: :msb)
|
|
434
|
+
return if @maintree.build_table(LENGTH_TABLEBITS)
|
|
435
|
+
end
|
|
394
436
|
raise DecompressionError,
|
|
395
437
|
"Failed to build main tree"
|
|
396
438
|
end
|
|
@@ -428,6 +470,22 @@ module Cabriolet
|
|
|
428
470
|
bit_order: :msb)
|
|
429
471
|
return if @pretree.build_table(PRETREE_TABLEBITS)
|
|
430
472
|
|
|
473
|
+
# In salvage mode, try to continue with a valid default tree
|
|
474
|
+
if @salvage
|
|
475
|
+
# For a valid Huffman tree with table_bits=6, we need exactly 64 slots.
|
|
476
|
+
# With 8 symbols at length 3: 8 * 2^(6-3) = 8 * 8 = 64 slots (complete)
|
|
477
|
+
# For simplicity: 8 at length 3 fills direct table (64 slots)
|
|
478
|
+
default_lengths = [
|
|
479
|
+
3, 3, 3, 3, 3, 3, 3, 3, # 8 at length 3: fills 64 slots
|
|
480
|
+
7, 7, 7, 7, 7, 7, 7, 7, # 8 at length 7: extended table
|
|
481
|
+
7, 7, 7, 7 # 4 at length 7: extended table
|
|
482
|
+
]
|
|
483
|
+
@pretree_lengths = default_lengths
|
|
484
|
+
@pretree = Huffman::Tree.new(default_lengths, PRETREE_MAXSYMBOLS,
|
|
485
|
+
bit_order: :msb)
|
|
486
|
+
return if @pretree.build_table(PRETREE_TABLEBITS)
|
|
487
|
+
end
|
|
488
|
+
|
|
431
489
|
raise DecompressionError, "Failed to build pretree"
|
|
432
490
|
end
|
|
433
491
|
|
data/lib/cabriolet/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: cabriolet
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-02-20 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bindata
|