format_parser 0.2.0 → 0.3.0
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/.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
|