format_parser 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -0
- data/.travis.yml +1 -0
- data/README.md +14 -11
- data/format_parser.gemspec +11 -10
- data/lib/care.rb +9 -17
- data/lib/format_parser.rb +11 -13
- data/lib/format_parser/version.rb +1 -1
- data/lib/io_constraint.rb +3 -3
- data/lib/io_utils.rb +4 -10
- data/lib/parsers/aiff_parser.rb +9 -10
- data/lib/parsers/dpx_parser.rb +42 -42
- data/lib/parsers/dsl.rb +2 -2
- data/lib/parsers/exif_parser.rb +3 -8
- data/lib/parsers/fdx_parser.rb +3 -3
- data/lib/parsers/gif_parser.rb +3 -5
- data/lib/parsers/jpeg_parser.rb +4 -8
- data/lib/parsers/moov_parser.rb +8 -6
- data/lib/parsers/moov_parser/decoder.rb +105 -122
- data/lib/parsers/mp3_parser.rb +36 -46
- data/lib/parsers/mp3_parser/id3_v1.rb +7 -13
- data/lib/parsers/mp3_parser/id3_v2.rb +6 -6
- data/lib/parsers/png_parser.rb +5 -12
- data/lib/parsers/psd_parser.rb +2 -2
- data/lib/parsers/tiff_parser.rb +10 -12
- data/lib/parsers/wav_parser.rb +3 -3
- data/lib/read_limiter.rb +3 -7
- data/lib/remote_io.rb +3 -6
- data/spec/care_spec.rb +10 -10
- data/spec/file_information_spec.rb +1 -3
- data/spec/format_parser_spec.rb +6 -6
- data/spec/io_utils_spec.rb +7 -7
- data/spec/parsers/exif_parser_spec.rb +2 -3
- data/spec/parsers/gif_parser_spec.rb +1 -1
- data/spec/parsers/jpeg_parser_spec.rb +0 -1
- data/spec/parsers/moov_parser_spec.rb +2 -3
- data/spec/parsers/png_parser_spec.rb +1 -1
- data/spec/parsers/tiff_parser_spec.rb +0 -1
- data/spec/parsers/wav_parser_spec.rb +3 -3
- data/spec/read_limiter_spec.rb +0 -1
- data/spec/remote_fetching_spec.rb +34 -20
- data/spec/remote_io_spec.rb +20 -21
- data/spec/spec_helper.rb +2 -2
- metadata +19 -4
data/lib/parsers/dsl.rb
CHANGED
@@ -18,8 +18,8 @@ module FormatParser
|
|
18
18
|
private
|
19
19
|
|
20
20
|
def __define(name, value)
|
21
|
-
throw ArgumentError(
|
22
|
-
throw ArgumentError(
|
21
|
+
throw ArgumentError('empty array') if value.empty?
|
22
|
+
throw ArgumentError('requires array of symbols') if value.any? { |s| !s.is_a?(Symbol) }
|
23
23
|
define_method(name) do
|
24
24
|
value
|
25
25
|
end
|
data/lib/parsers/exif_parser.rb
CHANGED
@@ -11,8 +11,6 @@ class FormatParser::EXIFParser
|
|
11
11
|
def readbyte
|
12
12
|
if byte = read(1)
|
13
13
|
byte.unpack('C').first
|
14
|
-
else
|
15
|
-
nil
|
16
14
|
end
|
17
15
|
end
|
18
16
|
|
@@ -62,19 +60,16 @@ class FormatParser::EXIFParser
|
|
62
60
|
|
63
61
|
def orientation_parser(raw_exif_data)
|
64
62
|
value = raw_exif_data.orientation.to_i
|
65
|
-
if valid_orientation?(value)
|
66
|
-
@orientation = ORIENTATIONS[value - 1]
|
67
|
-
end
|
63
|
+
@orientation = ORIENTATIONS[value - 1] if valid_orientation?(value)
|
68
64
|
end
|
69
65
|
|
70
66
|
def valid_orientation?(value)
|
71
67
|
(1..ORIENTATIONS.length).include?(value)
|
72
68
|
end
|
73
69
|
|
74
|
-
def cr2_check(
|
70
|
+
def cr2_check(_file_io)
|
75
71
|
@file_io.seek(8)
|
76
72
|
cr2_check_bytes = @file_io.read(2)
|
77
|
-
cr2_check_bytes ==
|
73
|
+
cr2_check_bytes == 'CR'
|
78
74
|
end
|
79
|
-
|
80
75
|
end
|
data/lib/parsers/fdx_parser.rb
CHANGED
@@ -6,7 +6,7 @@ class FormatParser::FDXParser
|
|
6
6
|
natures :document
|
7
7
|
|
8
8
|
def call(io)
|
9
|
-
return
|
9
|
+
return unless xml_check(io)
|
10
10
|
file_and_document_type = safe_read(io, 100)
|
11
11
|
file_type, document_type = check_for_document_type(file_and_document_type)
|
12
12
|
return if file_type != :fdx
|
@@ -18,12 +18,12 @@ class FormatParser::FDXParser
|
|
18
18
|
|
19
19
|
def xml_check(io)
|
20
20
|
xml_check = safe_read(io, 5)
|
21
|
-
xml_check ==
|
21
|
+
xml_check == '<?xml'
|
22
22
|
end
|
23
23
|
|
24
24
|
def check_for_document_type(file_and_document_type)
|
25
25
|
sanitized_data = file_and_document_type.downcase
|
26
|
-
if sanitized_data.include?(
|
26
|
+
if sanitized_data.include?('finaldraft') && sanitized_data.include?('script')
|
27
27
|
return :fdx, :script
|
28
28
|
else
|
29
29
|
return
|
data/lib/parsers/gif_parser.rb
CHANGED
@@ -14,7 +14,7 @@ class FormatParser::GIFParser
|
|
14
14
|
return unless HEADERS.include?(header)
|
15
15
|
|
16
16
|
w, h = safe_read(io, 4).unpack('vv')
|
17
|
-
gct_byte,
|
17
|
+
gct_byte, _bgcolor_index, _pixel_aspect_ratio = safe_read(io, 5).unpack('Cvv')
|
18
18
|
|
19
19
|
# and actually onwards for this:
|
20
20
|
# http://www.matthewflickinger.com/lab/whatsinagif/bits_and_bytes.asp
|
@@ -24,12 +24,10 @@ class FormatParser::GIFParser
|
|
24
24
|
bytes_per_color = gct_byte >> 6
|
25
25
|
unpacked_radix = gct_byte & 0b00000111
|
26
26
|
num_colors = 2**(unpacked_radix + 1)
|
27
|
-
gct_table_size = num_colors*bytes_per_color
|
27
|
+
gct_table_size = num_colors * bytes_per_color
|
28
28
|
|
29
29
|
# If we have the global color table - skip over it
|
30
|
-
if has_gct
|
31
|
-
safe_read(io, gct_table_size)
|
32
|
-
end
|
30
|
+
safe_read(io, gct_table_size) if has_gct
|
33
31
|
|
34
32
|
# Now it gets interesting - we are at the place where an
|
35
33
|
# application extension for the NETSCAPE2.0 block will occur.
|
data/lib/parsers/jpeg_parser.rb
CHANGED
@@ -24,10 +24,6 @@ class FormatParser::JPEGParser
|
|
24
24
|
|
25
25
|
private
|
26
26
|
|
27
|
-
def advance(n)
|
28
|
-
safe_read(@buf, n); nil
|
29
|
-
end
|
30
|
-
|
31
27
|
def read_char
|
32
28
|
safe_read(@buf, 1).unpack('C').first
|
33
29
|
end
|
@@ -68,7 +64,6 @@ class FormatParser::JPEGParser
|
|
68
64
|
nil # Due to the way JPEG is structured it is possible that some invalid inputs will get caught
|
69
65
|
end
|
70
66
|
|
71
|
-
|
72
67
|
# Read a byte, if it is 0xFF then skip bytes as long as they are also 0xFF (byte stuffing)
|
73
68
|
# and return the first byte scanned that is not 0xFF
|
74
69
|
def read_next_marker
|
@@ -85,7 +80,8 @@ class FormatParser::JPEGParser
|
|
85
80
|
size = read_char
|
86
81
|
|
87
82
|
if length == (size * 3) + 8
|
88
|
-
@width
|
83
|
+
@width = width
|
84
|
+
@height = height
|
89
85
|
else
|
90
86
|
raise InvalidStructure
|
91
87
|
end
|
@@ -93,7 +89,7 @@ class FormatParser::JPEGParser
|
|
93
89
|
|
94
90
|
def scan_app1_frame
|
95
91
|
frame = @buf.read(8)
|
96
|
-
if frame.include?(
|
92
|
+
if frame.include?('Exif')
|
97
93
|
scanner = FormatParser::EXIFParser.new(:jpeg, @buf)
|
98
94
|
if scanner.scan_image_exif
|
99
95
|
@exif_output = scanner.exif_data
|
@@ -111,7 +107,7 @@ class FormatParser::JPEGParser
|
|
111
107
|
|
112
108
|
def skip_frame
|
113
109
|
length = read_short - 2
|
114
|
-
|
110
|
+
safe_skip(@buf, length)
|
115
111
|
end
|
116
112
|
|
117
113
|
FormatParser.register_parser_constructor self
|
data/lib/parsers/moov_parser.rb
CHANGED
@@ -7,9 +7,9 @@ class FormatParser::MOOVParser
|
|
7
7
|
# we can reasonably call "file type" (something
|
8
8
|
# usable as a filename extension)
|
9
9
|
FTYP_MAP = {
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
'qt ' => :mov,
|
11
|
+
'mp4 ' => :mp4,
|
12
|
+
'm4a ' => :m4a,
|
13
13
|
}
|
14
14
|
|
15
15
|
natures :video
|
@@ -20,7 +20,7 @@ class FormatParser::MOOVParser
|
|
20
20
|
private_constant :Decoder
|
21
21
|
|
22
22
|
def call(io)
|
23
|
-
return
|
23
|
+
return unless matches_moov_definition?(io)
|
24
24
|
|
25
25
|
# Now we know we are in a MOOV, so go back and parse out the atom structure.
|
26
26
|
# Parsing out the atoms does not read their contents - at least it doesn't
|
@@ -40,7 +40,8 @@ class FormatParser::MOOVParser
|
|
40
40
|
ftyp_atom = decoder.find_first_atom_by_path(atom_tree, 'ftyp')
|
41
41
|
file_type = ftyp_atom.field_value(:major_brand)
|
42
42
|
|
43
|
-
width
|
43
|
+
width = nil
|
44
|
+
height = nil
|
44
45
|
|
45
46
|
# Try to find the width and height in the tkhd
|
46
47
|
if tkhd = decoder.find_first_atom_by_path(atom_tree, 'moov', 'trak', 'tkhd')
|
@@ -50,7 +51,8 @@ class FormatParser::MOOVParser
|
|
50
51
|
|
51
52
|
# Try to find the "topmost" duration (respecting edits)
|
52
53
|
if mdhd = decoder.find_first_atom_by_path(atom_tree, 'moov', 'mvhd')
|
53
|
-
timescale
|
54
|
+
timescale = mdhd.field_value(:tscale)
|
55
|
+
duration = mdhd.field_value(:duration)
|
54
56
|
media_duration_s = duration / timescale.to_f
|
55
57
|
end
|
56
58
|
|
@@ -2,10 +2,9 @@
|
|
2
2
|
# read atoms and parse their data fields if applicable. Also contains
|
3
3
|
# a few utility functions for finding atoms in a list etc.
|
4
4
|
class FormatParser::MOOVParser::Decoder
|
5
|
-
|
6
5
|
class Atom < Struct.new(:at, :atom_size, :atom_type, :path, :children, :atom_fields)
|
7
6
|
def to_s
|
8
|
-
|
7
|
+
'%s (%s): %d bytes at offset %d' % [atom_type, path.join('.'), atom_size, at]
|
9
8
|
end
|
10
9
|
|
11
10
|
def field_value(data_field)
|
@@ -20,10 +19,10 @@ class FormatParser::MOOVParser::Decoder
|
|
20
19
|
end
|
21
20
|
|
22
21
|
# Atoms (boxes) that are known to only contain children, no data fields
|
23
|
-
KNOWN_BRANCH_ATOM_TYPES = %w(
|
22
|
+
KNOWN_BRANCH_ATOM_TYPES = %w(moov mdia trak clip edts minf dinf stbl udta meta)
|
24
23
|
|
25
24
|
# Atoms (boxes) that are known to contain both leaves and data fields
|
26
|
-
KNOWN_BRANCH_AND_LEAF_ATOM_TYPES = %w(
|
25
|
+
KNOWN_BRANCH_AND_LEAF_ATOM_TYPES = %w(meta) # the udta.meta thing used by iTunes
|
27
26
|
|
28
27
|
# Limit how many atoms we scan in sequence, to prevent derailments
|
29
28
|
MAX_ATOMS_AT_LEVEL = 128
|
@@ -32,13 +31,13 @@ class FormatParser::MOOVParser::Decoder
|
|
32
31
|
# matches the type, drilling down if a list of atom names is given
|
33
32
|
def find_first_atom_by_path(atoms, *atom_types)
|
34
33
|
type_to_find = atom_types.shift
|
35
|
-
requisite = atoms.find {|e| e.atom_type == type_to_find }
|
34
|
+
requisite = atoms.find { |e| e.atom_type == type_to_find }
|
36
35
|
|
37
36
|
# Return if we found our match
|
38
37
|
return requisite if atom_types.empty?
|
39
38
|
|
40
39
|
# Return nil if we didn't find the match at this nesting level
|
41
|
-
return
|
40
|
+
return unless requisite
|
42
41
|
|
43
42
|
# ...otherwise drill further down
|
44
43
|
find_first_atom_by_path(requisite.children || [], *atom_types)
|
@@ -50,7 +49,7 @@ class FormatParser::MOOVParser::Decoder
|
|
50
49
|
# numbr of bytes is reserved for the compatible brands, 4 bytes per
|
51
50
|
# brand.
|
52
51
|
num_brands = (atom_size - 8 - 8) / 4
|
53
|
-
|
52
|
+
{
|
54
53
|
major_brand: read_bytes(io, 4),
|
55
54
|
minor_version: read_binary_coded_decimal(io),
|
56
55
|
compatible_brands: (1..num_brands).map { read_bytes(io, 4) },
|
@@ -60,94 +59,88 @@ class FormatParser::MOOVParser::Decoder
|
|
60
59
|
def parse_tkhd_atom(io, _)
|
61
60
|
version = read_byte_value(io)
|
62
61
|
is_v1 = version == 1
|
63
|
-
|
64
|
-
:
|
65
|
-
:
|
66
|
-
:
|
67
|
-
:
|
68
|
-
:
|
69
|
-
:
|
70
|
-
:
|
71
|
-
:
|
72
|
-
:
|
73
|
-
:
|
74
|
-
:
|
75
|
-
:
|
76
|
-
:
|
77
|
-
:
|
78
|
-
:
|
79
|
-
|
80
|
-
repack(tkhd_info_bites)
|
62
|
+
{
|
63
|
+
version: version,
|
64
|
+
flags: read_chars(io, 3),
|
65
|
+
ctime: is_v1 ? read_64bit_uint(io) : read_32bit_uint(io),
|
66
|
+
mtime: is_v1 ? read_64bit_uint(io) : read_32bit_uint(io),
|
67
|
+
trak_id: read_32bit_uint(io),
|
68
|
+
reserved_1: read_chars(io, 4),
|
69
|
+
duration: is_v1 ? read_64bit_uint(io) : read_32bit_uint(io),
|
70
|
+
reserved_2: read_chars(io, 8),
|
71
|
+
layer: read_16bit_uint(io),
|
72
|
+
alternate_group: read_16bit_uint(io),
|
73
|
+
volume: read_16bit_uint(io),
|
74
|
+
reserved_3: read_chars(io, 2),
|
75
|
+
matrix_structure: (1..9).map { read_32bit_fixed_point(io) },
|
76
|
+
track_width: read_32bit_fixed_point(io),
|
77
|
+
track_height: read_32bit_fixed_point(io),
|
78
|
+
}
|
81
79
|
end
|
82
80
|
|
83
81
|
def parse_mdhd_atom(io, _)
|
84
82
|
version = read_byte_value(io)
|
85
83
|
is_v1 = version == 1
|
86
|
-
|
87
|
-
:
|
88
|
-
:
|
89
|
-
:
|
90
|
-
:
|
91
|
-
:
|
92
|
-
:
|
93
|
-
:
|
94
|
-
:
|
95
|
-
|
96
|
-
repack(mdhd_info_bites)
|
84
|
+
{
|
85
|
+
version: version,
|
86
|
+
flags: read_bytes(io, 3),
|
87
|
+
ctime: is_v1 ? read_64bit_uint(io) : read_32bit_uint(io),
|
88
|
+
mtime: is_v1 ? read_64bit_uint(io) : read_32bit_uint(io),
|
89
|
+
tscale: read_32bit_uint(io),
|
90
|
+
duration: is_v1 ? read_64bit_uint(io) : read_32bit_uint(io),
|
91
|
+
language: read_32bit_uint(io),
|
92
|
+
quality: read_32bit_uint(io),
|
93
|
+
}
|
97
94
|
end
|
98
95
|
|
99
96
|
def parse_vmhd_atom(io, _)
|
100
|
-
|
101
|
-
:
|
102
|
-
:
|
103
|
-
:
|
104
|
-
:
|
105
|
-
:
|
106
|
-
:
|
107
|
-
|
108
|
-
repack(vmhd_info_bites)
|
97
|
+
{
|
98
|
+
version: read_byte_value(io),
|
99
|
+
flags: read_bytes(io, 3),
|
100
|
+
graphics_mode: read_bytes(io, 2),
|
101
|
+
opcolor_r: read_32bit_uint(io),
|
102
|
+
opcolor_g: read_32bit_uint(io),
|
103
|
+
opcolor_b: read_32bit_uint(io),
|
104
|
+
}
|
109
105
|
end
|
110
106
|
|
111
107
|
def parse_mvhd_atom(io, _)
|
112
108
|
version = read_byte_value(io)
|
113
109
|
is_v1 = version == 1
|
114
|
-
|
115
|
-
:
|
116
|
-
:
|
117
|
-
:
|
118
|
-
:
|
119
|
-
:
|
120
|
-
:
|
121
|
-
:
|
122
|
-
:
|
123
|
-
:
|
124
|
-
:
|
125
|
-
:
|
126
|
-
:
|
127
|
-
:
|
128
|
-
:
|
129
|
-
:
|
130
|
-
:
|
131
|
-
|
132
|
-
repack(mvhd_info_bites)
|
110
|
+
{
|
111
|
+
version: version,
|
112
|
+
flags: read_bytes(io, 3),
|
113
|
+
ctime: is_v1 ? read_64bit_uint(io) : read_32bit_uint(io),
|
114
|
+
mtime: is_v1 ? read_64bit_uint(io) : read_32bit_uint(io),
|
115
|
+
tscale: read_32bit_uint(io),
|
116
|
+
duration: is_v1 ? read_64bit_uint(io) : read_32bit_uint(io),
|
117
|
+
preferred_rate: read_32bit_uint(io),
|
118
|
+
reserved: read_bytes(io, 10),
|
119
|
+
matrix_structure: (1..9).map { read_32bit_fixed_point(io) },
|
120
|
+
preview_time: read_32bit_uint(io),
|
121
|
+
preview_duration: read_32bit_uint(io),
|
122
|
+
poster_time: read_32bit_uint(io),
|
123
|
+
selection_time: read_32bit_uint(io),
|
124
|
+
selection_duration: read_32bit_uint(io),
|
125
|
+
current_time: read_32bit_uint(io),
|
126
|
+
next_trak_id: read_32bit_uint(io),
|
127
|
+
}
|
133
128
|
end
|
134
129
|
|
135
130
|
def parse_dref_atom(io, _)
|
136
|
-
|
137
|
-
:
|
138
|
-
:
|
139
|
-
:
|
140
|
-
|
141
|
-
dict = repack(dref_info_bites)
|
131
|
+
dict = {
|
132
|
+
version: read_byte_value(io),
|
133
|
+
flags: read_bytes(io, 3),
|
134
|
+
num_entries: read_32bit_uint(io),
|
135
|
+
}
|
142
136
|
num_entries = dict[:num_entries]
|
143
137
|
entries = (1..num_entries).map do
|
144
|
-
|
145
|
-
:
|
146
|
-
:
|
147
|
-
:
|
148
|
-
:
|
149
|
-
|
150
|
-
entry = repack(dref_entry_bites)
|
138
|
+
entry = {
|
139
|
+
size: read_32bit_uint(io),
|
140
|
+
type: read_bytes(io, 4),
|
141
|
+
version: read_bytes(io, 1),
|
142
|
+
flags: read_bytes(io, 3),
|
143
|
+
}
|
151
144
|
entry[:data] = read_bytes(io, entry[:size] - 12)
|
152
145
|
entry
|
153
146
|
end
|
@@ -156,21 +149,19 @@ class FormatParser::MOOVParser::Decoder
|
|
156
149
|
end
|
157
150
|
|
158
151
|
def parse_elst_atom(io, _)
|
159
|
-
|
160
|
-
:
|
161
|
-
:
|
162
|
-
:
|
163
|
-
|
164
|
-
dict = repack(elst_info_bites)
|
152
|
+
dict = {
|
153
|
+
version: read_byte_value(io),
|
154
|
+
flags: read_bytes(io, 3),
|
155
|
+
num_entries: read_32bit_uint(io),
|
156
|
+
}
|
165
157
|
is_v1 = dict[:version] == 1 # Usual is 0, version 1 has 64bit durations
|
166
158
|
num_entries = dict[:num_entries]
|
167
159
|
entries = (1..num_entries).map do
|
168
|
-
|
169
|
-
:
|
170
|
-
:
|
171
|
-
:
|
172
|
-
|
173
|
-
repack(entry_bites)
|
160
|
+
{
|
161
|
+
track_duration: is_v1 ? read_64bit_uint(io) : read_32bit_uint(io),
|
162
|
+
media_time: is_v1 ? read_64bit_uint(io) : read_32bit_uint(io),
|
163
|
+
media_rate: read_32bit_uint(io),
|
164
|
+
}
|
174
165
|
end
|
175
166
|
dict[:entries] = entries
|
176
167
|
dict
|
@@ -178,21 +169,20 @@ class FormatParser::MOOVParser::Decoder
|
|
178
169
|
|
179
170
|
def parse_hdlr_atom(io, atom_size)
|
180
171
|
sub_io = StringIO.new(io.read(atom_size - 8))
|
181
|
-
|
182
|
-
:
|
183
|
-
:
|
184
|
-
:
|
185
|
-
:
|
186
|
-
:
|
187
|
-
:
|
188
|
-
:
|
189
|
-
:
|
190
|
-
|
191
|
-
repack(hdlr_info_bites)
|
172
|
+
{
|
173
|
+
version: read_byte_value(sub_io),
|
174
|
+
flags: read_bytes(sub_io, 3),
|
175
|
+
component_type: read_bytes(sub_io, 4),
|
176
|
+
component_subtype: read_bytes(sub_io, 4),
|
177
|
+
component_manufacturer: read_bytes(sub_io, 4),
|
178
|
+
component_flags: read_bytes(sub_io, 4),
|
179
|
+
component_flags_mask: read_bytes(sub_io, 4),
|
180
|
+
component_name: sub_io.read,
|
181
|
+
}
|
192
182
|
end
|
193
183
|
|
194
184
|
def parse_atom_fields_per_type(io, atom_size, atom_type)
|
195
|
-
if respond_to?("parse_#{atom_type}_atom",
|
185
|
+
if respond_to?("parse_#{atom_type}_atom", true)
|
196
186
|
send("parse_#{atom_type}_atom", io, atom_size)
|
197
187
|
else
|
198
188
|
nil # We can't look inside this leaf atom
|
@@ -208,29 +198,28 @@ class FormatParser::MOOVParser::Decoder
|
|
208
198
|
MAX_ATOMS_AT_LEVEL.times do
|
209
199
|
atom_pos = io.pos
|
210
200
|
|
211
|
-
if atom_pos - initial_pos >= max_read
|
212
|
-
break
|
213
|
-
end
|
201
|
+
break if atom_pos - initial_pos >= max_read
|
214
202
|
|
215
|
-
size_and_type = io.read(4+4)
|
216
|
-
if size_and_type.to_s.bytesize < 8
|
217
|
-
break
|
218
|
-
end
|
203
|
+
size_and_type = io.read(4 + 4)
|
204
|
+
break if size_and_type.to_s.bytesize < 8
|
219
205
|
|
220
206
|
atom_size, atom_type = size_and_type.unpack('Na4')
|
221
207
|
|
222
208
|
# If atom_size is specified to be 1, it is larger than what fits into the
|
223
209
|
# 4 bytes and we need to read it right after the atom type
|
224
|
-
if atom_size == 1
|
225
|
-
|
226
|
-
|
210
|
+
atom_size = read_64bit_uint(io) if atom_size == 1
|
211
|
+
|
212
|
+
# We are allowed to read what comes after
|
213
|
+
# the atom size and atom type, but not any more than that
|
214
|
+
size_of_atom_type_and_size = io.pos - atom_pos
|
215
|
+
atom_size_sans_header = atom_size - size_of_atom_type_and_size
|
227
216
|
|
228
217
|
children, fields = if KNOWN_BRANCH_AND_LEAF_ATOM_TYPES.include?(atom_type)
|
229
|
-
parse_atom_children_and_data_fields(io,
|
218
|
+
parse_atom_children_and_data_fields(io, atom_size_sans_header, atom_type)
|
230
219
|
elsif KNOWN_BRANCH_ATOM_TYPES.include?(atom_type)
|
231
|
-
[extract_atom_stream(io,
|
220
|
+
[extract_atom_stream(io, atom_size_sans_header, current_branch + [atom_type]), nil]
|
232
221
|
else # Assume leaf atom
|
233
|
-
[nil, parse_atom_fields_per_type(io,
|
222
|
+
[nil, parse_atom_fields_per_type(io, atom_size_sans_header, atom_type)]
|
234
223
|
end
|
235
224
|
|
236
225
|
atoms << Atom.new(atom_pos, atom_size, atom_type, current_branch + [atom_type], children, fields)
|
@@ -241,11 +230,11 @@ class FormatParser::MOOVParser::Decoder
|
|
241
230
|
end
|
242
231
|
|
243
232
|
def read_16bit_fixed_point(io)
|
244
|
-
|
233
|
+
_whole, _fraction = io.read(2).unpack('CC')
|
245
234
|
end
|
246
235
|
|
247
236
|
def read_32bit_fixed_point(io)
|
248
|
-
|
237
|
+
_whole, _fraction = io.read(4).unpack('nn')
|
249
238
|
end
|
250
239
|
|
251
240
|
def read_chars(io, n)
|
@@ -274,12 +263,6 @@ class FormatParser::MOOVParser::Decoder
|
|
274
263
|
|
275
264
|
def read_binary_coded_decimal(io)
|
276
265
|
bcd_string = io.read(4)
|
277
|
-
bcd_string.insert(0, '0') if bcd_string.length.odd?
|
278
266
|
[bcd_string].pack('H*').unpack('C*')
|
279
267
|
end
|
280
|
-
|
281
|
-
def repack(properties_to_packspecs)
|
282
|
-
keys, bytes = properties_to_packspecs.partition.with_index { |_, i| i.even? }
|
283
|
-
Hash[keys.zip(bytes)]
|
284
|
-
end
|
285
268
|
end
|