Class: Omnizip::Formats::Iso::VolumeBuilder

Inherits:
Object
  • Object
show all
Defined in:
lib/omnizip/formats/iso/volume_builder.rb

Overview

ISO 9660 Volume Descriptor Builder

Builds primary and supplementary volume descriptors for ISO images. Handles proper encoding of volume metadata and root directory information.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ VolumeBuilder

Initialize volume builder

Parameters:

  • options (Hash) (defaults to: {})

    Builder options



40
41
42
43
44
45
46
47
48
49
# File 'lib/omnizip/formats/iso/volume_builder.rb', line 40

def initialize(options = {})
  @volume_id = options.fetch(:volume_id, "OMNIZIP")
  @system_id = options.fetch(:system_id, "LINUX")
  @publisher = options.fetch(:publisher, "")
  @preparer = options.fetch(:preparer, "")
  @application = options.fetch(:application, "OMNIZIP")
  @level = options.fetch(:level, 2)
  @rock_ridge = options.fetch(:rock_ridge, false)
  @joliet = options.fetch(:joliet, false)
end

Instance Attribute Details

#applicationString (readonly)

Returns Application.

Returns:

  • (String)

    Application



26
27
28
# File 'lib/omnizip/formats/iso/volume_builder.rb', line 26

def application
  @application
end

#jolietBoolean (readonly)

Returns Joliet enabled.

Returns:

  • (Boolean)

    Joliet enabled



35
36
37
# File 'lib/omnizip/formats/iso/volume_builder.rb', line 35

def joliet
  @joliet
end

#levelInteger (readonly)

Returns ISO level.

Returns:

  • (Integer)

    ISO level



29
30
31
# File 'lib/omnizip/formats/iso/volume_builder.rb', line 29

def level
  @level
end

#preparerString (readonly)

Returns Preparer.

Returns:

  • (String)

    Preparer



23
24
25
# File 'lib/omnizip/formats/iso/volume_builder.rb', line 23

def preparer
  @preparer
end

#publisherString (readonly)

Returns Publisher.

Returns:

  • (String)

    Publisher



20
21
22
# File 'lib/omnizip/formats/iso/volume_builder.rb', line 20

def publisher
  @publisher
end

#rock_ridgeBoolean (readonly)

Returns Rock Ridge enabled.

Returns:

  • (Boolean)

    Rock Ridge enabled



32
33
34
# File 'lib/omnizip/formats/iso/volume_builder.rb', line 32

def rock_ridge
  @rock_ridge
end

#system_idString (readonly)

Returns System identifier.

Returns:

  • (String)

    System identifier



17
18
19
# File 'lib/omnizip/formats/iso/volume_builder.rb', line 17

def system_id
  @system_id
end

#volume_idString (readonly)

Returns Volume identifier.

Returns:

  • (String)

    Volume identifier



14
15
16
# File 'lib/omnizip/formats/iso/volume_builder.rb', line 14

def volume_id
  @volume_id
end

Instance Method Details

#build_joliet(root_dir) ⇒ String

Build Joliet supplementary volume descriptor

Parameters:

  • root_dir (Hash)

    Root directory information

Returns:

  • (String)

    Joliet SVD sector (2048 bytes)



172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/omnizip/formats/iso/volume_builder.rb', line 172

def build_joliet(root_dir)
  # Joliet is similar to primary VD but with:
  # - Type = 2 (supplementary)
  # - UCS-2 encoding for strings
  # - Escape sequences for character set

  sector = build_primary(root_dir)

  # Modify type to supplementary
  sector.setbyte(0, Iso::VD_SUPPLEMENTARY)

  # Add escape sequences for UCS-2
  # Bytes 88-90: %/@  %/C  %/E for levels 1, 2, 3
  sector.setbyte(88, 0x25)  # '%'
  sector.setbyte(89, 0x2F)  # '/'
  sector.setbyte(90, 0x45)  # 'E'

  # Convert volume ID to UCS-2
  volume_id_ucs2 = @volume_id.encode("UTF-16BE")
  padded_volume_id = pad_string(volume_id_ucs2,
                                32).force_encoding("ASCII-8BIT")

  # Replace bytes 40-71 with UCS-2 volume ID
  32.times do |i|
    sector.setbyte(40 + i, padded_volume_id.getbyte(i))
  end

  sector
