CFPropertyList 2.0.14 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +19 -0
- data/README.md +79 -0
- data/{README → README.rdoc} +8 -1
- data/THANKS +7 -0
- data/lib/{rbBinaryCFPropertyList.rb → cfpropertylist/rbBinaryCFPropertyList.rb} +223 -228
- data/lib/{rbCFPlistError.rb → cfpropertylist/rbCFPlistError.rb} +1 -1
- data/lib/{rbCFPropertyList.rb → cfpropertylist/rbCFPropertyList.rb} +157 -59
- data/lib/{rbCFTypes.rb → cfpropertylist/rbCFTypes.rb} +158 -50
- data/lib/{rbXMLCFPropertyList.rb → cfpropertylist/rbLibXMLParser.rb} +36 -12
- data/lib/cfpropertylist/rbNokogiriParser.rb +151 -0
- data/lib/cfpropertylist/rbPlainCFPropertyList.rb +199 -0
- data/lib/cfpropertylist/rbREXMLParser.rb +148 -0
- data/lib/cfpropertylist.rb +2 -2
- metadata +52 -59
@@ -1,5 +1,7 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
|
3
|
+
require 'stringio'
|
4
|
+
|
3
5
|
module CFPropertyList
|
4
6
|
# Binary PList parser class
|
5
7
|
class Binary
|
@@ -16,7 +18,7 @@ module CFPropertyList
|
|
16
18
|
@offsets = []
|
17
19
|
|
18
20
|
fd = nil
|
19
|
-
if(opts.has_key?(:file))
|
21
|
+
if(opts.has_key?(:file))
|
20
22
|
fd = File.open(opts[:file],"rb")
|
21
23
|
file = opts[:file]
|
22
24
|
else
|
@@ -38,17 +40,20 @@ module CFPropertyList
|
|
38
40
|
@count_objects = number_of_objects
|
39
41
|
|
40
42
|
# decode offset table
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
43
|
+
if(offset_size != 3)
|
44
|
+
formats = ["","C*","n*","","N*"]
|
45
|
+
@offsets = coded_offset_table.unpack(formats[offset_size])
|
46
|
+
else
|
47
|
+
@offsets = coded_offset_table.unpack("C*").each_slice(3).map {
|
48
|
+
|x,y,z| (x << 16) | (y << 8) | z
|
49
|
+
}
|
45
50
|
end
|
46
51
|
|
47
52
|
@object_ref_size = object_ref_size
|
48
53
|
val = read_binary_object_at(file,fd,top_object)
|
49
54
|
|
50
55
|
fd.close
|
51
|
-
|
56
|
+
val
|
52
57
|
end
|
53
58
|
|
54
59
|
|
@@ -60,19 +65,21 @@ module CFPropertyList
|
|
60
65
|
|
61
66
|
@written_object_count = 0
|
62
67
|
@object_table = []
|
63
|
-
@object_ref_size = 0
|
64
68
|
|
65
69
|
@offsets = []
|
66
70
|
|
67
71
|
binary_str = "bplist00"
|
68
72
|
|
69
73
|
@object_refs = count_object_refs(opts[:root])
|
70
|
-
@object_ref_size = Binary.bytes_needed(@object_refs)
|
71
74
|
|
72
75
|
opts[:root].to_binary(self)
|
73
76
|
|
74
77
|
next_offset = 8
|
75
|
-
offsets = @object_table.
|
78
|
+
offsets = @object_table.map do |object|
|
79
|
+
offset = next_offset
|
80
|
+
next_offset += object.bytesize
|
81
|
+
offset
|
82
|
+
end
|
76
83
|
binary_str << @object_table.join
|
77
84
|
|
78
85
|
table_offset = next_offset
|
@@ -89,60 +96,54 @@ module CFPropertyList
|
|
89
96
|
end
|
90
97
|
end
|
91
98
|
|
92
|
-
binary_str << [offset_size, @
|
99
|
+
binary_str << [offset_size, object_ref_size(@object_refs)].pack("x6CC")
|
93
100
|
binary_str << [@object_table.size].pack("x4N")
|
94
101
|
binary_str << [0].pack("x4N")
|
95
102
|
binary_str << [table_offset].pack("x4N")
|
96
103
|
|
97
|
-
|
104
|
+
binary_str
|
105
|
+
end
|
106
|
+
|
107
|
+
def object_ref_size object_refs
|
108
|
+
Binary.bytes_needed(object_refs)
|
98
109
|
end
|
99
110
|
|
100
111
|
# read a „null” type (i.e. null byte, marker byte, bool value)
|
101
112
|
def read_binary_null_type(length)
|
102
113
|
case length
|
103
|
-
when 0
|
104
|
-
when 8
|
105
|
-
when 9
|
106
|
-
when 15 then
|
114
|
+
when 0 then 0 # null byte
|
115
|
+
when 8 then CFBoolean.new(false)
|
116
|
+
when 9 then CFBoolean.new(true)
|
117
|
+
when 15 then 15 # fill type
|
118
|
+
else
|
119
|
+
raise CFFormatError.new("unknown null type: #{length}")
|
107
120
|
end
|
108
|
-
|
109
|
-
raise CFFormatError.new("unknown null type: #{length}")
|
110
121
|
end
|
111
122
|
protected :read_binary_null_type
|
112
123
|
|
113
124
|
# read a binary int value
|
114
125
|
def read_binary_int(fname,fd,length)
|
115
|
-
|
126
|
+
if length > 4
|
127
|
+
raise CFFormatError.new("Integer greater than 16 bytes: #{length}")
|
128
|
+
end
|
116
129
|
|
117
130
|
nbytes = 1 << length
|
118
131
|
|
119
|
-
val = nil
|
120
132
|
buff = fd.read(nbytes)
|
121
133
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
hiword,loword = buff.unpack("NN")
|
134
|
-
if (hiword & 0x80000000) != 0
|
135
|
-
# 8 byte integers are always signed, and are negative when bit 63 is
|
136
|
-
# set. Decoding into either a Fixnum or Bignum is tricky, however,
|
137
|
-
# because the size of a Fixnum varies among systems, and Ruby
|
138
|
-
# doesn't consider the number to be negative, and won't sign extend.
|
139
|
-
val = -(2**63 - ((hiword & 0x7fffffff) << 32 | loword))
|
140
|
-
else
|
141
|
-
val = hiword << 32 | loword
|
134
|
+
CFInteger.new(
|
135
|
+
case length
|
136
|
+
when 0 then buff.unpack("C")[0]
|
137
|
+
when 1 then buff.unpack("n")[0]
|
138
|
+
when 2 then buff.unpack("N")[0]
|
139
|
+
# 8 byte integers are always signed
|
140
|
+
when 3 then buff.unpack("q>")[0]
|
141
|
+
# 16 byte integers are used to represent unsigned 8 byte integers
|
142
|
+
# where the unsigned value is stored in the lower 8 bytes and the
|
143
|
+
# upper 8 bytes are unused.
|
144
|
+
when 4 then buff.unpack("Q>Q>")[1]
|
142
145
|
end
|
143
|
-
|
144
|
-
|
145
|
-
return CFInteger.new(val);
|
146
|
+
)
|
146
147
|
end
|
147
148
|
protected :read_binary_int
|
148
149
|
|
@@ -151,23 +152,22 @@ module CFPropertyList
|
|
151
152
|
raise CFFormatError.new("Real greater than 8 bytes: #{length}") if length > 3
|
152
153
|
|
153
154
|
nbytes = 1 << length
|
154
|
-
val = nil
|
155
155
|
buff = fd.read(nbytes)
|
156
156
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
157
|
+
CFReal.new(
|
158
|
+
case length
|
159
|
+
when 0 # 1 byte float? must be an error
|
160
|
+
raise CFFormatError.new("got #{length+1} byte float, must be an error!")
|
161
|
+
when 1 # 2 byte float? must be an error
|
162
|
+
raise CFFormatError.new("got #{length+1} byte float, must be an error!")
|
163
|
+
when 2 then
|
164
|
+
buff.reverse.unpack("e")[0]
|
165
|
+
when 3 then
|
166
|
+
buff.reverse.unpack("E")[0]
|
167
|
+
else
|
168
|
+
fail "unexpected length: #{length}"
|
169
|
+
end
|
170
|
+
)
|
171
171
|
end
|
172
172
|
protected :read_binary_real
|
173
173
|
|
@@ -176,48 +176,46 @@ module CFPropertyList
|
|
176
176
|
raise CFFormatError.new("Date greater than 8 bytes: #{length}") if length > 3
|
177
177
|
|
178
178
|
nbytes = 1 << length
|
179
|
-
val = nil
|
180
179
|
buff = fd.read(nbytes)
|
181
180
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
return CFDate.new(val,CFDate::TIMESTAMP_APPLE)
|
181
|
+
CFDate.new(
|
182
|
+
case length
|
183
|
+
when 0 then # 1 byte CFDate is an error
|
184
|
+
raise CFFormatError.new("#{length+1} byte CFDate, error")
|
185
|
+
when 1 then # 2 byte CFDate is an error
|
186
|
+
raise CFFormatError.new("#{length+1} byte CFDate, error")
|
187
|
+
when 2 then
|
188
|
+
buff.reverse.unpack("e")[0]
|
189
|
+
when 3 then
|
190
|
+
buff.reverse.unpack("E")[0]
|
191
|
+
end,
|
192
|
+
CFDate::TIMESTAMP_APPLE
|
193
|
+
)
|
196
194
|
end
|
197
195
|
protected :read_binary_date
|
198
196
|
|
199
197
|
# Read a binary data value
|
200
198
|
def read_binary_data(fname,fd,length)
|
201
|
-
|
202
|
-
buff = fd.read(length) if length > 0
|
203
|
-
return CFData.new(buff,CFData::DATA_RAW)
|
199
|
+
CFData.new(read_fd(fd, length), CFData::DATA_RAW)
|
204
200
|
end
|
205
201
|
protected :read_binary_data
|
206
202
|
|
203
|
+
def read_fd fd, length
|
204
|
+
length > 0 ? fd.read(length) : ""
|
205
|
+
end
|
206
|
+
|
207
207
|
# Read a binary string value
|
208
208
|
def read_binary_string(fname,fd,length)
|
209
|
-
buff =
|
210
|
-
buff = fd.read(length) if length > 0
|
211
|
-
|
209
|
+
buff = read_fd fd, length
|
212
210
|
@unique_table[buff] = true unless @unique_table.has_key?(buff)
|
213
|
-
|
211
|
+
CFString.new(buff)
|
214
212
|
end
|
215
213
|
protected :read_binary_string
|
216
214
|
|
217
215
|
# Convert the given string from one charset to another
|
218
216
|
def Binary.charset_convert(str,from,to="UTF-8")
|
219
|
-
return str.
|
220
|
-
|
217
|
+
return str.dup.force_encoding(from).encode(to) if str.respond_to?("encode")
|
218
|
+
Iconv.conv(to,from,str)
|
221
219
|
end
|
222
220
|
|
223
221
|
# Count characters considering character set
|
@@ -228,7 +226,7 @@ module CFPropertyList
|
|
228
226
|
utf8_str = Iconv.conv("UTF-8",charset,str)
|
229
227
|
size = utf8_str.scan(/./mu).size
|
230
228
|
end
|
231
|
-
|
229
|
+
|
232
230
|
# UTF-16 code units in the range D800-DBFF are the beginning of
|
233
231
|
# a surrogate pair, and count as one additional character for
|
234
232
|
# length calculation.
|
@@ -239,8 +237,8 @@ module CFPropertyList
|
|
239
237
|
str.split('').each_slice(2) { |pair| size += 1 if ("\xd8".."\xdb").include?(pair[0]) }
|
240
238
|
end
|
241
239
|
end
|
242
|
-
|
243
|
-
|
240
|
+
|
241
|
+
size
|
244
242
|
end
|
245
243
|
|
246
244
|
# Read a unicode string value, coded as UTF-16BE
|
@@ -251,18 +249,28 @@ module CFPropertyList
|
|
251
249
|
buff = fd.read(2*length)
|
252
250
|
|
253
251
|
@unique_table[buff] = true unless @unique_table.has_key?(buff)
|
254
|
-
|
252
|
+
CFString.new(Binary.charset_convert(buff,"UTF-16BE","UTF-8"))
|
255
253
|
end
|
256
254
|
protected :read_binary_unicode_string
|
257
255
|
|
256
|
+
def unpack_with_size(nbytes, buff)
|
257
|
+
format = ["C*", "n*", "N*", "N*"][nbytes - 1];
|
258
|
+
|
259
|
+
if nbytes == 3
|
260
|
+
buff = "\0" + buff.scan(/.{1,3}/).join("\0")
|
261
|
+
end
|
262
|
+
|
263
|
+
return buff.unpack(format)
|
264
|
+
end
|
265
|
+
|
258
266
|
# Read an binary array value, including contained objects
|
259
267
|
def read_binary_array(fname,fd,length)
|
260
268
|
ary = []
|
261
269
|
|
262
270
|
# first: read object refs
|
263
|
-
if(length != 0)
|
271
|
+
if(length != 0)
|
264
272
|
buff = fd.read(length * @object_ref_size)
|
265
|
-
objects = buff.unpack(@object_ref_size == 1 ? "C*" : "n*")
|
273
|
+
objects = unpack_with_size(@object_ref_size, buff) #buff.unpack(@object_ref_size == 1 ? "C*" : "n*")
|
266
274
|
|
267
275
|
# now: read objects
|
268
276
|
0.upto(length-1) do |i|
|
@@ -271,7 +279,7 @@ module CFPropertyList
|
|
271
279
|
end
|
272
280
|
end
|
273
281
|
|
274
|
-
|
282
|
+
CFArray.new(ary)
|
275
283
|
end
|
276
284
|
protected :read_binary_array
|
277
285
|
|
@@ -282,11 +290,11 @@ module CFPropertyList
|
|
282
290
|
# first: read keys
|
283
291
|
if(length != 0) then
|
284
292
|
buff = fd.read(length * @object_ref_size)
|
285
|
-
keys =
|
293
|
+
keys = unpack_with_size(@object_ref_size, buff)
|
286
294
|
|
287
295
|
# second: read object refs
|
288
296
|
buff = fd.read(length * @object_ref_size)
|
289
|
-
objects =
|
297
|
+
objects = unpack_with_size(@object_ref_size, buff)
|
290
298
|
|
291
299
|
# read real keys and objects
|
292
300
|
0.upto(length-1) do |i|
|
@@ -296,17 +304,18 @@ module CFPropertyList
|
|
296
304
|
end
|
297
305
|
end
|
298
306
|
|
299
|
-
|
307
|
+
CFDictionary.new(dict)
|
300
308
|
end
|
301
309
|
protected :read_binary_dict
|
302
310
|
|
303
|
-
# Read an object type byte, decode it and delegate to the correct
|
311
|
+
# Read an object type byte, decode it and delegate to the correct
|
312
|
+
# reader function
|
304
313
|
def read_binary_object(fname,fd)
|
305
314
|
# first: read the marker byte
|
306
315
|
buff = fd.read(1)
|
307
316
|
|
308
317
|
object_length = buff.unpack("C*")
|
309
|
-
object_length = object_length[0]
|
318
|
+
object_length = object_length[0] & 0xF
|
310
319
|
|
311
320
|
buff = buff.unpack("H*")
|
312
321
|
object_type = buff[0][0].chr
|
@@ -316,29 +325,28 @@ module CFPropertyList
|
|
316
325
|
object_length = object_length.value
|
317
326
|
end
|
318
327
|
|
319
|
-
retval = nil
|
320
328
|
case object_type
|
321
|
-
when '0'
|
322
|
-
|
323
|
-
when '1'
|
324
|
-
|
325
|
-
when '2'
|
326
|
-
|
327
|
-
when '3'
|
328
|
-
|
329
|
-
when '4'
|
330
|
-
|
331
|
-
when '5'
|
332
|
-
|
333
|
-
when '6'
|
334
|
-
|
335
|
-
when '
|
336
|
-
|
337
|
-
when '
|
338
|
-
|
329
|
+
when '0' # null, false, true, fillbyte
|
330
|
+
read_binary_null_type(object_length)
|
331
|
+
when '1' # integer
|
332
|
+
read_binary_int(fname,fd,object_length)
|
333
|
+
when '2' # real
|
334
|
+
read_binary_real(fname,fd,object_length)
|
335
|
+
when '3' # date
|
336
|
+
read_binary_date(fname,fd,object_length)
|
337
|
+
when '4' # data
|
338
|
+
read_binary_data(fname,fd,object_length)
|
339
|
+
when '5' # byte string, usually utf8 encoded
|
340
|
+
read_binary_string(fname,fd,object_length)
|
341
|
+
when '6' # unicode string (utf16be)
|
342
|
+
read_binary_unicode_string(fname,fd,object_length)
|
343
|
+
when '8'
|
344
|
+
CFUid.new(read_binary_int(fname, fd, object_length).value)
|
345
|
+
when 'a' # array
|
346
|
+
read_binary_array(fname,fd,object_length)
|
347
|
+
when 'd' # dictionary
|
348
|
+
read_binary_dict(fname,fd,object_length)
|
339
349
|
end
|
340
|
-
|
341
|
-
return retval
|
342
350
|
end
|
343
351
|
protected :read_binary_object
|
344
352
|
|
@@ -346,36 +354,30 @@ module CFPropertyList
|
|
346
354
|
def read_binary_object_at(fname,fd,pos)
|
347
355
|
position = @offsets[pos]
|
348
356
|
fd.seek(position,IO::SEEK_SET)
|
349
|
-
|
357
|
+
read_binary_object(fname,fd)
|
350
358
|
end
|
351
359
|
protected :read_binary_object_at
|
352
360
|
|
353
361
|
# pack an +int+ of +nbytes+ with size
|
354
362
|
def Binary.pack_it_with_size(nbytes,int)
|
355
363
|
case nbytes
|
356
|
-
when 1
|
357
|
-
|
358
|
-
when
|
359
|
-
return [int].pack('n')
|
360
|
-
when 4
|
361
|
-
return [int].pack('N')
|
364
|
+
when 1 then [int].pack('c')
|
365
|
+
when 2 then [int].pack('n')
|
366
|
+
when 4 then [int].pack('N')
|
362
367
|
when 8
|
363
|
-
|
368
|
+
[int >> 32, int & 0xFFFFFFFF].pack('NN')
|
364
369
|
else
|
365
370
|
raise CFFormatError.new("Don't know how to pack #{nbytes} byte integer")
|
366
371
|
end
|
367
372
|
end
|
368
|
-
|
373
|
+
|
369
374
|
def Binary.pack_int_array_with_size(nbytes, array)
|
370
375
|
case nbytes
|
371
|
-
when 1
|
372
|
-
|
373
|
-
when
|
374
|
-
array.pack('n*')
|
375
|
-
when 4
|
376
|
-
array.pack('N*')
|
376
|
+
when 1 then array.pack('C*')
|
377
|
+
when 2 then array.pack('n*')
|
378
|
+
when 4 then array.pack('N*')
|
377
379
|
when 8
|
378
|
-
array.
|
380
|
+
array.map { |int| [int >> 32, int & 0xFFFFFFFF].pack('NN') }.join
|
379
381
|
else
|
380
382
|
raise CFFormatError.new("Don't know how to pack #{nbytes} byte integer")
|
381
383
|
end
|
@@ -383,41 +385,36 @@ module CFPropertyList
|
|
383
385
|
|
384
386
|
# calculate how many bytes are needed to save +count+
|
385
387
|
def Binary.bytes_needed(count)
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
nbytes = 4
|
392
|
-
elsif count < 2**64
|
393
|
-
nbytes = 8
|
388
|
+
case
|
389
|
+
when count < 2**8 then 1
|
390
|
+
when count < 2**16 then 2
|
391
|
+
when count < 2**32 then 4
|
392
|
+
when count < 2**64 then 8
|
394
393
|
else
|
395
394
|
raise CFFormatError.new("Data size too large: #{count}")
|
396
395
|
end
|
397
|
-
|
398
|
-
return nbytes
|
399
396
|
end
|
400
397
|
|
401
398
|
# Create a type byte for binary format as defined by apple
|
402
399
|
def Binary.type_bytes(type, length)
|
403
400
|
if length < 15
|
404
|
-
|
401
|
+
[(type << 4) | length].pack('C')
|
405
402
|
else
|
406
403
|
bytes = [(type << 4) | 0xF]
|
407
404
|
if length <= 0xFF
|
408
|
-
|
405
|
+
bytes.push(0x10, length).pack('CCC') # 1 byte length
|
409
406
|
elsif length <= 0xFFFF
|
410
|
-
|
407
|
+
bytes.push(0x11, length).pack('CCn') # 2 byte length
|
411
408
|
elsif length <= 0xFFFFFFFF
|
412
|
-
|
409
|
+
bytes.push(0x12, length).pack('CCN') # 4 byte length
|
413
410
|
elsif length <= 0x7FFFFFFFFFFFFFFF
|
414
|
-
|
411
|
+
bytes.push(0x13, length >> 32, length & 0xFFFFFFFF).pack('CCNN') # 8 byte length
|
415
412
|
else
|
416
413
|
raise CFFormatError.new("Integer too large: #{int}")
|
417
414
|
end
|
418
415
|
end
|
419
416
|
end
|
420
|
-
|
417
|
+
|
421
418
|
def count_object_refs(object)
|
422
419
|
case object
|
423
420
|
when CFArray
|
@@ -443,140 +440,136 @@ module CFPropertyList
|
|
443
440
|
|
444
441
|
def Binary.ascii_string?(str)
|
445
442
|
if str.respond_to?(:ascii_only?)
|
446
|
-
|
443
|
+
str.ascii_only?
|
447
444
|
else
|
448
|
-
|
445
|
+
str !~ /[\x80-\xFF]/mn
|
449
446
|
end
|
450
447
|
end
|
451
|
-
|
448
|
+
|
452
449
|
# Uniques and transforms a string value to binary format and adds it to the object table
|
453
450
|
def string_to_binary(val)
|
454
|
-
|
455
|
-
|
456
|
-
unless(@unique_table.has_key?(val)) then
|
457
|
-
saved_object_count = @written_object_count
|
458
|
-
@written_object_count += 1
|
459
|
-
|
460
|
-
@unique_table[val] = saved_object_count
|
461
|
-
utf16 = !Binary.ascii_string?(val)
|
451
|
+
val = val.to_s
|
462
452
|
|
463
|
-
|
464
|
-
if(
|
465
|
-
utf8_strlen = Binary.charset_strlen(val, "UTF-8")
|
453
|
+
@unique_table[val] ||= begin
|
454
|
+
if !Binary.ascii_string?(val)
|
466
455
|
val = Binary.charset_convert(val,"UTF-8","UTF-16BE")
|
467
456
|
bdata = Binary.type_bytes(0b0110, Binary.charset_strlen(val,"UTF-16BE"))
|
468
457
|
|
469
458
|
val.force_encoding("ASCII-8BIT") if val.respond_to?("encode")
|
470
|
-
@object_table[
|
459
|
+
@object_table[@written_object_count] = bdata << val
|
471
460
|
else
|
472
|
-
utf8_strlen = val.bytesize
|
473
461
|
bdata = Binary.type_bytes(0b0101,val.bytesize)
|
474
|
-
@object_table[
|
462
|
+
@object_table[@written_object_count] = bdata << val
|
475
463
|
end
|
476
|
-
else
|
477
|
-
saved_object_count = @unique_table[val]
|
478
|
-
end
|
479
464
|
|
480
|
-
|
465
|
+
@written_object_count += 1
|
466
|
+
@written_object_count - 1
|
467
|
+
end
|
481
468
|
end
|
482
469
|
|
483
470
|
# Codes an integer to binary format
|
484
471
|
def int_to_binary(value)
|
472
|
+
# Note: nbytes is actually an exponent. number of bytes = 2**nbytes.
|
485
473
|
nbytes = 0
|
486
|
-
nbytes = 1
|
487
|
-
nbytes += 1 if value > 0xFFFF # 4 byte integer
|
488
|
-
nbytes += 1 if value > 0xFFFFFFFF # 8 byte integer
|
489
|
-
nbytes
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
if(nbytes == 0) then
|
498
|
-
fmt = "C"
|
499
|
-
elsif(nbytes == 1)
|
500
|
-
fmt = "n"
|
474
|
+
nbytes = 1 if value > 0xFF # 1 byte unsigned integer
|
475
|
+
nbytes += 1 if value > 0xFFFF # 4 byte unsigned integer
|
476
|
+
nbytes += 1 if value > 0xFFFFFFFF # 8 byte unsigned integer
|
477
|
+
nbytes += 1 if value > 0x7FFFFFFFFFFFFFFF # 8 byte unsigned integer, stored in lower half of 16 bytes
|
478
|
+
nbytes = 3 if value < 0 # signed integers always stored in 8 bytes
|
479
|
+
|
480
|
+
Binary.type_bytes(0b0001, nbytes) <<
|
481
|
+
if nbytes < 4
|
482
|
+
[value].pack(["C", "n", "N", "q>"][nbytes])
|
483
|
+
else # nbytes == 4
|
484
|
+
[0,value].pack("Q>Q>")
|
501
485
|
end
|
502
|
-
|
503
|
-
buff = [value].pack(fmt)
|
504
|
-
else
|
505
|
-
# 64 bit signed integer; we need the higher and the lower 32 bit of the value
|
506
|
-
high_word = value >> 32
|
507
|
-
low_word = value & 0xFFFFFFFF
|
508
|
-
buff = [high_word,low_word].pack("NN")
|
509
|
-
end
|
510
|
-
|
511
|
-
return bdata + buff
|
512
486
|
end
|
513
487
|
|
514
488
|
# Codes a real value to binary format
|
515
489
|
def real_to_binary(val)
|
516
|
-
|
517
|
-
buff = [val].pack("d")
|
518
|
-
return bdata + buff.reverse
|
490
|
+
Binary.type_bytes(0b0010,3) << [val].pack("E").reverse
|
519
491
|
end
|
520
492
|
|
521
493
|
# Converts a numeric value to binary and adds it to the object table
|
522
494
|
def num_to_binary(value)
|
523
|
-
|
495
|
+
@object_table[@written_object_count] =
|
496
|
+
if value.is_a?(CFInteger)
|
497
|
+
int_to_binary(value.value)
|
498
|
+
else
|
499
|
+
real_to_binary(value.value)
|
500
|
+
end
|
501
|
+
|
524
502
|
@written_object_count += 1
|
503
|
+
@written_object_count - 1
|
504
|
+
end
|
525
505
|
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
506
|
+
def uid_to_binary(value)
|
507
|
+
nbytes = 0
|
508
|
+
nbytes = 1 if value > 0xFF # 1 byte integer
|
509
|
+
nbytes += 1 if value > 0xFFFF # 4 byte integer
|
510
|
+
nbytes += 1 if value > 0xFFFFFFFF # 8 byte integer
|
511
|
+
nbytes = 3 if value < 0 # 8 byte integer, since signed
|
512
|
+
|
513
|
+
@object_table[@written_object_count] = Binary.type_bytes(0b1000, nbytes) <<
|
514
|
+
if nbytes < 3
|
515
|
+
[value].pack(
|
516
|
+
if nbytes == 0 then "C"
|
517
|
+
elsif nbytes == 1 then "n"
|
518
|
+
else "N"
|
519
|
+
end
|
520
|
+
)
|
521
|
+
else
|
522
|
+
# 64 bit signed integer; we need the higher and the lower 32 bit of the value
|
523
|
+
high_word = value >> 32
|
524
|
+
low_word = value & 0xFFFFFFFF
|
525
|
+
[high_word,low_word].pack("NN")
|
526
|
+
end
|
532
527
|
|
533
|
-
@
|
534
|
-
|
528
|
+
@written_object_count += 1
|
529
|
+
@written_object_count - 1
|
535
530
|
end
|
536
531
|
|
537
532
|
# Convert date value (apple format) to binary and adds it to the object table
|
538
533
|
def date_to_binary(val)
|
539
|
-
saved_object_count = @written_object_count
|
540
|
-
@written_object_count += 1
|
541
|
-
|
542
534
|
val = val.getutc.to_f - CFDate::DATE_DIFF_APPLE_UNIX # CFDate is a real, number of seconds since 01/01/2001 00:00:00 GMT
|
543
535
|
|
544
|
-
|
545
|
-
|
536
|
+
@object_table[@written_object_count] =
|
537
|
+
(Binary.type_bytes(0b0011, 3) << [val].pack("E").reverse)
|
546
538
|
|
547
|
-
|
539
|
+
@written_object_count += 1
|
540
|
+
@written_object_count - 1
|
548
541
|
end
|
549
542
|
|
550
543
|
# Convert a bool value to binary and add it to the object table
|
551
544
|
def bool_to_binary(val)
|
552
|
-
saved_object_count = @written_object_count
|
553
|
-
@written_object_count += 1
|
554
545
|
|
555
|
-
@object_table[
|
556
|
-
|
546
|
+
@object_table[@written_object_count] = val ? "\x9" : "\x8" # 0x9 is 1001, type indicator for true; 0x8 is 1000, type indicator for false
|
547
|
+
@written_object_count += 1
|
548
|
+
@written_object_count - 1
|
557
549
|
end
|
558
550
|
|
559
551
|
# Convert data value to binary format and add it to the object table
|
560
552
|
def data_to_binary(val)
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
bdata = Binary.type_bytes(0b0100, val.bytesize)
|
565
|
-
@object_table[saved_object_count] = bdata + val
|
553
|
+
@object_table[@written_object_count] =
|
554
|
+
(Binary.type_bytes(0b0100, val.bytesize) << val)
|
566
555
|
|
567
|
-
|
556
|
+
@written_object_count += 1
|
557
|
+
@written_object_count - 1
|
568
558
|
end
|
569
559
|
|
570
560
|
# Convert array to binary format and add it to the object table
|
571
561
|
def array_to_binary(val)
|
572
562
|
saved_object_count = @written_object_count
|
573
563
|
@written_object_count += 1
|
564
|
+
#@object_refs += val.value.size
|
574
565
|
|
575
|
-
|
576
|
-
|
566
|
+
values = val.value.map { |v| v.to_binary(self) }
|
567
|
+
bdata = Binary.type_bytes(0b1010, val.value.size) <<
|
568
|
+
Binary.pack_int_array_with_size(object_ref_size(@object_refs),
|
569
|
+
values)
|
577
570
|
|
578
571
|
@object_table[saved_object_count] = bdata
|
579
|
-
|
572
|
+
saved_object_count
|
580
573
|
end
|
581
574
|
|
582
575
|
# Convert dictionary to binary format and add it to the object table
|
@@ -584,11 +577,13 @@ module CFPropertyList
|
|
584
577
|
saved_object_count = @written_object_count
|
585
578
|
@written_object_count += 1
|
586
579
|
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
580
|
+
#@object_refs += val.value.keys.size * 2
|
581
|
+
|
582
|
+
keys_and_values = val.value.keys.map { |k| CFString.new(k).to_binary(self) }
|
583
|
+
keys_and_values.concat(val.value.values.map { |v| v.to_binary(self) })
|
584
|
+
|
585
|
+
bdata = Binary.type_bytes(0b1101,val.value.size) <<
|
586
|
+
Binary.pack_int_array_with_size(object_ref_size(@object_refs), keys_and_values)
|
592
587
|
|
593
588
|
@object_table[saved_object_count] = bdata
|
594
589
|
return saved_object_count
|