depix 1.0.3 → 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +5 -1
- data/Manifest.txt +4 -0
- data/README.txt +18 -4
- data/bin/depix-describe +14 -3
- data/lib/depix.rb +25 -76
- data/lib/depix/benchmark.rb +15 -0
- data/lib/depix/dict.rb +127 -2
- data/lib/depix/editor.rb +32 -0
- data/lib/depix/reader.rb +83 -0
- data/lib/depix/structs.rb +16 -7
- data/test/test_depix.rb +39 -2
- data/test/test_dict.rb +210 -19
- metadata +8 -4
data/History.txt
CHANGED
data/Manifest.txt
CHANGED
@@ -7,9 +7,13 @@ bin/depix-describe
|
|
7
7
|
lib/depix.rb
|
8
8
|
lib/depix/struct_explainer.rb
|
9
9
|
lib/depix/structs.rb
|
10
|
+
lib/depix/benchmark.rb
|
10
11
|
lib/depix/compact_structs.rb
|
11
12
|
lib/depix/enums.rb
|
12
13
|
lib/depix/dict.rb
|
14
|
+
lib/depix/reader.rb
|
15
|
+
lib/depix/editor.rb
|
16
|
+
test/test_dict.rb
|
13
17
|
test/test_depix.rb
|
14
18
|
test/samples/E012_P001_L000002_lin.0001.dpx
|
15
19
|
test/samples/E012_P001_L000002_lin.0002.dpx
|
data/README.txt
CHANGED
@@ -4,12 +4,22 @@
|
|
4
4
|
|
5
5
|
== DESCRIPTION:
|
6
6
|
|
7
|
-
Read DPX file metadata
|
7
|
+
Read and write DPX file metadata
|
8
8
|
|
9
9
|
== SYNOPSIS:
|
10
10
|
|
11
|
+
Reading headers
|
12
|
+
|
11
13
|
meta = Depix.from_file(dpx_file_path)
|
12
14
|
puts meta.time_code #=> 10:00:00:02
|
15
|
+
|
16
|
+
Writing headers
|
17
|
+
|
18
|
+
editor = Depix::Editor.new(dpx_file_path)
|
19
|
+
|
20
|
+
# Advance the time code by one frame and save
|
21
|
+
editor.headers.time_code = editor.headers.time_code + 1
|
22
|
+
editor.commit!
|
13
23
|
|
14
24
|
The data returned is described in the DPX_HEADER_STRUCTURE[link:files/DPX_HEADER_STRUCTURE_txt.html]. It's
|
15
25
|
a vanilla Ruby object with no extra methods except for the readers that have the same name as the specified
|
@@ -19,11 +29,15 @@ The gem also contains an executable called depix-desribe which can be used from
|
|
19
29
|
|
20
30
|
$book depix-describe 001_PTAPE_001.001.dpx
|
21
31
|
|
22
|
-
|
32
|
+
for a long description or
|
33
|
+
|
34
|
+
$book depix-describe -s 001_PTAPE_001.001.dpx
|
23
35
|
|
24
|
-
|
36
|
+
for a short description
|
37
|
+
|
38
|
+
== NOTES:
|
25
39
|
|
26
|
-
Autodesk IFFS systems write the reel name for the file to the orientation.device field
|
40
|
+
Autodesk IFFS systems write the reel name for the file to the orientation.device field, some scanners write it into user data.
|
27
41
|
|
28
42
|
== REQUIREMENTS:
|
29
43
|
|
data/bin/depix-describe
CHANGED
@@ -10,13 +10,24 @@ OptionParser.new do |opts|
|
|
10
10
|
opts.on("-c", "--compact", "Compact output (only fields that change per frame)") do |v|
|
11
11
|
options[:compact] = true
|
12
12
|
end
|
13
|
+
|
14
|
+
opts.on("-s", "--synthetics", "Output only synthetic fields (like time code and aspect)") do |v|
|
15
|
+
options[:synthetics] = true
|
16
|
+
end
|
17
|
+
|
13
18
|
end.parse!
|
14
19
|
|
15
20
|
ARGV.each do | file |
|
16
|
-
puts "Describing DPX #{file}. Empty elements are omitted
|
17
|
-
puts "===================================================\n
|
21
|
+
puts "Describing DPX #{file}. Empty elements are omitted."
|
22
|
+
puts "===================================================\n"
|
18
23
|
begin
|
19
|
-
|
24
|
+
if options[:synthetics]
|
25
|
+
puts Depix.describe_brief(file)
|
26
|
+
elsif options[:compact]
|
27
|
+
puts Depix.describe_file(file, true)
|
28
|
+
else
|
29
|
+
puts Depix.describe_file(file)
|
30
|
+
end
|
20
31
|
rescue Depix::InvalidHeader
|
21
32
|
puts " - Invalid header data"
|
22
33
|
end
|
data/lib/depix.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'stringio'
|
2
1
|
require 'rubygems'
|
3
2
|
require 'timecode'
|
4
3
|
|
@@ -6,14 +5,18 @@ require File.dirname(__FILE__) + '/depix/dict'
|
|
6
5
|
require File.dirname(__FILE__) + '/depix/structs'
|
7
6
|
require File.dirname(__FILE__) + '/depix/compact_structs'
|
8
7
|
require File.dirname(__FILE__) + '/depix/enums'
|
8
|
+
require File.dirname(__FILE__) + '/depix/reader'
|
9
|
+
require File.dirname(__FILE__) + '/depix/editor'
|
10
|
+
|
9
11
|
|
10
12
|
module Depix
|
11
|
-
VERSION = '1.0.
|
13
|
+
VERSION = '1.0.4'
|
12
14
|
|
13
15
|
class InvalidHeader < RuntimeError; end
|
14
16
|
|
15
17
|
# Offers convenience access to a few common attributes bypassing the piecemeal structs
|
16
18
|
module Synthetics
|
19
|
+
|
17
20
|
def keycode
|
18
21
|
[film.id, film.type, film.offset, film.prefix, film.count].compact.join(' ')
|
19
22
|
end
|
@@ -21,13 +24,23 @@ module Depix
|
|
21
24
|
# Return the flame reel name. The data after the first null byte is not meant to be seen and is used by Flame internally
|
22
25
|
# as it seems
|
23
26
|
def flame_reel
|
24
|
-
orientation.device.split(
|
27
|
+
orientation.device.split(0x00.chr).shift
|
28
|
+
end
|
29
|
+
|
30
|
+
# Assign reel name
|
31
|
+
def flame_reel=(new_reel)
|
32
|
+
orientation.device = new_reel
|
25
33
|
end
|
26
34
|
|
27
35
|
def time_code
|
28
36
|
Timecode.from_uint(television.time_code) #, film.frame_rate)
|
29
37
|
end
|
30
38
|
|
39
|
+
# Assign frame rate and timecode from a Timecode object
|
40
|
+
def time_code=(new_tc)
|
41
|
+
television.time_code, film.frame_rate = new_tc.to_uint, new_tc.fps
|
42
|
+
end
|
43
|
+
|
31
44
|
# Get the name of the transfer function (Linear, Logarithmic, ...)
|
32
45
|
def colorimetric
|
33
46
|
COLORIMETRIC.invert[image.image_elements[0].colorimetric]
|
@@ -38,6 +51,11 @@ module Depix
|
|
38
51
|
COMPONENT_TYPE.invert[image.image_elements[0].descriptor]
|
39
52
|
end
|
40
53
|
|
54
|
+
# Aspect in it's traditional repr
|
55
|
+
def aspect
|
56
|
+
"%.2f" % (orientation.aspect_ratio[0].to_f / orientation.aspect_ratio[1].to_f)
|
57
|
+
end
|
58
|
+
|
41
59
|
# Is this DPX file little-endian? This would be an exception, but still useful
|
42
60
|
def le?
|
43
61
|
file.magic == 'XPDS'
|
@@ -65,78 +83,9 @@ module Depix
|
|
65
83
|
Reader.new.describe_file(path, compact)
|
66
84
|
end
|
67
85
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
def describe_file(path, compact = false)
|
72
|
-
header = File.open(path, 'r') { |f| f.read(DPX.length) }
|
73
|
-
describe_struct(parse(header, false))
|
74
|
-
end
|
75
|
-
|
76
|
-
def from_file(path, compact)
|
77
|
-
header = File.open(path, 'r') { |f| f.read(DPX.length) }
|
78
|
-
begin
|
79
|
-
parse(header, compact)
|
80
|
-
rescue InvalidHeader => e
|
81
|
-
raise InvalidHeader, "Invalid header in file #{path}"
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
# The hear of Depix
|
86
|
-
def parse(data, compact)
|
87
|
-
magic = data[0..3]
|
88
|
-
|
89
|
-
raise InvalidHeader unless %w( SDPX XPDS).include?(magic)
|
90
|
-
|
91
|
-
struct = compact ? CompactDPX : DPX
|
92
|
-
|
93
|
-
is_be = (magic == "SDPX")
|
94
|
-
version_check = FileInfo.only(:magic, :version)
|
95
|
-
|
96
|
-
result = begin
|
97
|
-
if is_be
|
98
|
-
version_check.consume!(data.unpack(version_check.pattern))
|
99
|
-
else
|
100
|
-
version_check.consume!(data.unpack(make_le(version_check.pattern)))
|
101
|
-
end
|
102
|
-
rescue ArgumentError
|
103
|
-
raise InvalidHeader
|
104
|
-
end
|
105
|
-
|
106
|
-
raise InvalidHeader unless result.version == "V1.0"
|
107
|
-
|
108
|
-
template = is_be ? DPX.pattern : make_le(DPX.pattern)
|
109
|
-
struct.consume!(data.unpack(struct.pattern))
|
110
|
-
end
|
111
|
-
|
112
|
-
# Describe a filled DPX structure
|
113
|
-
def describe_struct(result, pad_offset = 0)
|
114
|
-
result.class.fields.inject([]) do | info, field |
|
115
|
-
value = result.send(field.name)
|
116
|
-
parts = []
|
117
|
-
if value
|
118
|
-
parts << field.desc if field.desc
|
119
|
-
parts << if field.is_a?(InnerField)
|
120
|
-
describe_struct(value, pad_offset + 1)
|
121
|
-
elsif field.is_a?(ArrayField)
|
122
|
-
# Exception for image elements
|
123
|
-
value = result.image_elements[0...result.number_elements] if field.name == :image_elements
|
124
|
-
value.map { | v | v.is_a?(Dict) ? describe_struct(v, pad_offset + 2) : v }
|
125
|
-
else
|
126
|
-
value
|
127
|
-
end
|
128
|
-
end
|
129
|
-
if parts.any?
|
130
|
-
info << parts.join(' ')
|
131
|
-
end
|
132
|
-
info
|
133
|
-
end.map{|e| (' ' * pad_offset) + e }.join("\n")
|
134
|
-
end
|
135
|
-
|
136
|
-
# Convert an unpack pattern to LE
|
137
|
-
def make_le(pattern)
|
138
|
-
pattern.gsub(/n/, "v").gsub(/N/, "V").gsub(/g/, "f")
|
139
|
-
end
|
140
|
-
|
86
|
+
# Return a formatted description of the DPX file at path, showing only synthetic attributes
|
87
|
+
def self.describe_brief(path)
|
88
|
+
Reader.new.describe_synthetics_of_struct(from_file(path))
|
141
89
|
end
|
90
|
+
|
142
91
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../depix'
|
2
|
+
|
3
|
+
require 'benchmark'
|
4
|
+
|
5
|
+
iter = 10000
|
6
|
+
|
7
|
+
puts "Reading DPX header #{iter} times, all data"
|
8
|
+
puts Benchmark.measure {
|
9
|
+
iter.times { Depix.from_file(File.dirname(__FILE__)+"/../../test/samples/026_FROM_HERO_TAPE_5-3-1_MOV.0029.dpx", false) }
|
10
|
+
}
|
11
|
+
|
12
|
+
puts "Reading DPX header #{iter} times, compact data"
|
13
|
+
puts Benchmark.measure {
|
14
|
+
iter.times { Depix.from_file(File.dirname(__FILE__)+"/../../test/samples/026_FROM_HERO_TAPE_5-3-1_MOV.0029.dpx", true) }
|
15
|
+
}
|
data/lib/depix/dict.rb
CHANGED
@@ -50,6 +50,26 @@ module Depix
|
|
50
50
|
def consume!(stack)
|
51
51
|
clean(stack.shift)
|
52
52
|
end
|
53
|
+
|
54
|
+
# Check that the passed value:
|
55
|
+
# a) Matches the Ruby type expected
|
56
|
+
# b) Fits into the slot
|
57
|
+
# c) Does not overflow
|
58
|
+
# When the validation fails should raise
|
59
|
+
def validate!(value)
|
60
|
+
raise "#{name} value required, but got nil in #{name}".strip if value.nil? && req?
|
61
|
+
raise "Value expected to be #{rtype} but was #{value.class}" if !value.nil? && rtype && !value.is_a?(rtype)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Pack a value passed into a string
|
65
|
+
def pack(value)
|
66
|
+
raise "No pattern defined for #{self}" unless pattern
|
67
|
+
if value.nil?
|
68
|
+
[self.class.const_get(:BLANK)].pack(pattern)
|
69
|
+
else
|
70
|
+
[value].pack(pattern)
|
71
|
+
end
|
72
|
+
end
|
53
73
|
end
|
54
74
|
|
55
75
|
class U32Field < Field
|
@@ -67,6 +87,14 @@ module Depix
|
|
67
87
|
def clean(value)
|
68
88
|
value == BLANK ? nil : value
|
69
89
|
end
|
90
|
+
|
91
|
+
# Override - might be Bignum although cast to Integer sometimes
|
92
|
+
def validate!(value)
|
93
|
+
raise "#{name} value required, but got nil".strip if value.nil? && req?
|
94
|
+
raise "#{name} value expected to be #{rtype} but was #{value.class}" if !value.nil? && (!value.is_a?(Integer) && !value.is_a?(Bignum))
|
95
|
+
raise "#{name} value #{value} overflows" if !value.nil? && (value < 0 || value >= BLANK)
|
96
|
+
end
|
97
|
+
|
70
98
|
end
|
71
99
|
|
72
100
|
class U8Field < Field
|
@@ -87,7 +115,12 @@ module Depix
|
|
87
115
|
end
|
88
116
|
|
89
117
|
def clean(v)
|
90
|
-
v == BLANK ? nil : v
|
118
|
+
(v == BLANK || v == -1) ? nil : v
|
119
|
+
end
|
120
|
+
|
121
|
+
def validate!(value)
|
122
|
+
super(value)
|
123
|
+
raise "#{name} value #{value} out of bounds for 8 bit unsigned int".lstrip if (!value.nil? && (value < 0 || value >= BLANK))
|
91
124
|
end
|
92
125
|
end
|
93
126
|
|
@@ -101,6 +134,10 @@ module Depix
|
|
101
134
|
def consume(stack)
|
102
135
|
nil
|
103
136
|
end
|
137
|
+
|
138
|
+
def pack(data)
|
139
|
+
raise "This is a filler, it cannot be reconstructed from a value"
|
140
|
+
end
|
104
141
|
end
|
105
142
|
|
106
143
|
class U16Field < Field
|
@@ -122,10 +159,16 @@ module Depix
|
|
122
159
|
def clean(v)
|
123
160
|
v == BLANK ? nil : v
|
124
161
|
end
|
162
|
+
|
163
|
+
def validate!(value)
|
164
|
+
super(value)
|
165
|
+
raise "#{name} value #{value} out of bounds for 16bit unsigned int" if (value < 0 || value >= BLANK)
|
166
|
+
end
|
125
167
|
end
|
126
168
|
|
127
169
|
class R32Field < Field
|
128
170
|
undef :length=, :pattern=
|
171
|
+
BLANK = 0xFFFFFFFF
|
129
172
|
|
130
173
|
def pattern
|
131
174
|
"g"
|
@@ -169,6 +212,15 @@ module Depix
|
|
169
212
|
def rtype
|
170
213
|
String
|
171
214
|
end
|
215
|
+
|
216
|
+
def validate!(value)
|
217
|
+
super(value)
|
218
|
+
raise "#{value} overflows the #{length} bytes allocated" if !value.nil? && value.length > length
|
219
|
+
end
|
220
|
+
|
221
|
+
def pack(value)
|
222
|
+
value.ljust(length, "\000") rescue ("\000" * length)
|
223
|
+
end
|
172
224
|
end
|
173
225
|
|
174
226
|
# Wrapper for an array structure
|
@@ -198,6 +250,27 @@ module Depix
|
|
198
250
|
r = (req? ? "- required" : nil)
|
199
251
|
[tpl, desc, r].compact.join(' ')
|
200
252
|
end
|
253
|
+
|
254
|
+
def validate!(array)
|
255
|
+
raise "This value would overflow, #{array.length} elements passed but only #{members.length} fit" unless array.length <= members.length
|
256
|
+
raise "This value is required, but the array is empty" if req? && array.empty?
|
257
|
+
array.zip(members).map do | v, m |
|
258
|
+
m.validate!(v) unless (v.nil? && !m.req?)
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
def pack(values)
|
263
|
+
# For members that are present, get values. For members that are missing, fill with null bytes upto length.
|
264
|
+
# For values that are nil, skip packing
|
265
|
+
members.zip(values).map do |m, v|
|
266
|
+
if !m.req? && v.nil?
|
267
|
+
raise "#{m} needs to provide length" unless m.length
|
268
|
+
"\377" * m.length
|
269
|
+
else
|
270
|
+
v.respond_to?(:pack) ? v.pack : m.pack(v)
|
271
|
+
end
|
272
|
+
end.join
|
273
|
+
end
|
201
274
|
end
|
202
275
|
|
203
276
|
# Wrapper for a contained structure
|
@@ -220,6 +293,15 @@ module Depix
|
|
220
293
|
def rtype
|
221
294
|
cast
|
222
295
|
end
|
296
|
+
|
297
|
+
def validate!(value)
|
298
|
+
super(value)
|
299
|
+
cast.validate!(value) if cast.respond_to?(:validate!) && (!value.nil? || req?)
|
300
|
+
end
|
301
|
+
|
302
|
+
def pack(value)
|
303
|
+
cast.pack(value)
|
304
|
+
end
|
223
305
|
end
|
224
306
|
|
225
307
|
# Base class for a struct. Could also be implemented as a module actually
|
@@ -232,7 +314,14 @@ module Depix
|
|
232
314
|
def fields
|
233
315
|
@fields ||= []
|
234
316
|
end
|
235
|
-
|
317
|
+
|
318
|
+
# Validate a passed instance
|
319
|
+
def validate!(instance)
|
320
|
+
fields.each do | f |
|
321
|
+
f.validate!(instance.send(f.name)) if f.name
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
236
325
|
# Define a 4-byte unsigned integer
|
237
326
|
def u32(name, *extras)
|
238
327
|
count, opts = count_and_opts_from(extras)
|
@@ -273,6 +362,7 @@ module Depix
|
|
273
362
|
else
|
274
363
|
[Field.send("emit_#{mapped_to}")] * count
|
275
364
|
end
|
365
|
+
yield a.members if block_given?
|
276
366
|
fields << a
|
277
367
|
end
|
278
368
|
|
@@ -343,6 +433,41 @@ module Depix
|
|
343
433
|
only([])
|
344
434
|
end
|
345
435
|
|
436
|
+
# Pack the instance of this struct
|
437
|
+
def pack(instance, buffer = nil)
|
438
|
+
|
439
|
+
# Preallocate a buffer just as big as me since we want everything to remain at fixed offsets
|
440
|
+
buffer ||= ("\000" * length)
|
441
|
+
|
442
|
+
# If the instance is nil return pure padding
|
443
|
+
return buffer if instance.nil?
|
444
|
+
|
445
|
+
# Now for the important stuff. For each field that we have, replace a piece at offsets in the buffer
|
446
|
+
# with the packed results, skipping fillers
|
447
|
+
fields.each_with_index do | f, i |
|
448
|
+
|
449
|
+
# Skip blanking, we just dont touch it. TODO - test!
|
450
|
+
next if f.is_a?(Filler)
|
451
|
+
|
452
|
+
# Where should we put that value?
|
453
|
+
offset = fields[0...i].inject(0){|_, s| _ + s.length }
|
454
|
+
|
455
|
+
val = instance.send(f.name)
|
456
|
+
|
457
|
+
# Validate the passed value using the format the field supports
|
458
|
+
f.validate!(val)
|
459
|
+
|
460
|
+
packed = f.pack(val)
|
461
|
+
|
462
|
+
# Signal offset violation
|
463
|
+
raise "Improper length for #{f.name} - packed #{packed.length} bytes but #{f.length} is required to fill the slot" if packed.length != f.length
|
464
|
+
|
465
|
+
buffer[offset...(offset+f.length)] = packed
|
466
|
+
end
|
467
|
+
raise "Resulting buffer not the same length, expected #{length} bytes but compued #{buffer.length}" if buffer.length != length
|
468
|
+
buffer
|
469
|
+
end
|
470
|
+
|
346
471
|
private
|
347
472
|
|
348
473
|
# extract_options! on a diet
|
data/lib/depix/editor.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
module Depix
|
2
|
+
# Used to edit DPX headers. Create an Editor object and pass the path to the file to it. Change the headers variable to contain the edited
|
3
|
+
# DPX headers and call commit!. Note that the DPX header will be overwritten in place - if you want to save another version you need to manage it yourself
|
4
|
+
class Editor
|
5
|
+
|
6
|
+
# Stores the path to file
|
7
|
+
attr_reader :path
|
8
|
+
|
9
|
+
# Stores the Depix::DPX object with headers
|
10
|
+
attr_accessor :headers
|
11
|
+
|
12
|
+
# Create a new editor for the file at path
|
13
|
+
def initialize(file_path)
|
14
|
+
@path = file_path
|
15
|
+
@headers = Depix.from_file(@path)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Save the headers to file at path, overwriting the old ones
|
19
|
+
def commit!
|
20
|
+
raise "No headers" unless @headers
|
21
|
+
raise "Cannot pack LE headers" if @headers.le?
|
22
|
+
packed = @headers.class.pack(@headers)
|
23
|
+
|
24
|
+
# Validate that we can unpack first - what if something went wrong?
|
25
|
+
Depix::Reader.new.parse(packed, false)
|
26
|
+
|
27
|
+
File.open(@path, 'rb+') do | f |
|
28
|
+
f.seek(0, IO::SEEK_SET); f.write(packed)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/depix/reader.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
module Depix
|
2
|
+
class Reader
|
3
|
+
|
4
|
+
# Returns a printable report on all the headers present in the file at the path passed
|
5
|
+
def describe_file(path, compact = false)
|
6
|
+
header = File.open(path, 'r') { |f| f.read(DPX.length) }
|
7
|
+
struct = parse(header, false)
|
8
|
+
describe_struct(struct) + describe_synthetics_of_struct(struct)
|
9
|
+
end
|
10
|
+
|
11
|
+
def describe_synthetics_of_struct(struct)
|
12
|
+
Synthetics.instance_methods.reject{|m| m.include?('=')}.map do | m |
|
13
|
+
[m, struct.send(m)].join(' : ')
|
14
|
+
end.unshift("============").unshift("\nSynthetic properties").join("\n")
|
15
|
+
end
|
16
|
+
|
17
|
+
def from_file(path, compact)
|
18
|
+
header = File.open(path, 'r') { |f| f.read(DPX.length) }
|
19
|
+
begin
|
20
|
+
parse(header, compact)
|
21
|
+
rescue InvalidHeader => e
|
22
|
+
raise InvalidHeader, "Invalid header in file #{path} - #{e.message}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# The hear of Depix
|
27
|
+
def parse(data, compact)
|
28
|
+
magic = data[0..3]
|
29
|
+
|
30
|
+
raise InvalidHeader, "No magic bytes found at start" unless %w( SDPX XPDS).include?(magic)
|
31
|
+
|
32
|
+
struct = compact ? CompactDPX : DPX
|
33
|
+
|
34
|
+
is_be = (magic == "SDPX")
|
35
|
+
version_check = FileInfo.only(:magic, :version)
|
36
|
+
|
37
|
+
result = begin
|
38
|
+
if is_be
|
39
|
+
version_check.consume!(data.unpack(version_check.pattern))
|
40
|
+
else
|
41
|
+
version_check.consume!(data.unpack(make_le(version_check.pattern)))
|
42
|
+
end
|
43
|
+
rescue ArgumentError
|
44
|
+
raise InvalidHeader
|
45
|
+
end
|
46
|
+
|
47
|
+
raise InvalidHeader, "Unknown version tag #{result.version}" unless result.version == "V1.0"
|
48
|
+
|
49
|
+
template = is_be ? DPX.pattern : make_le(DPX.pattern)
|
50
|
+
struct.consume!(data.unpack(struct.pattern))
|
51
|
+
end
|
52
|
+
|
53
|
+
# Describe a filled DPX structure
|
54
|
+
def describe_struct(result, pad_offset = 0)
|
55
|
+
result.class.fields.inject([]) do | info, field |
|
56
|
+
value = result.send(field.name)
|
57
|
+
parts = []
|
58
|
+
if value
|
59
|
+
parts << field.desc if field.desc
|
60
|
+
parts << if field.is_a?(InnerField)
|
61
|
+
describe_struct(value, pad_offset + 1)
|
62
|
+
elsif field.is_a?(ArrayField)
|
63
|
+
# Exception for image elements
|
64
|
+
value = result.image_elements[0...result.number_elements] if field.name == :image_elements
|
65
|
+
value.map { | v | v.is_a?(Dict) ? describe_struct(v, pad_offset + 2) : v }
|
66
|
+
else
|
67
|
+
value
|
68
|
+
end
|
69
|
+
end
|
70
|
+
if parts.any?
|
71
|
+
info << parts.join(' ')
|
72
|
+
end
|
73
|
+
info
|
74
|
+
end.map{|e| (' ' * pad_offset) + e }.join("\n")
|
75
|
+
end
|
76
|
+
|
77
|
+
# Convert an unpack pattern to LE
|
78
|
+
def make_le(pattern)
|
79
|
+
pattern.gsub(/n/, "v").gsub(/N/, "V").gsub(/g/, "f")
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
data/lib/depix/structs.rb
CHANGED
@@ -121,17 +121,26 @@ module Depix
|
|
121
121
|
|
122
122
|
u32 :pixels_per_line, :desc => 'Pixels per horizontal line', :req => true
|
123
123
|
u32 :lines_per_element, :desc => 'Line count', :req => true
|
124
|
-
|
124
|
+
|
125
|
+
array :image_elements, ImageElement, 8, :desc => "Image elements" do | elements |
|
126
|
+
elements[0].req = true
|
127
|
+
end
|
128
|
+
|
125
129
|
char :reserve, 52
|
130
|
+
|
131
|
+
# Only expose the elements present
|
132
|
+
def image_elements #:nodoc:
|
133
|
+
@image_elements[0...number_elements]
|
134
|
+
end
|
126
135
|
end
|
127
136
|
|
128
137
|
#:include:DPX_HEADER_STRUCTURE.txt
|
129
138
|
class DPX < Dict
|
130
|
-
inner :file, FileInfo, :desc => "File information"
|
131
|
-
inner :image, ImageInfo, :desc => "Image information"
|
132
|
-
inner :orientation, OrientationInfo, :desc => "Orientation"
|
133
|
-
inner :film, FilmInfo, :desc => "Film industry info"
|
134
|
-
inner :television, TelevisionInfo, :desc => "TV industry info"
|
135
|
-
inner :user, UserInfo, :desc => "User info"
|
139
|
+
inner :file, FileInfo, :desc => "File information", :req => true
|
140
|
+
inner :image, ImageInfo, :desc => "Image information", :req => true
|
141
|
+
inner :orientation, OrientationInfo, :desc => "Orientation", :req => true
|
142
|
+
inner :film, FilmInfo, :desc => "Film industry info", :req => true
|
143
|
+
inner :television, TelevisionInfo, :desc => "TV industry info", :req => true
|
144
|
+
inner :user, UserInfo, :desc => "User info", :req => true
|
136
145
|
end
|
137
146
|
end
|
data/test/test_depix.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/../lib/depix'
|
2
2
|
require 'test/unit'
|
3
3
|
|
4
|
+
SAMPLE_DPX = File.dirname(__FILE__) + '/samples/E012_P001_L000002_lin.0001.dpx'
|
5
|
+
|
4
6
|
class ReaderTest < Test::Unit::TestCase
|
5
7
|
|
6
|
-
SAMPLE_DPX = File.dirname(__FILE__) + '/samples/E012_P001_L000002_lin.0001.dpx'
|
7
|
-
|
8
8
|
def test_parsed_properly
|
9
9
|
file = SAMPLE_DPX
|
10
10
|
parsed = Depix.from_file(file)
|
@@ -69,6 +69,7 @@ class ReaderTest < Test::Unit::TestCase
|
|
69
69
|
assert_equal :RGB, parsed.component_type
|
70
70
|
assert_equal :Linear, parsed.colorimetric
|
71
71
|
assert_equal "E012", parsed.flame_reel
|
72
|
+
assert_equal "1.33", parsed.aspect
|
72
73
|
end
|
73
74
|
|
74
75
|
def test_parsed_properly_using_compact_structs
|
@@ -84,6 +85,17 @@ class ReaderTest < Test::Unit::TestCase
|
|
84
85
|
end
|
85
86
|
end
|
86
87
|
|
88
|
+
def test_packing
|
89
|
+
original_header = File.read(SAMPLE_DPX)[0...Depix::DPX.length]
|
90
|
+
|
91
|
+
assert_nothing_raised do
|
92
|
+
dpx = Depix.from_string(original_header)
|
93
|
+
packed = Depix::DPX.pack(dpx, original_header.dup)
|
94
|
+
|
95
|
+
dpx2 = Depix.from_string(packed)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
87
99
|
def test_parsing_something_else_should_raise
|
88
100
|
s = "Mary had a little lamb"
|
89
101
|
assert_raise(Depix::InvalidHeader) { Depix.from_string(s) }
|
@@ -95,4 +107,29 @@ class ReaderTest < Test::Unit::TestCase
|
|
95
107
|
assert_raise(Depix::InvalidHeader) { Depix.from_string(s) }
|
96
108
|
|
97
109
|
end
|
110
|
+
end
|
111
|
+
|
112
|
+
class EditorTest < Test::Unit::TestCase
|
113
|
+
def test_instantiation
|
114
|
+
e = Depix::Editor.new(SAMPLE_DPX)
|
115
|
+
assert_not_nil e
|
116
|
+
assert_equal SAMPLE_DPX, e.path
|
117
|
+
assert_not_nil e.headers
|
118
|
+
end
|
119
|
+
|
120
|
+
def test_commit
|
121
|
+
temp_path = SAMPLE_DPX + ".test"
|
122
|
+
begin
|
123
|
+
FileUtils.cp(SAMPLE_DPX, temp_path)
|
124
|
+
e = Depix::Editor.new(temp_path)
|
125
|
+
e.headers.orientation.device = "E013"
|
126
|
+
|
127
|
+
assert_nothing_raised { e.commit! }
|
128
|
+
|
129
|
+
re_read = Depix.from_file(temp_path)
|
130
|
+
assert_equal "E013", re_read.orientation.device
|
131
|
+
ensure
|
132
|
+
File.unlink(temp_path)
|
133
|
+
end
|
134
|
+
end
|
98
135
|
end
|
data/test/test_dict.rb
CHANGED
@@ -3,6 +3,28 @@ require 'test/unit'
|
|
3
3
|
|
4
4
|
include Depix
|
5
5
|
|
6
|
+
class BogusError < RuntimeError; end
|
7
|
+
|
8
|
+
class AlwaysInvalidField < Field
|
9
|
+
def validate!(value)
|
10
|
+
raise BogusError, "Never valid"
|
11
|
+
end
|
12
|
+
|
13
|
+
def pack(some_value)
|
14
|
+
raise BogusError, "Will not pack"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class AlwaysInvalidStruct
|
19
|
+
def self.validate!(value)
|
20
|
+
raise BogusError, "Never valid"
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.pack(instance)
|
24
|
+
raise BogusError, "Will not pack"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
6
28
|
module FieldConformity
|
7
29
|
def conform_field!(f)
|
8
30
|
assert_respond_to f, :name
|
@@ -12,6 +34,8 @@ module FieldConformity
|
|
12
34
|
assert_respond_to f, :req?
|
13
35
|
assert_respond_to f, :rtype
|
14
36
|
assert_respond_to f, :explain
|
37
|
+
assert_respond_to f, :validate!
|
38
|
+
assert_respond_to f, :pack
|
15
39
|
end
|
16
40
|
|
17
41
|
def assert_method_removed(f, method)
|
@@ -99,24 +123,24 @@ class TestField < Test::Unit::TestCase
|
|
99
123
|
assert_equal [2,3], ar
|
100
124
|
end
|
101
125
|
|
102
|
-
def
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
f = InnerField.new :cast => catcher
|
110
|
-
assert_respond_to f, :consume!
|
126
|
+
def test_validate
|
127
|
+
f = Field.new
|
128
|
+
f.rtype = self.class
|
129
|
+
assert_nothing_raised { f.validate! nil }
|
111
130
|
|
112
|
-
assert_raise(RuntimeError) { f.
|
131
|
+
assert_raise(RuntimeError) { f.validate! "boo" }
|
132
|
+
assert_nothing_raised { f.validate! self }
|
113
133
|
end
|
114
134
|
|
115
|
-
def
|
116
|
-
f =
|
117
|
-
|
118
|
-
|
119
|
-
|
135
|
+
def test_validate_if_required
|
136
|
+
f = Field.new
|
137
|
+
f.req = true
|
138
|
+
assert_raise(RuntimeError) { f.validate! nil }
|
139
|
+
end
|
140
|
+
|
141
|
+
def test_pack_raises
|
142
|
+
f = Field.new
|
143
|
+
assert_raise(RuntimeError) { f.pack("foo")}
|
120
144
|
end
|
121
145
|
end
|
122
146
|
|
@@ -159,6 +183,59 @@ class TestArrayField < Test::Unit::TestCase
|
|
159
183
|
assert_equal 3, f.length
|
160
184
|
assert_equal "CC2", f.pattern
|
161
185
|
end
|
186
|
+
|
187
|
+
def test_consume
|
188
|
+
f = ArrayField.new :members => [Field.new, Field.new]
|
189
|
+
assert_respond_to f, :consume!
|
190
|
+
|
191
|
+
assert_equal [1,2], f.consume!([1,2])
|
192
|
+
end
|
193
|
+
|
194
|
+
def test_validate
|
195
|
+
f = ArrayField.new :members => [Field.new(:rtype => self.class)]
|
196
|
+
|
197
|
+
# Overflow
|
198
|
+
assert_raise(RuntimeError) { f.validate!([nil, nil]) }
|
199
|
+
|
200
|
+
# Just empty
|
201
|
+
assert_nothing_raised { f.validate!([]) }
|
202
|
+
|
203
|
+
# Nil vaue
|
204
|
+
assert_nothing_raised { f.validate!([nil]) }
|
205
|
+
|
206
|
+
# type cast
|
207
|
+
assert_raise(RuntimeError) { f.validate!(["nil"]) }
|
208
|
+
|
209
|
+
assert_nothing_raised { f.validate!([self]) }
|
210
|
+
end
|
211
|
+
|
212
|
+
def test_validate_fails_with_empty_array_and_required_field
|
213
|
+
f = ArrayField.new :members => [Field.new(:rtype => self.class)], :req => true
|
214
|
+
assert_raise(RuntimeError) { f.validate!([]) }
|
215
|
+
end
|
216
|
+
|
217
|
+
def test_validate_does_not_validate_inner_structure_if_no_value_present_and_field_is_not_required
|
218
|
+
f = ArrayField.new :members => [AlwaysInvalidField.new]
|
219
|
+
assert_nothing_raised { f.validate! [nil] }
|
220
|
+
end
|
221
|
+
|
222
|
+
|
223
|
+
def test_pack_tries_to_pack_inner_structures
|
224
|
+
f = ArrayField.new :members => [AlwaysInvalidField.new]
|
225
|
+
assert_raise(BogusError) { f.pack([1, 2]) }
|
226
|
+
end
|
227
|
+
|
228
|
+
def test_pack_pads_properly
|
229
|
+
f = ArrayField.new :members => [U32Field.new, R32Field.new, R32Field.new]
|
230
|
+
assert_equal "\000\000\000\001@\000\000\000\377\377\377\377", f.pack([1.0, 2.0])
|
231
|
+
assert_equal f.length, f.pack([1.0, 2.0]).length
|
232
|
+
end
|
233
|
+
|
234
|
+
def test_does_not_try_to_pack_nil_values
|
235
|
+
f = ArrayField.new(:members => [AlwaysInvalidField.new(:length => 2)])
|
236
|
+
assert_equal "\377\377", f.pack([])
|
237
|
+
end
|
238
|
+
|
162
239
|
end
|
163
240
|
|
164
241
|
class TestInnerField < Test::Unit::TestCase
|
@@ -194,6 +271,34 @@ class TestInnerField < Test::Unit::TestCase
|
|
194
271
|
casted = InnerField.new(:cast => c)
|
195
272
|
assert_equal c, casted.rtype
|
196
273
|
end
|
274
|
+
|
275
|
+
def test_consume
|
276
|
+
catcher = Class.new do
|
277
|
+
def self.consume!(arg)
|
278
|
+
raise RuntimeError if arg == ["julik"]
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
f = InnerField.new :cast => catcher
|
283
|
+
assert_respond_to f, :consume!
|
284
|
+
|
285
|
+
assert_raise(RuntimeError) { f.consume!(["julik"]) }
|
286
|
+
end
|
287
|
+
|
288
|
+
def test_validate_with_nil_and_no_requirement
|
289
|
+
f = InnerField.new :cast => AlwaysInvalidStruct, :req => true
|
290
|
+
assert_raise(RuntimeError) { f.validate!(nil) }
|
291
|
+
end
|
292
|
+
|
293
|
+
def test_validate
|
294
|
+
f = InnerField.new :cast => AlwaysInvalidStruct
|
295
|
+
assert_raise(BogusError) { f.validate!(AlwaysInvalidStruct.new) }
|
296
|
+
end
|
297
|
+
|
298
|
+
def test_pack_tries_to_pack_inner_structures
|
299
|
+
f = InnerField.new :cast => AlwaysInvalidStruct
|
300
|
+
assert_raise(BogusError) { f.pack(AlwaysInvalidStruct.new) }
|
301
|
+
end
|
197
302
|
end
|
198
303
|
|
199
304
|
class TestWideIntField < Test::Unit::TestCase
|
@@ -209,6 +314,23 @@ class TestWideIntField < Test::Unit::TestCase
|
|
209
314
|
assert_equal 66, f.clean(66)
|
210
315
|
assert_equal nil, f.clean(0xFFFFFFFF)
|
211
316
|
end
|
317
|
+
|
318
|
+
def test_validate
|
319
|
+
f = U32Field.new
|
320
|
+
|
321
|
+
assert_nothing_raised { f.validate! 8 }
|
322
|
+
assert_nothing_raised { f.validate! 0 }
|
323
|
+
assert_nothing_raised { f.validate! 65536 }
|
324
|
+
assert_raise(RuntimeError) { f.validate!(0xFFFFFFFF) }
|
325
|
+
assert_nothing_raised { f.validate!(0xFFFFFFFF - 1) }
|
326
|
+
|
327
|
+
end
|
328
|
+
|
329
|
+
def test_pack
|
330
|
+
w = U32Field.new
|
331
|
+
assert_equal "\000\000\000\036", w.pack(30)
|
332
|
+
assert_equal "\377\377\377\377", w.pack(nil)
|
333
|
+
end
|
212
334
|
end
|
213
335
|
|
214
336
|
class TestCharField < Test::Unit::TestCase
|
@@ -240,7 +362,25 @@ class TestCharField < Test::Unit::TestCase
|
|
240
362
|
assert_equal nil, f.clean("\0\0\0\0\0\0")
|
241
363
|
assert_equal nil, f.clean("\0\0\0\377\377\0\0\0")
|
242
364
|
assert_equal "foo\0foo", f.clean("\0\0foo\0foo\0")
|
243
|
-
|
365
|
+
end
|
366
|
+
|
367
|
+
def test_char_field_validates_overflow
|
368
|
+
f = CharField.new :length => 2
|
369
|
+
assert_raise(RuntimeError) { f.validate!("xxx")}
|
370
|
+
assert_nothing_raised { f.validate!("xx")}
|
371
|
+
assert_nothing_raised { f.validate!(nil)}
|
372
|
+
end
|
373
|
+
|
374
|
+
def test_char_field_validates_required_with_nil
|
375
|
+
f = CharField.new :length => 2, :req => true
|
376
|
+
assert_raise(RuntimeError) { f.validate!(nil)}
|
377
|
+
end
|
378
|
+
|
379
|
+
def test_char_field_packs
|
380
|
+
f = CharField.new(:length => 6)
|
381
|
+
assert_equal "xx\000\000\000\000", f.pack("xx")
|
382
|
+
assert_equal "\000\000\000\000\000\000", f.pack("")
|
383
|
+
assert_equal "\000\000\000\000\000\000", f.pack(nil)
|
244
384
|
end
|
245
385
|
end
|
246
386
|
|
@@ -261,6 +401,12 @@ class TestFloatField < Test::Unit::TestCase
|
|
261
401
|
assert_equal Float, f.rtype
|
262
402
|
assert_equal nil, f.clean(the_nan)
|
263
403
|
end
|
404
|
+
|
405
|
+
def test_pack
|
406
|
+
w = R32Field.new
|
407
|
+
assert_equal "@fff", w.pack(3.6)
|
408
|
+
assert_equal "O\200\000\000", w.pack(nil)
|
409
|
+
end
|
264
410
|
end
|
265
411
|
|
266
412
|
class TestSmallintField < Test::Unit::TestCase
|
@@ -272,7 +418,6 @@ class TestSmallintField < Test::Unit::TestCase
|
|
272
418
|
|
273
419
|
assert_method_removed(f, :pattern=)
|
274
420
|
assert_method_removed(f, :length=)
|
275
|
-
|
276
421
|
end
|
277
422
|
|
278
423
|
def test_smallint_operation
|
@@ -285,11 +430,18 @@ class TestSmallintField < Test::Unit::TestCase
|
|
285
430
|
|
286
431
|
def test_smallint_clean
|
287
432
|
f = U8Field.new
|
288
|
-
|
289
433
|
assert_equal nil, f.clean(0xFF)
|
290
434
|
assert_equal 10, f.clean(10)
|
291
435
|
end
|
292
436
|
|
437
|
+
def test_validate
|
438
|
+
f = U8Field.new
|
439
|
+
assert_nothing_raised { f.validate! 8 }
|
440
|
+
assert_nothing_raised { f.validate! 0 }
|
441
|
+
assert_raise(RuntimeError) { f.validate!( -1 ) }
|
442
|
+
assert_raise(RuntimeError) { f.validate!( 255) }
|
443
|
+
assert_raise(RuntimeError) { f.validate!( 256) }
|
444
|
+
end
|
293
445
|
end
|
294
446
|
|
295
447
|
class TestDoubleField < Test::Unit::TestCase
|
@@ -317,6 +469,15 @@ class TestDoubleField < Test::Unit::TestCase
|
|
317
469
|
assert_equal nil, f.clean(0xFFFF)
|
318
470
|
assert_equal 10, f.clean(10)
|
319
471
|
end
|
472
|
+
|
473
|
+
def test_validate
|
474
|
+
f = U16Field.new
|
475
|
+
assert_nothing_raised { f.validate! 8 }
|
476
|
+
assert_nothing_raised { f.validate! 0 }
|
477
|
+
assert_raise(RuntimeError) { f.validate!( -1) }
|
478
|
+
assert_raise(RuntimeError) { f.validate!( 65535) }
|
479
|
+
assert_raise(RuntimeError) { f.validate!( 65536) }
|
480
|
+
end
|
320
481
|
end
|
321
482
|
|
322
483
|
class TestFillerField < Test::Unit::TestCase
|
@@ -344,7 +505,10 @@ class TestFillerField < Test::Unit::TestCase
|
|
344
505
|
data.freeze
|
345
506
|
assert_nothing_raised { Filler.new(:length => 1).consume(data) }
|
346
507
|
end
|
347
|
-
|
508
|
+
|
509
|
+
def test_pack_raises
|
510
|
+
assert_raise(RuntimeError) { Filler.new(:length => 3).pack('xxx') }
|
511
|
+
end
|
348
512
|
end
|
349
513
|
|
350
514
|
class TestFieldEmit < Test::Unit::TestCase
|
@@ -401,6 +565,12 @@ class TestDict < Test::Unit::TestCase
|
|
401
565
|
assert_equal [], dict_class.fields
|
402
566
|
end
|
403
567
|
|
568
|
+
def test_dict_responds_to_validate
|
569
|
+
dict_class = Class.new(Dict)
|
570
|
+
assert_respond_to dict_class, :validate!
|
571
|
+
one = dict_class.new
|
572
|
+
end
|
573
|
+
|
404
574
|
def test_dict_fields_array_not_class_shared
|
405
575
|
d1, d2 = (0..1).map{|_| Class.new(Dict) }
|
406
576
|
|
@@ -434,7 +604,27 @@ class TestDict < Test::Unit::TestCase
|
|
434
604
|
assert_equal 'A1A1', c.pattern
|
435
605
|
assert_equal 2, c.length
|
436
606
|
end
|
607
|
+
|
608
|
+
def test_dict_does_not_validate_inner_nil
|
609
|
+
wrapper_class = Class.new(Dict) do
|
610
|
+
u32 :bigint
|
611
|
+
inner :invalid, AlwaysInvalidStruct
|
612
|
+
end
|
613
|
+
struct = wrapper_class.new
|
614
|
+
assert_nothing_raised { wrapper_class.validate!(struct) }
|
615
|
+
end
|
437
616
|
|
617
|
+
def test_dict_calls_validate
|
618
|
+
wrapper_class = Class.new(Dict) do
|
619
|
+
u32 :bigint
|
620
|
+
inner :invalid, AlwaysInvalidStruct, :req => true
|
621
|
+
end
|
622
|
+
|
623
|
+
struct = wrapper_class.new
|
624
|
+
struct.invalid = AlwaysInvalidStruct.new
|
625
|
+
|
626
|
+
assert_raise(BogusError) { wrapper_class.validate!(struct) }
|
627
|
+
end
|
438
628
|
end
|
439
629
|
|
440
630
|
class TestDictConsume < Test::Unit::TestCase
|
@@ -449,6 +639,7 @@ class TestDictConsume < Test::Unit::TestCase
|
|
449
639
|
assert_equal "a", result.foo
|
450
640
|
assert_equal "b", result.bar
|
451
641
|
end
|
642
|
+
|
452
643
|
end
|
453
644
|
|
454
645
|
class TestDictEmitDSL < Test::Unit::TestCase
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: depix
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Julik Tarkhanov
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-12-
|
12
|
+
date: 2008-12-26 00:00:00 +01:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -22,7 +22,7 @@ dependencies:
|
|
22
22
|
- !ruby/object:Gem::Version
|
23
23
|
version: 1.8.2
|
24
24
|
version:
|
25
|
-
description: Read DPX file metadata
|
25
|
+
description: Read and write DPX file metadata
|
26
26
|
email:
|
27
27
|
- me@julik.nl
|
28
28
|
executables:
|
@@ -44,9 +44,13 @@ files:
|
|
44
44
|
- lib/depix.rb
|
45
45
|
- lib/depix/struct_explainer.rb
|
46
46
|
- lib/depix/structs.rb
|
47
|
+
- lib/depix/benchmark.rb
|
47
48
|
- lib/depix/compact_structs.rb
|
48
49
|
- lib/depix/enums.rb
|
49
50
|
- lib/depix/dict.rb
|
51
|
+
- lib/depix/reader.rb
|
52
|
+
- lib/depix/editor.rb
|
53
|
+
- test/test_dict.rb
|
50
54
|
- test/test_depix.rb
|
51
55
|
- test/samples/E012_P001_L000002_lin.0001.dpx
|
52
56
|
- test/samples/E012_P001_L000002_lin.0002.dpx
|
@@ -78,7 +82,7 @@ rubyforge_project: wiretap
|
|
78
82
|
rubygems_version: 1.3.1
|
79
83
|
signing_key:
|
80
84
|
specification_version: 2
|
81
|
-
summary: Read DPX file metadata
|
85
|
+
summary: Read and write DPX file metadata
|
82
86
|
test_files:
|
83
87
|
- test/test_depix.rb
|
84
88
|
- test/test_dict.rb
|