line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Net::DAAP::DMAP; |
2
|
1
|
|
|
1
|
|
24871
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
111
|
|
3
|
|
|
|
|
|
|
our $NOISY = 0; |
4
|
|
|
|
|
|
|
our $VERSION = '1.27'; |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
=pod |
7
|
|
|
|
|
|
|
|
8
|
|
|
|
|
|
|
=head1 NAME |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
Net::DAAP::DMAP - Perl module for reading and writing DAAP structures |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
=head2 SYNOPSIS |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
use Net::DAAP::DMAP qw(:all); |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
$hash_ref = dmap_to_hash_ref($dmap); # crude |
17
|
|
|
|
|
|
|
$array_ref = dmap_to_array_ref($dmap); # crude |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
$array_ref = dmap_unpack($dmap); # knows about data types |
20
|
|
|
|
|
|
|
$node = dmap_seek($array_ref, $path); |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
$flattened = dmap_flatten($array_ref); # convert to path = data formta |
23
|
|
|
|
|
|
|
$flat_list = dmap_flat_list($array_ref); # convert to [ path, data ] format |
24
|
|
|
|
|
|
|
$xml = dmap_to_xml($dmap); # convert to XML fragment |
25
|
|
|
|
|
|
|
$dmap = dmap_pack($dmap); # convert to DMAP packet |
26
|
|
|
|
|
|
|
update_content_codes($unpacked_content_codes_response); |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
=head1 DESCRIPTION |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
=head2 WARNING! |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
Until 2.0, I reserve the right to change the interface. In |
33
|
|
|
|
|
|
|
particular, I think C, C, and |
34
|
|
|
|
|
|
|
C are likely to disappear. And I suspect the hive |
35
|
|
|
|
|
|
|
brain of Perl can come up with a better data structure than I have. |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
=head2 Back to the Description |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
A DMAP structure is a binary record used in Apple's DAAP protocol. A |
40
|
|
|
|
|
|
|
DMAP structure may contain other DMAP structures. Fields in a DMAP |
41
|
|
|
|
|
|
|
structure are identified by a short name ("msdc"). The short name is |
42
|
|
|
|
|
|
|
what's in the binary record, but a content codes list gives a long |
43
|
|
|
|
|
|
|
name ("dmap.databasescount") and a data type for the record (32-bit |
44
|
|
|
|
|
|
|
integer). |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
A parsed DMAP structure is built out of arrays. For example: |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
[ |
49
|
|
|
|
|
|
|
[ |
50
|
|
|
|
|
|
|
'dmap.loginresponse', |
51
|
|
|
|
|
|
|
[ |
52
|
|
|
|
|
|
|
[ |
53
|
|
|
|
|
|
|
'dmap.status', |
54
|
|
|
|
|
|
|
200 |
55
|
|
|
|
|
|
|
], |
56
|
|
|
|
|
|
|
[ |
57
|
|
|
|
|
|
|
'dmap.sessionid', |
58
|
|
|
|
|
|
|
2393 |
59
|
|
|
|
|
|
|
] |
60
|
|
|
|
|
|
|
] |
61
|
|
|
|
|
|
|
] |
62
|
|
|
|
|
|
|
] |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
(C returns this kind of structure) |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
There are two rules here: a field is wrapped in an array, and |
67
|
|
|
|
|
|
|
a container's values are wrapped in an array. So the structure |
68
|
|
|
|
|
|
|
is programmatically built as: |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
$status_field = [ 'dmap.status', 200 ]; |
71
|
|
|
|
|
|
|
$session_id_field = [ 'dmap.sessionid', 2393 ]; |
72
|
|
|
|
|
|
|
$response_value = [ $status_field, $session_id_field ]; |
73
|
|
|
|
|
|
|
$login_response_field = [ 'dmap.loginresponse', $response_value ]; |
74
|
|
|
|
|
|
|
$entire_response = [ $login_response_field ]; |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
The outer array is necessary because not every response has only one |
77
|
|
|
|
|
|
|
top-level container as this does. |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
In XML you'd write the response as: |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
200 |
83
|
|
|
|
|
|
|
2393 |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
This is what C returns. |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
A much more convenient structure for representing this data would |
89
|
|
|
|
|
|
|
be: |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
{ |
92
|
|
|
|
|
|
|
'dmap.loginresponse' => { |
93
|
|
|
|
|
|
|
{ 'dmap.status' => 200, |
94
|
|
|
|
|
|
|
'dmap.sessionid' => 2393, |
95
|
|
|
|
|
|
|
}, |
96
|
|
|
|
|
|
|
} |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
This is the output of C, but beware! This isn't |
99
|
|
|
|
|
|
|
suitable for every response. The hash is indexed by field name and a |
100
|
|
|
|
|
|
|
structure may contain many elements of the same name. For example, |
101
|
|
|
|
|
|
|
requesting the content codes list gives you a list of records that |
102
|
|
|
|
|
|
|
have the field name C. |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
The array structure returned by C is complex, but |
105
|
|
|
|
|
|
|
the C function makes it easier. This takes a structure and |
106
|
|
|
|
|
|
|
a path expressed as a slash-separated list of field names: |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
dmap.loginresponse/dmap.sessionid |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
The return value is the the value of the first C found |
111
|
|
|
|
|
|
|
in the first C structure. In the case of the |
112
|
|
|
|
|
|
|
sample record above, it would be 2393. |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
Another way to handle these complex arrays is to C them. |
115
|
|
|
|
|
|
|
This returns an array of "I = value" lines, where I is |
116
|
|
|
|
|
|
|
a slash-separated path. For example: |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
[ |
119
|
|
|
|
|
|
|
'/dmap.loginresponse/dmap.status = 200', |
120
|
|
|
|
|
|
|
'/dmap.loginresponse/dmap.sessionid = 2393' |
121
|
|
|
|
|
|
|
] |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
You can use C and regexps to find data if that's the way your |
124
|
|
|
|
|
|
|
mind works. |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
C has a similar looking cousin called C, |
127
|
|
|
|
|
|
|
which returns an array of "I => I" pairs. For example: |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
[ |
131
|
|
|
|
|
|
|
'/dmap.loginresponse/dmap.status' => 200, |
132
|
|
|
|
|
|
|
'/dmap.loginresponse/dmap.sessionid' => 2393, |
133
|
|
|
|
|
|
|
] |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
You can then turn this into a hash (which may of course lose you the |
136
|
|
|
|
|
|
|
first elements), or iterate over it in pairs, if that's easier. |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
You can, but don't have to, update the tables of field names ("content |
139
|
|
|
|
|
|
|
codes") and data types. DAAP offers a request that returns a packet |
140
|
|
|
|
|
|
|
of content codes. Feed that packet to C. |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
=head2 Implementation Details |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
It's all implementation details. Here are the various data types. |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
1, 3, 5, 7 = ints, size 8,16,32,64 bit |
147
|
|
|
|
|
|
|
9 = string, 10 = time_t-style time |
148
|
|
|
|
|
|
|
11 = version (two 16-bit ints, I think) |
149
|
|
|
|
|
|
|
12 = container |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
This uses Math::BigInt for 64-bit quantities, as not every platform |
152
|
|
|
|
|
|
|
has 64-bit int support available. |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
There's no support for types 2, 4, 6, 8 yet because nobody'd found |
155
|
|
|
|
|
|
|
examples of them in the field: are they endian changes, or signedness |
156
|
|
|
|
|
|
|
changes. The assumption is that all numbers are unsigned (why allow |
157
|
|
|
|
|
|
|
the possibility of a negative number of songs?). |
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
=head1 AUTHOR |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
Nathan Torkington, . For support, join the |
162
|
|
|
|
|
|
|
DAAP developers mailing list by sending mail to
|
163
|
|
|
|
|
|
|
develooper.com>. |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
Richard Clamp is the current maintainer, and |
166
|
|
|
|
|
|
|
took over in July 2004. |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
=cut |
169
|
|
|
|
|
|
|
|
170
|
1
|
|
|
1
|
|
6
|
use Exporter; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
46
|
|
171
|
1
|
|
|
1
|
|
1730
|
use Math::BigInt; |
|
1
|
|
|
|
|
28103
|
|
|
1
|
|
|
|
|
6
|
|
172
|
1
|
|
|
1
|
|
24781
|
use Carp; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
2134
|
|
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
our @ISA = qw(Exporter); |
175
|
|
|
|
|
|
|
our @EXPORT_OK = qw(dmap_to_hash_ref dmap_to_array_ref update_content_codes |
176
|
|
|
|
|
|
|
dmap_unpack dmap_to_xml dmap_seek dmap_flatten dmap_flat_list dmap_pack ); |
177
|
|
|
|
|
|
|
our %EXPORT_TAGS = ( 'all' => \@EXPORT_OK ); |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
our $Types; |
180
|
|
|
|
|
|
|
my %Type_To_Unpack; |
181
|
|
|
|
|
|
|
my $Container_Type; |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
# initialize the types and their unpack() equivalents |
184
|
|
|
|
|
|
|
init(); |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
sub init { |
187
|
1
|
|
|
1
|
0
|
5
|
local $/; |
188
|
1
|
|
|
|
|
1071
|
$Types = eval ; |
189
|
|
|
|
|
|
|
|
190
|
1
|
|
|
|
|
6
|
$Container_Type = 12; |
191
|
|
|
|
|
|
|
|
192
|
1
|
|
|
|
|
23
|
%Type_To_Unpack = ( |
193
|
|
|
|
|
|
|
1 => 'c', |
194
|
|
|
|
|
|
|
3 => 'n', |
195
|
|
|
|
|
|
|
5 => 'N', |
196
|
|
|
|
|
|
|
7 => 'Q', |
197
|
|
|
|
|
|
|
9 => 'a*', # utf-8 encoded |
198
|
|
|
|
|
|
|
10 => 'N', |
199
|
|
|
|
|
|
|
11 => 'nn', |
200
|
|
|
|
|
|
|
42 => 'a*', # this is a local invention - 9 is |
201
|
|
|
|
|
|
|
# getting handled as utf-8, but for |
202
|
|
|
|
|
|
|
# dpap.picturedata that would be |
203
|
|
|
|
|
|
|
# bad m'kay |
204
|
|
|
|
|
|
|
); |
205
|
|
|
|
|
|
|
} |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
sub dmap_to_hash_ref { |
208
|
0
|
|
|
0
|
0
|
0
|
my $buf = shift; |
209
|
0
|
|
|
|
|
0
|
my %tags; |
210
|
|
|
|
|
|
|
|
211
|
0
|
|
|
|
|
0
|
while (length $buf) { |
212
|
0
|
|
|
|
|
0
|
my ($tag, $len) = unpack("a4N", $buf); |
213
|
0
|
0
|
0
|
|
|
0
|
if (!defined($len) or length $buf < 8+$len) { |
214
|
0
|
|
|
|
|
0
|
return; |
215
|
|
|
|
|
|
|
} |
216
|
0
|
|
|
|
|
0
|
my $data = substr($buf, 8, $len); |
217
|
|
|
|
|
|
|
# try to unpack--if we can, assume it was a container |
218
|
0
|
|
|
|
|
0
|
my $data2 = dmap_to_hash_ref($data); |
219
|
0
|
0
|
|
|
|
0
|
$tags{$tag} = $data2 ? $data2 : $data; |
220
|
0
|
|
|
|
|
0
|
substr($buf, 0, 8+$len) = ''; |
221
|
|
|
|
|
|
|
} |
222
|
0
|
|
|
|
|
0
|
return \%tags; |
223
|
|
|
|
|
|
|
} |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
sub dmap_flatten { |
226
|
0
|
|
|
0
|
0
|
0
|
my $struct = shift; |
227
|
0
|
|
|
|
|
0
|
my $arrayref = []; |
228
|
|
|
|
|
|
|
|
229
|
0
|
|
|
|
|
0
|
flatten_traverse($arrayref, "", $struct); |
230
|
0
|
|
|
|
|
0
|
return $arrayref; |
231
|
|
|
|
|
|
|
} |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
sub flatten_traverse { |
234
|
0
|
|
|
0
|
0
|
0
|
my ($array_ref, $prefix, $struct) = @_; |
235
|
|
|
|
|
|
|
|
236
|
0
|
|
|
|
|
0
|
foreach my $ref (@$struct) { |
237
|
0
|
|
|
|
|
0
|
for (my $i=0; $i < @$ref; $i+=2) { |
238
|
0
|
|
|
|
|
0
|
my ($tag, $data) = ($ref->[$i], $ref->[$i+1]); |
239
|
|
|
|
|
|
|
|
240
|
0
|
0
|
|
|
|
0
|
if (ref $data eq 'ARRAY') { |
241
|
0
|
|
|
|
|
0
|
flatten_traverse($array_ref, "$prefix/$tag", $data); |
242
|
|
|
|
|
|
|
} else { |
243
|
0
|
|
|
|
|
0
|
push @$array_ref, "$prefix/$tag = $data"; |
244
|
|
|
|
|
|
|
} |
245
|
|
|
|
|
|
|
} |
246
|
|
|
|
|
|
|
} |
247
|
|
|
|
|
|
|
} |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
sub dmap_flat_list { |
250
|
0
|
|
|
0
|
0
|
0
|
return @{ flat_list_traverse([], "", shift) }; |
|
0
|
|
|
|
|
0
|
|
251
|
|
|
|
|
|
|
} |
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
sub flat_list_traverse { |
254
|
0
|
|
|
0
|
0
|
0
|
my ($list, $prefix, $struct) = @_; |
255
|
0
|
|
|
|
|
0
|
foreach my $ref (@$struct) { |
256
|
0
|
|
|
|
|
0
|
for (my $i=0; $i < @$ref; $i+=2) { |
257
|
0
|
|
|
|
|
0
|
my ($tag, $data) = ($ref->[$i], $ref->[$i+1]); |
258
|
|
|
|
|
|
|
|
259
|
0
|
0
|
|
|
|
0
|
if (ref $data eq 'ARRAY') { |
260
|
0
|
|
|
|
|
0
|
flat_list_traverse($list, "$prefix/$tag", $data); |
261
|
|
|
|
|
|
|
} else { |
262
|
0
|
|
|
|
|
0
|
push @$list, "$prefix/$tag", $data; |
263
|
|
|
|
|
|
|
} |
264
|
|
|
|
|
|
|
} |
265
|
|
|
|
|
|
|
} |
266
|
0
|
|
|
|
|
0
|
return $list; |
267
|
|
|
|
|
|
|
} |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
sub dmap_unpack { |
271
|
120
|
|
|
120
|
0
|
15704
|
my $buf = shift; |
272
|
120
|
|
|
|
|
146
|
my @tags; |
273
|
|
|
|
|
|
|
|
274
|
120
|
|
|
|
|
254
|
while (length $buf) { |
275
|
487
|
|
|
|
|
1413
|
my ($tag, $len) = unpack("a4N", $buf); |
276
|
487
|
|
|
|
|
1030
|
my $data = substr($buf, 8, $len); |
277
|
487
|
|
|
|
|
733
|
substr($buf, 0, 8+$len) = ''; |
278
|
487
|
|
|
|
|
904
|
my $type = $Types->{$tag}{TYPE}; |
279
|
487
|
100
|
|
|
|
1137
|
unless ($type) { |
280
|
1
|
50
|
|
|
|
5
|
carp "'$tag' unknown, can't unpack" if $NOISY; |
281
|
1
|
|
|
|
|
4
|
next; |
282
|
|
|
|
|
|
|
} |
283
|
|
|
|
|
|
|
|
284
|
486
|
100
|
|
|
|
964
|
if ($type == 12) { |
|
|
100
|
|
|
|
|
|
285
|
113
|
|
|
|
|
212
|
$data = dmap_unpack($data); |
286
|
|
|
|
|
|
|
} elsif ($type == 7) { |
287
|
9
|
|
|
|
|
22
|
my ($n1, $n2) = unpack("N2", $data); |
288
|
9
|
|
|
|
|
50
|
$data = new Math::BigInt(new Math::BigInt($n1)->blsft(32)); |
289
|
9
|
|
|
|
|
2877
|
$data += $n2; |
290
|
9
|
|
|
|
|
1743
|
$data = "$data"; |
291
|
|
|
|
|
|
|
} else { |
292
|
364
|
|
|
|
|
948
|
$data = unpack($Type_To_Unpack{$type}, $data); |
293
|
|
|
|
|
|
|
} |
294
|
|
|
|
|
|
|
# type 9 is really utf-8 encoded, so if we can, show that it is |
295
|
486
|
100
|
66
|
|
|
2276
|
if ($type == 9 && eval { require Encode; 1 }) { |
|
207
|
|
|
|
|
2194
|
|
|
207
|
|
|
|
|
15213
|
|
296
|
207
|
|
|
|
|
517
|
$data = Encode::decode('utf-8', $data); |
297
|
|
|
|
|
|
|
} |
298
|
486
|
|
|
|
|
10597
|
push @tags, [ $Types->{$tag}{NAME}, $data ]; |
299
|
|
|
|
|
|
|
} |
300
|
|
|
|
|
|
|
|
301
|
120
|
|
|
|
|
463
|
return \@tags; |
302
|
|
|
|
|
|
|
} |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
sub dmap_to_xml { |
305
|
0
|
|
|
0
|
0
|
0
|
my $buf = shift; |
306
|
0
|
|
|
|
|
0
|
my $xml = ''; |
307
|
|
|
|
|
|
|
|
308
|
0
|
|
|
|
|
0
|
while (length $buf) { |
309
|
0
|
|
|
|
|
0
|
my ($tag, $len) = unpack("a4N", $buf); |
310
|
0
|
|
|
|
|
0
|
my $data = substr($buf, 8, $len); |
311
|
0
|
|
|
|
|
0
|
my $type = $Types->{$tag}{TYPE}; |
312
|
|
|
|
|
|
|
|
313
|
0
|
0
|
|
|
|
0
|
if ($type == 12) { |
314
|
0
|
|
|
|
|
0
|
$data = dmap_to_xml($data); |
315
|
|
|
|
|
|
|
} else { |
316
|
0
|
|
|
|
|
0
|
$data = unpack($Type_To_Unpack{$type}, $data); |
317
|
|
|
|
|
|
|
} |
318
|
0
|
|
|
|
|
0
|
$xml .= sprintf("<%s>\n %s\n%s>\n", $tag, $data, $tag); |
319
|
0
|
|
|
|
|
0
|
substr($buf, 0, 8+$len) = ''; |
320
|
|
|
|
|
|
|
} |
321
|
0
|
|
|
|
|
0
|
return $xml; |
322
|
|
|
|
|
|
|
} |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
sub dmap_to_array_ref { |
325
|
0
|
|
|
0
|
0
|
0
|
my $buf = shift; |
326
|
0
|
|
|
|
|
0
|
my @tags; |
327
|
|
|
|
|
|
|
|
328
|
0
|
|
|
|
|
0
|
while (length $buf) { |
329
|
0
|
|
|
|
|
0
|
my ($tag, $len) = unpack("a4N", $buf); |
330
|
0
|
0
|
0
|
|
|
0
|
if (!defined($len) or length $buf < 8+$len) { |
331
|
0
|
|
|
|
|
0
|
return; |
332
|
|
|
|
|
|
|
} |
333
|
0
|
|
|
|
|
0
|
my $data = substr($buf, 8, $len); |
334
|
|
|
|
|
|
|
# try to unpack, assume it was a container if it succeeded |
335
|
0
|
|
|
|
|
0
|
my $data2 = dmap_to_array_ref($data); |
336
|
0
|
0
|
|
|
|
0
|
push @tags, [ $tag, $data2 ? $data2 : $data ]; |
337
|
0
|
|
|
|
|
0
|
substr($buf, 0, 8+$len) = ''; |
338
|
|
|
|
|
|
|
} |
339
|
0
|
|
|
|
|
0
|
return \@tags; |
340
|
|
|
|
|
|
|
} |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
sub dmap_seek { |
343
|
0
|
|
|
0
|
0
|
0
|
my($struct, $to_find) = @_; |
344
|
|
|
|
|
|
|
|
345
|
0
|
|
0
|
|
|
0
|
CHUNK: while (defined($to_find) && length($to_find)) { |
346
|
0
|
|
|
|
|
0
|
my $top; |
347
|
0
|
|
|
|
|
0
|
($top, $to_find) = split m{/}, $to_find, 2; |
348
|
|
|
|
|
|
|
|
349
|
0
|
|
|
|
|
0
|
ELEMENT: foreach my $elt (@$struct) { |
350
|
|
|
|
|
|
|
|
351
|
0
|
0
|
|
|
|
0
|
if ($elt->[0] eq $top) { |
352
|
0
|
|
|
|
|
0
|
$struct = $elt->[1]; |
353
|
0
|
|
|
|
|
0
|
next CHUNK; |
354
|
|
|
|
|
|
|
} |
355
|
|
|
|
|
|
|
} |
356
|
0
|
|
|
|
|
0
|
return; # NOT FOUND |
357
|
|
|
|
|
|
|
} |
358
|
0
|
|
|
|
|
0
|
return $struct; |
359
|
|
|
|
|
|
|
} |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
sub update_content_codes { |
362
|
0
|
|
|
0
|
0
|
0
|
my $array = shift; |
363
|
0
|
|
|
|
|
0
|
my $short; |
364
|
|
|
|
|
|
|
|
365
|
0
|
|
|
|
|
0
|
my $mccr = dmap_seek($array, "dmap.contentcodesresponse"); |
366
|
0
|
0
|
|
|
|
0
|
die "Couldn't find mccr" unless defined $mccr; |
367
|
|
|
|
|
|
|
|
368
|
0
|
|
|
|
|
0
|
foreach my $mdcl_rec (@$mccr) { |
369
|
0
|
0
|
|
|
|
0
|
next unless $mdcl_rec->[0] eq 'dmap.dictionary'; |
370
|
0
|
|
|
|
|
0
|
my @fields = @{$mdcl_rec->[1]}; |
|
0
|
|
|
|
|
0
|
|
371
|
0
|
|
|
|
|
0
|
my ($name, $id, $type); |
372
|
0
|
|
|
|
|
0
|
foreach my $f (@fields) { |
373
|
0
|
0
|
|
|
|
0
|
if ($f->[0] eq 'dmap.contentcodesnumber') { $id = $f->[1] } |
|
0
|
|
|
|
|
0
|
|
374
|
0
|
0
|
|
|
|
0
|
if ($f->[0] eq 'dmap.contentcodesname') { $name = $f->[1] } |
|
0
|
|
|
|
|
0
|
|
375
|
0
|
0
|
|
|
|
0
|
if ($f->[0] eq 'dmap.contentcodestype') { $type = $f->[1] } |
|
0
|
|
|
|
|
0
|
|
376
|
|
|
|
|
|
|
} |
377
|
0
|
0
|
|
|
|
0
|
if ($id eq 'mcnm') { $type = 9 } # string names please |
|
0
|
|
|
|
|
0
|
|
378
|
0
|
0
|
|
|
|
0
|
if ($id eq 'pfdt') { $type = 42 } # and straight binary pictures |
|
0
|
|
|
|
|
0
|
|
379
|
0
|
|
|
|
|
0
|
my $record = { NAME => $name, ID => $id, TYPE => $type }; |
380
|
0
|
|
|
|
|
0
|
$short->{$id} = $record; |
381
|
|
|
|
|
|
|
} |
382
|
|
|
|
|
|
|
|
383
|
0
|
|
|
|
|
0
|
$Types = $short; |
384
|
|
|
|
|
|
|
} |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
sub dmap_pack { |
387
|
120
|
|
|
120
|
0
|
171
|
my $struct = shift; |
388
|
120
|
|
|
|
|
130
|
my $out = ''; |
389
|
|
|
|
|
|
|
|
390
|
120
|
100
|
|
|
|
768
|
my %by_name = map { %{$_} ? ( $_->{NAME} => $_ ) : () } values %$Types; |
|
10684
|
|
|
|
|
14916
|
|
|
10684
|
|
|
|
|
39103
|
|
391
|
120
|
|
|
|
|
1060
|
for my $pair (@$struct) { |
392
|
486
|
|
|
|
|
897
|
my ($name, $value) = @$pair; |
393
|
|
|
|
|
|
|
# dmap_unpack doesn't populate the name when its decoded |
394
|
|
|
|
|
|
|
# something it doesn't know the content-code of, like aeSV |
395
|
|
|
|
|
|
|
# which is new to 4.5 |
396
|
486
|
50
|
|
|
|
858
|
unless ($name) { |
397
|
0
|
0
|
|
|
|
0
|
carp "element without a name - skipping" if $NOISY; |
398
|
0
|
|
|
|
|
0
|
next; |
399
|
|
|
|
|
|
|
} |
400
|
|
|
|
|
|
|
# or, it may be we don't know what kind of thing this is |
401
|
486
|
50
|
|
|
|
1036
|
unless ($by_name{ $name }) { |
402
|
0
|
0
|
|
|
|
0
|
carp "$name has unknown type - skipping" if $NOISY; |
403
|
0
|
|
|
|
|
0
|
next; |
404
|
|
|
|
|
|
|
} |
405
|
|
|
|
|
|
|
|
406
|
486
|
|
|
|
|
897
|
my $tag = $by_name{ $name }{ID}; |
407
|
486
|
|
|
|
|
711
|
my $type = $by_name{ $name }{TYPE}; |
408
|
|
|
|
|
|
|
#print "$name => $tag $type $Type_To_Unpack{$type}\n"; |
409
|
|
|
|
|
|
|
#$SIG{__WARN__} = sub { die @_ }; |
410
|
486
|
100
|
66
|
|
|
1643
|
if ($type == 9 && eval { require Encode; 1 }) { |
|
207
|
|
|
|
|
1627
|
|
|
207
|
|
|
|
|
920
|
|
411
|
207
|
|
|
|
|
1086
|
$value = Encode::encode('utf-8', $value); |
412
|
|
|
|
|
|
|
} |
413
|
486
|
100
|
|
|
|
7863
|
if ($type == 12) { # container |
|
|
100
|
|
|
|
|
|
414
|
113
|
|
|
|
|
198
|
$value = dmap_pack( $value ); |
415
|
|
|
|
|
|
|
} |
416
|
|
|
|
|
|
|
elsif ($type == 7) { # 64-bit |
417
|
9
|
|
|
|
|
40
|
my $high = Math::BigInt->new( $value )->brsft(32).""; |
418
|
9
|
|
|
|
|
3718
|
my $low = Math::BigInt->new( $value )->band(0xFFFFFFFF).""; |
419
|
9
|
|
|
|
|
3192
|
$value = pack( "N2", $high, $low ); |
420
|
|
|
|
|
|
|
} |
421
|
|
|
|
|
|
|
else { |
422
|
1
|
|
|
1
|
|
8
|
no warnings 'uninitialized'; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
70
|
|
423
|
364
|
|
|
|
|
1022
|
$value = pack( $Type_To_Unpack{$type}, $value ); |
424
|
|
|
|
|
|
|
} |
425
|
1
|
|
|
1
|
|
1153
|
my $length = do { use bytes; length $value }; |
|
1
|
|
|
|
|
10
|
|
|
1
|
|
|
|
|
7
|
|
|
486
|
|
|
|
|
669
|
|
|
486
|
|
|
|
|
1984
|
|
426
|
486
|
|
|
|
|
2134
|
$out .= $tag . pack("N", $length) . $value; |
427
|
|
|
|
|
|
|
} |
428
|
120
|
|
|
|
|
1635
|
return $out; |
429
|
|
|
|
|
|
|
} |
430
|
|
|
|
|
|
|
|
431
|
|
|
|
|
|
|
1; |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
__DATA__ |