CFPropertyList 2.0.14 → 3.0.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 +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
|