end

#build_primary(root_dir) ⇒ String

Build primary volume descriptor

Parameters:

  • root_dir (Hash)

    Root directory information

Returns:

  • (String)

    Primary VD sector (2048 bytes)



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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/omnizip/formats/iso/volume_builder.rb', line 55

def build_primary(root_dir)
  sector = +""

  # Byte 0: Volume descriptor type (1 = Primary)
  sector << [Iso::VD_PRIMARY].pack("C")

  # Bytes 1-5: Standard identifier "CD001"
  sector << VolumeDescriptor::ISO_IDENTIFIER

  # Byte 6: Version (1)
  sector << [1].pack("C")

  # Byte 7: Unused (0)
  sector << "\x00"

  # Bytes 8-39: System identifier (a-characters, space-padded)
  sector << pad_a_string(@system_id, 32)

  # Bytes 40-71: Volume identifier (d-characters, space-padded)
  sector << pad_d_string(@volume_id, 32)

  # Bytes 72-79: Unused (zeros)
  sector << ("\x00" * 8)

  # Bytes 80-87: Volume space size (both-endian)
  volume_sectors = root_dir[:total_sectors] || 100
  sector << [volume_sectors].pack("V") # Little-endian
  sector << [volume_sectors].pack("N") # Big-endian

  # Bytes 88-119: Unused (zeros)
  sector << ("\x00" * 32)

  # Bytes 120-123: Volume set size (both-endian)
  sector << [1].pack("v")
  sector << [1].pack("n")

  # Bytes 124-127: Volume sequence number (both-endian)
  sector << [1].pack("v")
  sector << [1].pack("n")

  # Bytes 128-131: Logical block size (both-endian)
  sector << [Iso::SECTOR_SIZE].pack("v")
  sector << [Iso::SECTOR_SIZE].pack("n")

  # Bytes 132-139: Path table size (both-endian)
  path_table_size = root_dir[:path_table_size] || 10
  sector << [path_table_size].pack("V")
  sector << [path_table_size].pack("N")

  # Bytes 140-143: Location of type L path table
  sector << [root_dir[:path_table_location] || 19].pack("V")

  # Bytes 144-147: Location of optional type L path table
  sector << [0].pack("V")

  # Bytes 148-151: Location of type M path table
  sector << [root_dir[:path_table_location_be] || 20].pack("N")

  # Bytes 152-155: Location of optional type M path table
  sector << [0].pack("N")

  # Bytes 156-189: Root directory record (34 bytes)
  sector << build_root_directory_record(root_dir)

  # Bytes 190-317: Volume set identifier
  sector << pad_d_string("", 128)

  # Bytes 318-445: Publisher identifier
  sector << pad_a_string(@publisher, 128)

  # Bytes 446-573: Data preparer identifier
  sector << pad_a_string(@preparer, 128)

  # Bytes 574-701: Application identifier
  sector << pad_a_string(@application, 128)

  # Bytes 702-738: Copyright file identifier
  sector << pad_d_string("", 37)

  # Bytes 739-775: Abstract file identifier
  sector << pad_d_string("", 37)

  # Bytes 776-812: Bibliographic file identifier
  sector << pad_d_string("", 37)

  # Bytes 813-829: Volume creation date/time
  now = Time.now
  sector << encode_volume_datetime(now)

  # Bytes 830-846: Volume modification date/time
  sector << encode_volume_datetime(now)

  # Bytes 847-863: Volume expiration date/time (not set)
  sector << encode_volume_datetime(nil)

  # Bytes 864-880: Volume effective date/time (not set)
  sector << encode_volume_datetime(nil)

  # Byte 881: File structure version (1)
  sector << [1].pack("C")

  # Byte 882: Unused (0)
  sector << "\x00"

  # Bytes 883-1395: Application use
  sector << ("\x00" * 513)

  # Bytes 1396-2047: Reserved
  sector << ("\x00" * 652)

  sector
end