cabriolet 0.1.2 → 0.2.1
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/README.adoc +703 -38
- data/lib/cabriolet/algorithm_factory.rb +250 -0
- data/lib/cabriolet/base_compressor.rb +206 -0
- data/lib/cabriolet/binary/bitstream.rb +167 -16
- data/lib/cabriolet/binary/bitstream_writer.rb +150 -21
- data/lib/cabriolet/binary/chm_structures.rb +2 -2
- data/lib/cabriolet/binary/hlp_structures.rb +258 -37
- data/lib/cabriolet/binary/lit_structures.rb +231 -65
- data/lib/cabriolet/binary/oab_structures.rb +17 -1
- data/lib/cabriolet/cab/command_handler.rb +226 -0
- data/lib/cabriolet/cab/compressor.rb +108 -84
- data/lib/cabriolet/cab/decompressor.rb +16 -20
- data/lib/cabriolet/cab/extractor.rb +142 -66
- data/lib/cabriolet/cab/file_compression_work.rb +52 -0
- data/lib/cabriolet/cab/file_compression_worker.rb +89 -0
- data/lib/cabriolet/checksum.rb +49 -0
- data/lib/cabriolet/chm/command_handler.rb +227 -0
- data/lib/cabriolet/chm/compressor.rb +7 -3
- data/lib/cabriolet/chm/decompressor.rb +39 -21
- data/lib/cabriolet/chm/parser.rb +5 -2
- data/lib/cabriolet/cli/base_command_handler.rb +127 -0
- data/lib/cabriolet/cli/command_dispatcher.rb +140 -0
- data/lib/cabriolet/cli/command_registry.rb +83 -0
- data/lib/cabriolet/cli.rb +356 -607
- data/lib/cabriolet/collections/file_collection.rb +175 -0
- data/lib/cabriolet/compressors/base.rb +1 -1
- data/lib/cabriolet/compressors/lzx.rb +241 -54
- data/lib/cabriolet/compressors/mszip.rb +35 -3
- data/lib/cabriolet/compressors/quantum.rb +36 -95
- data/lib/cabriolet/decompressors/base.rb +1 -1
- data/lib/cabriolet/decompressors/lzss.rb +13 -3
- data/lib/cabriolet/decompressors/lzx.rb +70 -33
- data/lib/cabriolet/decompressors/mszip.rb +126 -39
- data/lib/cabriolet/decompressors/quantum.rb +83 -53
- data/lib/cabriolet/errors.rb +3 -0
- data/lib/cabriolet/extraction/base_extractor.rb +88 -0
- data/lib/cabriolet/extraction/extractor.rb +171 -0
- data/lib/cabriolet/extraction/file_extraction_work.rb +60 -0
- data/lib/cabriolet/extraction/file_extraction_worker.rb +106 -0
- data/lib/cabriolet/file_entry.rb +156 -0
- data/lib/cabriolet/file_manager.rb +144 -0
- data/lib/cabriolet/format_base.rb +79 -0
- data/lib/cabriolet/hlp/command_handler.rb +282 -0
- data/lib/cabriolet/hlp/compressor.rb +28 -238
- data/lib/cabriolet/hlp/decompressor.rb +107 -147
- data/lib/cabriolet/hlp/parser.rb +52 -101
- data/lib/cabriolet/hlp/quickhelp/compression_stream.rb +138 -0
- data/lib/cabriolet/hlp/quickhelp/compressor.rb +151 -0
- data/lib/cabriolet/hlp/quickhelp/decompressor.rb +558 -0
- data/lib/cabriolet/hlp/quickhelp/file_writer.rb +125 -0
- data/lib/cabriolet/hlp/quickhelp/huffman_stream.rb +74 -0
- data/lib/cabriolet/hlp/quickhelp/huffman_tree.rb +167 -0
- data/lib/cabriolet/hlp/quickhelp/offset_calculator.rb +61 -0
- data/lib/cabriolet/hlp/quickhelp/parser.rb +274 -0
- data/lib/cabriolet/hlp/quickhelp/structure_builder.rb +93 -0
- data/lib/cabriolet/hlp/quickhelp/topic_builder.rb +52 -0
- data/lib/cabriolet/hlp/quickhelp/topic_compressor.rb +83 -0
- data/lib/cabriolet/hlp/winhelp/btree_builder.rb +289 -0
- data/lib/cabriolet/hlp/winhelp/compressor.rb +400 -0
- data/lib/cabriolet/hlp/winhelp/decompressor.rb +192 -0
- data/lib/cabriolet/hlp/winhelp/parser.rb +484 -0
- data/lib/cabriolet/hlp/winhelp/zeck_lz77.rb +271 -0
- data/lib/cabriolet/huffman/encoder.rb +15 -12
- data/lib/cabriolet/huffman/tree.rb +85 -1
- data/lib/cabriolet/kwaj/command_handler.rb +213 -0
- data/lib/cabriolet/kwaj/compressor.rb +7 -3
- data/lib/cabriolet/kwaj/decompressor.rb +18 -12
- data/lib/cabriolet/lit/command_handler.rb +221 -0
- data/lib/cabriolet/lit/compressor.rb +119 -168
- data/lib/cabriolet/lit/content_encoder.rb +76 -0
- data/lib/cabriolet/lit/content_type_detector.rb +50 -0
- data/lib/cabriolet/lit/decompressor.rb +518 -152
- data/lib/cabriolet/lit/directory_builder.rb +153 -0
- data/lib/cabriolet/lit/guid_generator.rb +16 -0
- data/lib/cabriolet/lit/header_writer.rb +124 -0
- data/lib/cabriolet/lit/parser.rb +670 -0
- data/lib/cabriolet/lit/piece_builder.rb +74 -0
- data/lib/cabriolet/lit/structure_builder.rb +252 -0
- data/lib/cabriolet/models/hlp_file.rb +130 -29
- data/lib/cabriolet/models/hlp_header.rb +105 -17
- data/lib/cabriolet/models/lit_header.rb +212 -25
- data/lib/cabriolet/models/szdd_header.rb +10 -2
- data/lib/cabriolet/models/winhelp_header.rb +127 -0
- data/lib/cabriolet/oab/command_handler.rb +257 -0
- data/lib/cabriolet/oab/compressor.rb +17 -8
- data/lib/cabriolet/oab/decompressor.rb +41 -10
- data/lib/cabriolet/offset_calculator.rb +81 -0
- data/lib/cabriolet/plugin.rb +233 -0
- data/lib/cabriolet/plugin_manager.rb +453 -0
- data/lib/cabriolet/plugin_validator.rb +422 -0
- data/lib/cabriolet/quantum_shared.rb +105 -0
- data/lib/cabriolet/system/io_system.rb +3 -0
- data/lib/cabriolet/system/memory_handle.rb +17 -4
- data/lib/cabriolet/szdd/command_handler.rb +217 -0
- data/lib/cabriolet/szdd/compressor.rb +15 -11
- data/lib/cabriolet/szdd/decompressor.rb +18 -9
- data/lib/cabriolet/version.rb +1 -1
- data/lib/cabriolet.rb +181 -20
- metadata +69 -4
- data/lib/cabriolet/auto.rb +0 -173
- data/lib/cabriolet/parallel.rb +0 -333
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Cabriolet
|
|
4
|
+
module LIT
|
|
5
|
+
# Builds LIT directory structure with AOLL chunks
|
|
6
|
+
class DirectoryBuilder
|
|
7
|
+
# Chunk size for directory entries (8KB)
|
|
8
|
+
DEFAULT_CHUNK_SIZE = 0x2000
|
|
9
|
+
|
|
10
|
+
attr_reader :chunk_size, :entries
|
|
11
|
+
|
|
12
|
+
# Initialize directory builder
|
|
13
|
+
#
|
|
14
|
+
# @param chunk_size [Integer] Chunk size for directory entries
|
|
15
|
+
def initialize(chunk_size: DEFAULT_CHUNK_SIZE)
|
|
16
|
+
@chunk_size = chunk_size
|
|
17
|
+
@entries = []
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Add an entry to the directory
|
|
21
|
+
#
|
|
22
|
+
# @param name [String] Entry name
|
|
23
|
+
# @param section [Integer] Section number
|
|
24
|
+
# @param offset [Integer] Offset within section
|
|
25
|
+
# @param size [Integer] Size in bytes
|
|
26
|
+
def add_entry(name:, section:, offset:, size:)
|
|
27
|
+
@entries << {
|
|
28
|
+
name: name,
|
|
29
|
+
section: section,
|
|
30
|
+
offset: offset,
|
|
31
|
+
size: size,
|
|
32
|
+
}
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Build the directory structure
|
|
36
|
+
#
|
|
37
|
+
# @return [Hash] Directory structure with entries and metadata
|
|
38
|
+
def build
|
|
39
|
+
{
|
|
40
|
+
entries: @entries,
|
|
41
|
+
chunk_size: @chunk_size,
|
|
42
|
+
num_chunks: calculate_num_chunks,
|
|
43
|
+
}
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Build AOLL (Archive Object List List) chunk
|
|
47
|
+
#
|
|
48
|
+
# @return [String] Binary AOLL chunk data
|
|
49
|
+
def build_aoll_chunk
|
|
50
|
+
# Build all entry data first
|
|
51
|
+
entries_data = @entries.map { |entry| encode_entry(entry) }.join
|
|
52
|
+
|
|
53
|
+
# Calculate quickref offset (starts after entries data)
|
|
54
|
+
quickref_offset = entries_data.bytesize
|
|
55
|
+
|
|
56
|
+
# Build AOLL header
|
|
57
|
+
header = Binary::LITStructures::AOLLHeader.new
|
|
58
|
+
header.tag = Binary::LITStructures::Tags::AOLL
|
|
59
|
+
header.quickref_offset = quickref_offset
|
|
60
|
+
header.current_chunk_low = 0
|
|
61
|
+
header.current_chunk_high = 0
|
|
62
|
+
header.prev_chunk_low = 0xFFFFFFFF
|
|
63
|
+
header.prev_chunk_high = 0xFFFFFFFF
|
|
64
|
+
header.next_chunk_low = 0xFFFFFFFF
|
|
65
|
+
header.next_chunk_high = 0xFFFFFFFF
|
|
66
|
+
header.entries_so_far = @entries.size
|
|
67
|
+
header.reserved = 0
|
|
68
|
+
header.chunk_distance = 0
|
|
69
|
+
header.reserved2 = 0
|
|
70
|
+
|
|
71
|
+
header.to_binary_s + entries_data
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Calculate total size needed for directory
|
|
75
|
+
#
|
|
76
|
+
# @return [Integer] Size in bytes
|
|
77
|
+
def calculate_size
|
|
78
|
+
# IFCM header + AOLL chunk + padding
|
|
79
|
+
ifcm_size = Binary::LITStructures::IFCMHeader.new.to_binary_s.bytesize
|
|
80
|
+
aoll_size = build_aoll_chunk.bytesize
|
|
81
|
+
target_size = @chunk_size
|
|
82
|
+
|
|
83
|
+
[ifcm_size + aoll_size, target_size].max
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
private
|
|
87
|
+
|
|
88
|
+
# Calculate number of chunks needed
|
|
89
|
+
#
|
|
90
|
+
# @return [Integer] Number of chunks
|
|
91
|
+
def calculate_num_chunks
|
|
92
|
+
return 1 if @entries.empty?
|
|
93
|
+
|
|
94
|
+
total_size = @entries.sum { |e| estimate_entry_size(e) }
|
|
95
|
+
[1, (total_size / @chunk_size.to_f).ceil].max
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Estimate size of a directory entry
|
|
99
|
+
#
|
|
100
|
+
# @param entry [Hash] Directory entry
|
|
101
|
+
# @return [Integer] Estimated size
|
|
102
|
+
def estimate_entry_size(entry)
|
|
103
|
+
name_size = entry[:name].bytesize
|
|
104
|
+
# Name length (1-5 bytes) + name + section (1-5 bytes) + offset (1-5 bytes) + size (1-5 bytes)
|
|
105
|
+
5 + name_size + 15
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Encode a directory entry with variable-length integers
|
|
109
|
+
#
|
|
110
|
+
# @param entry [Hash] Directory entry
|
|
111
|
+
# @return [String] Encoded entry data
|
|
112
|
+
def encode_entry(entry)
|
|
113
|
+
data = +""
|
|
114
|
+
|
|
115
|
+
# Encode name length and name
|
|
116
|
+
name = entry[:name].dup.force_encoding("UTF-8")
|
|
117
|
+
data += encode_vint(name.bytesize)
|
|
118
|
+
data += name
|
|
119
|
+
|
|
120
|
+
# Encode section, offset, size
|
|
121
|
+
data += encode_vint(entry[:section])
|
|
122
|
+
data += encode_vint(entry[:offset])
|
|
123
|
+
data += encode_vint(entry[:size])
|
|
124
|
+
|
|
125
|
+
data
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Write a variable-length integer (MSB = continuation bit)
|
|
129
|
+
#
|
|
130
|
+
# @param value [Integer] Value to encode
|
|
131
|
+
# @return [String] Encoded integer
|
|
132
|
+
def encode_vint(value)
|
|
133
|
+
return [0x00].pack("C") if value.zero?
|
|
134
|
+
|
|
135
|
+
bytes = []
|
|
136
|
+
|
|
137
|
+
# Extract 7-bit chunks from value
|
|
138
|
+
loop do
|
|
139
|
+
bytes.unshift(value & 0x7F)
|
|
140
|
+
value >>= 7
|
|
141
|
+
break if value.zero?
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Set MSB on all bytes except the last
|
|
145
|
+
(0...(bytes.size - 1)).each do |i|
|
|
146
|
+
bytes[i] |= 0x80
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
bytes.pack("C*")
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Cabriolet
|
|
4
|
+
module LIT
|
|
5
|
+
# Generates GUIDs for LIT files
|
|
6
|
+
class GuidGenerator
|
|
7
|
+
# Generate a random GUID
|
|
8
|
+
#
|
|
9
|
+
# @return [String] 16-byte random GUID
|
|
10
|
+
def self.generate
|
|
11
|
+
require "securerandom"
|
|
12
|
+
SecureRandom.random_bytes(16)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Cabriolet
|
|
4
|
+
module LIT
|
|
5
|
+
# Writes LIT headers to output
|
|
6
|
+
class HeaderWriter
|
|
7
|
+
# Initialize header writer
|
|
8
|
+
#
|
|
9
|
+
# @param io_system [System::IOSystem] I/O system for writing
|
|
10
|
+
def initialize(io_system)
|
|
11
|
+
@io_system = io_system
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Write primary header
|
|
15
|
+
#
|
|
16
|
+
# @param output_handle [System::FileHandle] Output file handle
|
|
17
|
+
# @param structure [Hash] LIT structure
|
|
18
|
+
# @return [Integer] Bytes written
|
|
19
|
+
def write_primary_header(output_handle, structure)
|
|
20
|
+
header = Binary::LITStructures::PrimaryHeader.new
|
|
21
|
+
header.signature = Binary::LITStructures::SIGNATURE
|
|
22
|
+
header.version = structure[:version]
|
|
23
|
+
header.header_length = 40
|
|
24
|
+
header.num_pieces = 5
|
|
25
|
+
header.secondary_header_length = structure[:secondary_header][:length]
|
|
26
|
+
header.header_guid = structure[:header_guid]
|
|
27
|
+
|
|
28
|
+
header_data = header.to_binary_s
|
|
29
|
+
@io_system.write(output_handle, header_data)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Write piece structures
|
|
33
|
+
#
|
|
34
|
+
# @param output_handle [System::FileHandle] Output file handle
|
|
35
|
+
# @param pieces [Array<Hash>] Pieces array
|
|
36
|
+
# @return [Integer] Bytes written
|
|
37
|
+
def write_piece_structures(output_handle, pieces)
|
|
38
|
+
total_bytes = 0
|
|
39
|
+
|
|
40
|
+
pieces.each do |piece|
|
|
41
|
+
piece_struct = Binary::LITStructures::PieceStructure.new
|
|
42
|
+
piece_struct.offset_low = piece[:offset]
|
|
43
|
+
piece_struct.offset_high = 0
|
|
44
|
+
piece_struct.size_low = piece[:size]
|
|
45
|
+
piece_struct.size_high = 0
|
|
46
|
+
|
|
47
|
+
piece_data = piece_struct.to_binary_s
|
|
48
|
+
total_bytes += @io_system.write(output_handle, piece_data)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
total_bytes
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Write secondary header block
|
|
55
|
+
#
|
|
56
|
+
# @param output_handle [System::FileHandle] Output file handle
|
|
57
|
+
# @param sec_hdr [Hash] Secondary header metadata
|
|
58
|
+
# @return [Integer] Bytes written
|
|
59
|
+
def write_secondary_header(output_handle, sec_hdr)
|
|
60
|
+
header = Binary::LITStructures::SecondaryHeader.new
|
|
61
|
+
|
|
62
|
+
# SECHDR block
|
|
63
|
+
header.sechdr_version = 2
|
|
64
|
+
header.sechdr_length = 152
|
|
65
|
+
|
|
66
|
+
# Entry directory info
|
|
67
|
+
header.entry_aoli_idx = 0
|
|
68
|
+
header.entry_aoli_idx_high = 0
|
|
69
|
+
header.entry_reserved1 = 0
|
|
70
|
+
header.entry_last_aoll = 0
|
|
71
|
+
header.entry_reserved2 = 0
|
|
72
|
+
header.entry_chunklen = sec_hdr[:entry_chunklen]
|
|
73
|
+
header.entry_two = 2
|
|
74
|
+
header.entry_reserved3 = 0
|
|
75
|
+
header.entry_depth = sec_hdr[:entry_depth]
|
|
76
|
+
header.entry_reserved4 = 0
|
|
77
|
+
header.entry_entries = sec_hdr[:entry_entries]
|
|
78
|
+
header.entry_reserved5 = 0
|
|
79
|
+
|
|
80
|
+
# Count directory info
|
|
81
|
+
header.count_aoli_idx = 0xFFFFFFFF
|
|
82
|
+
header.count_aoli_idx_high = 0xFFFFFFFF
|
|
83
|
+
header.count_reserved1 = 0
|
|
84
|
+
header.count_last_aoll = 0
|
|
85
|
+
header.count_reserved2 = 0
|
|
86
|
+
header.count_chunklen = sec_hdr[:count_chunklen]
|
|
87
|
+
header.count_two = 2
|
|
88
|
+
header.count_reserved3 = 0
|
|
89
|
+
header.count_depth = 1
|
|
90
|
+
header.count_reserved4 = 0
|
|
91
|
+
header.count_entries = sec_hdr[:count_entries]
|
|
92
|
+
header.count_reserved5 = 0
|
|
93
|
+
|
|
94
|
+
header.entry_unknown = sec_hdr[:entry_unknown]
|
|
95
|
+
header.count_unknown = sec_hdr[:count_unknown]
|
|
96
|
+
|
|
97
|
+
# CAOL block
|
|
98
|
+
header.caol_tag = Binary::LITStructures::Tags::CAOL
|
|
99
|
+
header.caol_version = 2
|
|
100
|
+
header.caol_length = 80 # 48 + 32
|
|
101
|
+
header.creator_id = sec_hdr[:creator_id]
|
|
102
|
+
header.caol_reserved1 = 0
|
|
103
|
+
header.caol_entry_chunklen = sec_hdr[:entry_chunklen]
|
|
104
|
+
header.caol_count_chunklen = sec_hdr[:count_chunklen]
|
|
105
|
+
header.caol_entry_unknown = sec_hdr[:entry_unknown]
|
|
106
|
+
header.caol_count_unknown = sec_hdr[:count_unknown]
|
|
107
|
+
header.caol_reserved2 = 0
|
|
108
|
+
|
|
109
|
+
# ITSF block
|
|
110
|
+
header.itsf_tag = Binary::LITStructures::Tags::ITSF
|
|
111
|
+
header.itsf_version = 4
|
|
112
|
+
header.itsf_length = 32
|
|
113
|
+
header.itsf_unknown = 1
|
|
114
|
+
header.content_offset_low = sec_hdr[:content_offset]
|
|
115
|
+
header.content_offset_high = 0
|
|
116
|
+
header.timestamp = sec_hdr[:timestamp]
|
|
117
|
+
header.language_id = sec_hdr[:language_id]
|
|
118
|
+
|
|
119
|
+
header_data = header.to_binary_s
|
|
120
|
+
@io_system.write(output_handle, header_data)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|