ipa_reader 0.6.1 → 0.7.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.
- data/.gitignore +2 -0
- data/{README.txt → README.md} +10 -12
- data/Rakefile +4 -1
- data/lib/ipa_reader/ipa_file.rb +4 -1
- data/lib/ipa_reader/plist_binary.rb +28 -485
- data/lib/ipa_reader/png_file.rb +1 -1
- data/version.txt +1 -1
- metadata +71 -73
- data/.rvmrc +0 -1
data/.gitignore
ADDED
data/{README.txt → README.md}
RENAMED
@@ -1,18 +1,16 @@
|
|
1
|
-
ipa_reader
|
2
|
-
by Nicholas Schlueter
|
3
|
-
http://twitter.com/schlu
|
1
|
+
# ipa_reader
|
4
2
|
|
5
|
-
|
3
|
+
by [Nicholas Schlueter](http://twitter.com/schlu)
|
6
4
|
|
7
|
-
|
5
|
+
## DESCRIPTION
|
8
6
|
|
9
|
-
|
7
|
+
Reads metadata form iPhone Package Archive Files (ipa).
|
10
8
|
|
11
9
|
I am using this gem to get version to build the over the air iPhone Ad Hoc distribution plist file.
|
12
10
|
|
13
|
-
|
11
|
+
## USAGE
|
14
12
|
|
15
|
-
irb > require 'rubygems'
|
13
|
+
`irb > require 'rubygems'
|
16
14
|
=> true
|
17
15
|
irb > require 'ipa_reader'
|
18
16
|
=> true
|
@@ -31,13 +29,13 @@ irb > ipa_file.url_schemes
|
|
31
29
|
irb > ipa_file.bundle_identifier
|
32
30
|
=> "com.dcrails.multig"
|
33
31
|
irb > ipa_file.icon_prerendered
|
34
|
-
=> false
|
32
|
+
=> false`
|
35
33
|
|
36
|
-
|
34
|
+
## INSTALL
|
37
35
|
|
38
|
-
gem install ipa_reader
|
36
|
+
`gem install ipa_reader`
|
39
37
|
|
40
|
-
|
38
|
+
## LICENSE
|
41
39
|
|
42
40
|
(The MIT License)
|
43
41
|
|
data/Rakefile
CHANGED
@@ -11,8 +11,11 @@ task 'gem:release' => 'test:run'
|
|
11
11
|
Bones {
|
12
12
|
name 'ipa_reader'
|
13
13
|
authors 'Nicholas Schlueter'
|
14
|
+
summary 'Reads metadata form iPhone Package Archive Files (ipa).'
|
15
|
+
description 'I am using this gem to get version to build the over the air iPhone Ad Hoc distribution plist file.'
|
14
16
|
email 'schlueter@gmail.com'
|
15
|
-
url 'http://github.com/
|
17
|
+
url 'http://github.com/schlu/Ipa-Reader'
|
16
18
|
depend_on "zip", "2.0.2"
|
19
|
+
depend_on "CFPropertyList", "2.1.1"
|
17
20
|
}
|
18
21
|
|
data/lib/ipa_reader/ipa_file.rb
CHANGED
@@ -4,6 +4,7 @@ rescue LoadError
|
|
4
4
|
require 'rubygems'
|
5
5
|
require 'zip'
|
6
6
|
end
|
7
|
+
|
7
8
|
module IpaReader
|
8
9
|
class IpaFile
|
9
10
|
attr_accessor :plist, :file_path
|
@@ -11,7 +12,8 @@ module IpaReader
|
|
11
12
|
self.file_path = file_path
|
12
13
|
info_plist_file = nil
|
13
14
|
Zip::ZipFile.foreach(file_path) { |f| info_plist_file = f if f.name.match(/\/Info.plist/) }
|
14
|
-
|
15
|
+
cf_plist = CFPropertyList::List.new(:data => self.read_file(/\/Info.plist/), :format => CFPropertyList::List::FORMAT_BINARY)
|
16
|
+
self.plist = cf_plist.value.to_rb
|
15
17
|
end
|
16
18
|
|
17
19
|
def version
|
@@ -56,6 +58,7 @@ module IpaReader
|
|
56
58
|
end
|
57
59
|
|
58
60
|
def bundle_identifier
|
61
|
+
puts plist["CFBundleIdentifier"].class
|
59
62
|
plist["CFBundleIdentifier"]
|
60
63
|
end
|
61
64
|
|
@@ -1,490 +1,33 @@
|
|
1
|
-
|
2
|
-
require
|
3
|
-
|
4
|
-
require
|
1
|
+
begin
|
2
|
+
require 'cfpropertylist'
|
3
|
+
rescue LoadError
|
4
|
+
require 'rubygems'
|
5
|
+
require 'cfpropertylist'
|
6
|
+
end
|
5
7
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
plist += pack_int(offset, offset_byte_size)
|
28
|
-
end
|
29
|
-
# Write trailer.
|
30
|
-
plist += "\0\0\0\0\0\0" # Six unused bytes
|
31
|
-
plist += [
|
32
|
-
offset_byte_size,
|
33
|
-
ref_byte_size,
|
34
|
-
encoded_objs.length >> 32, encoded_objs.length & 0xffffffff,
|
35
|
-
0, 0, # Index of root object
|
36
|
-
offset_table_addr >> 32, offset_table_addr & 0xffffffff
|
37
|
-
].pack("CCNNNNNN")
|
38
|
-
plist
|
39
|
-
end
|
40
|
-
|
41
|
-
def self.decode_binary_plist(plist)
|
42
|
-
# Check header.
|
43
|
-
unless plist[0, 6] == "bplist"
|
44
|
-
raise ArgumentError, "argument is not a binary property list"
|
45
|
-
end
|
46
|
-
version = plist[6, 2]
|
47
|
-
unless version == "00"
|
48
|
-
raise ArgumentError,
|
49
|
-
"don't know how to decode format version #{version}"
|
50
|
-
end
|
51
|
-
# Read trailer.
|
52
|
-
trailer = plist[-26, 26].unpack("CCNNNNNN")
|
53
|
-
offset_byte_size = trailer[0]
|
54
|
-
ref_byte_size = trailer[1]
|
55
|
-
encoded_objs_length = combine_ints(32, trailer[2], trailer[3])
|
56
|
-
root_index = combine_ints(32, trailer[4], trailer[5])
|
57
|
-
offset_table_addr = combine_ints(32, trailer[6], trailer[7])
|
58
|
-
# Decode objects.
|
59
|
-
root_offset = offset_for_index(plist, offset_table_addr,
|
60
|
-
offset_byte_size, root_index)
|
61
|
-
root_obj = decode_binary_plist_obj(plist, root_offset, ref_byte_size)
|
62
|
-
unflatten_collection(root_obj, [root_obj], plist, offset_table_addr,
|
63
|
-
offset_byte_size, ref_byte_size)
|
64
|
-
end
|
65
|
-
|
66
|
-
private
|
67
|
-
|
68
|
-
# These marker bytes are prefixed to objects in a binary property list to
|
69
|
-
# indicate the type of the object.
|
70
|
-
CFBinaryPlistMarkerNull = 0x00 # :nodoc:
|
71
|
-
CFBinaryPlistMarkerFalse = 0x08 # :nodoc:
|
72
|
-
CFBinaryPlistMarkerTrue = 0x09 # :nodoc:
|
73
|
-
CFBinaryPlistMarkerFill = 0x0F # :nodoc:
|
74
|
-
CFBinaryPlistMarkerInt = 0x10 # :nodoc:
|
75
|
-
CFBinaryPlistMarkerReal = 0x20 # :nodoc:
|
76
|
-
CFBinaryPlistMarkerDate = 0x33 # :nodoc:
|
77
|
-
CFBinaryPlistMarkerData = 0x40 # :nodoc:
|
78
|
-
CFBinaryPlistMarkerASCIIString = 0x50 # :nodoc:
|
79
|
-
CFBinaryPlistMarkerUnicode16String = 0x60 # :nodoc:
|
80
|
-
CFBinaryPlistMarkerUID = 0x80 # :nodoc:
|
81
|
-
CFBinaryPlistMarkerArray = 0xA0 # :nodoc:
|
82
|
-
CFBinaryPlistMarkerSet = 0xC0 # :nodoc:
|
83
|
-
CFBinaryPlistMarkerDict = 0xD0 # :nodoc:
|
84
|
-
|
85
|
-
# POSIX uses a reference time of 1970-01-01T00:00:00Z; Cocoa's reference
|
86
|
-
# time is in 2001. This interval is for converting between the two.
|
87
|
-
NSTimeIntervalSince1970 = 978307200.0 # :nodoc:
|
88
|
-
|
89
|
-
# Takes an object (nominally a collection, like an Array, Set, or Hash, but
|
90
|
-
# any object is acceptable) and flattens it into a one-dimensional array.
|
91
|
-
# Non-collection objects appear in the array as-is, but the contents of
|
92
|
-
# Arrays, Sets, and Hashes are modified like so: (1) The contents of the
|
93
|
-
# collection are added, one-by-one, to the one-dimensional array. (2) The
|
94
|
-
# collection itself is modified so that it contains indexes pointing to the
|
95
|
-
# objects in the one-dimensional array. Here's an example with an Array:
|
96
|
-
#
|
97
|
-
# ary = [:a, :b, :c]
|
98
|
-
# flatten_collection(ary) # => [[1, 2, 3], :a, :b, :c]
|
99
|
-
#
|
100
|
-
# In the case of a Hash, keys and values are both appended to the one-
|
101
|
-
# dimensional array and then replaced with indexes.
|
102
|
-
#
|
103
|
-
# hsh = {:a => "blue", :b => "purple", :c => "green"}
|
104
|
-
# flatten_collection(hsh)
|
105
|
-
# # => [{1 => 2, 3 => 4, 5 => 6}, :a, "blue", :b, "purple", :c, "green"]
|
106
|
-
#
|
107
|
-
# An object will never be added to the one-dimensional array twice. If a
|
108
|
-
# collection refers to an object more than once, the object will be added
|
109
|
-
# to the one-dimensional array only once.
|
110
|
-
#
|
111
|
-
# ary = [:a, :a, :a]
|
112
|
-
# flatten_collection(ary) # => [[1, 1, 1], :a]
|
113
|
-
#
|
114
|
-
# The +obj_list+ and +id_refs+ parameters are private; they're used for
|
115
|
-
# descending into sub-collections recursively.
|
116
|
-
def self.flatten_collection(collection, obj_list = [], id_refs = {})
|
117
|
-
case collection
|
118
|
-
when Array, Set
|
119
|
-
if id_refs[collection.object_id]
|
120
|
-
return obj_list[id_refs[collection.object_id]]
|
121
|
-
end
|
122
|
-
obj_refs = collection.class.new
|
123
|
-
id_refs[collection.object_id] = obj_list.length
|
124
|
-
obj_list << obj_refs
|
125
|
-
collection.each do |obj|
|
126
|
-
flatten_collection(obj, obj_list, id_refs)
|
127
|
-
obj_refs << id_refs[obj.object_id]
|
128
|
-
end
|
129
|
-
return obj_list
|
130
|
-
when Hash
|
131
|
-
if id_refs[collection.object_id]
|
132
|
-
return obj_list[id_refs[collection.object_id]]
|
133
|
-
end
|
134
|
-
obj_refs = {}
|
135
|
-
id_refs[collection.object_id] = obj_list.length
|
136
|
-
obj_list << obj_refs
|
137
|
-
collection.each do |key, value|
|
138
|
-
key = key.to_s if key.is_a?(Symbol)
|
139
|
-
flatten_collection(key, obj_list, id_refs)
|
140
|
-
flatten_collection(value, obj_list, id_refs)
|
141
|
-
obj_refs[id_refs[key.object_id]] = id_refs[value.object_id]
|
142
|
-
end
|
143
|
-
return obj_list
|
144
|
-
else
|
145
|
-
unless id_refs[collection.object_id]
|
146
|
-
id_refs[collection.object_id] = obj_list.length
|
147
|
-
obj_list << collection
|
148
|
-
end
|
149
|
-
return obj_list
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
def self.unflatten_collection(collection, obj_list, plist,
|
154
|
-
offset_table_addr, offset_byte_size, ref_byte_size)
|
155
|
-
case collection
|
156
|
-
when Array, Set
|
157
|
-
collection.collect! do |index|
|
158
|
-
if obj = obj_list[index]
|
159
|
-
obj
|
160
|
-
else
|
161
|
-
offset = offset_for_index(plist, offset_table_addr, offset_byte_size,
|
162
|
-
index)
|
163
|
-
obj = decode_binary_plist_obj(plist, offset, ref_byte_size)
|
164
|
-
obj_list[index] = obj
|
165
|
-
unflatten_collection(obj, obj_list, plist, offset_table_addr,
|
166
|
-
offset_byte_size, ref_byte_size)
|
167
|
-
end
|
168
|
-
end
|
169
|
-
when Hash
|
170
|
-
hsh = {}
|
171
|
-
collection.each do |key, value|
|
172
|
-
unless key_obj = obj_list[key]
|
173
|
-
offset = offset_for_index(plist, offset_table_addr, offset_byte_size,
|
174
|
-
key)
|
175
|
-
key_obj = decode_binary_plist_obj(plist, offset, ref_byte_size)
|
176
|
-
obj_list[key] = key_obj
|
177
|
-
key_obj = unflatten_collection(key_obj, obj_list, plist,
|
178
|
-
offset_table_addr, offset_byte_size, ref_byte_size)
|
179
|
-
end
|
180
|
-
unless value_obj = obj_list[value]
|
181
|
-
offset = offset_for_index(plist, offset_table_addr, offset_byte_size,
|
182
|
-
value)
|
183
|
-
value_obj = decode_binary_plist_obj(plist, offset, ref_byte_size)
|
184
|
-
obj_list[value] = value_obj
|
185
|
-
value_obj = unflatten_collection(value_obj, obj_list, plist,
|
186
|
-
offset_table_addr, offset_byte_size, ref_byte_size)
|
187
|
-
end
|
188
|
-
hsh[key_obj] = value_obj
|
189
|
-
end
|
190
|
-
collection.replace(hsh)
|
191
|
-
end
|
192
|
-
return collection
|
193
|
-
end
|
194
|
-
|
195
|
-
# Returns a binary property list fragment that represents +obj+. The
|
196
|
-
# returned string is not a complete property list, just a fragment that
|
197
|
-
# describes +obj+, and is not useful without a header, offset table, and
|
198
|
-
# trailer.
|
199
|
-
#
|
200
|
-
# The following classes are recognized: String, Float, Integer, the Boolean
|
201
|
-
# classes, Time, IO, StringIO, Array, Set, and Hash. IO and StringIO
|
202
|
-
# objects are rewound, read, and the contents stored as data (i.e., Cocoa
|
203
|
-
# applications will decode them as NSData). All other classes are dumped
|
204
|
-
# with Marshal and stored as data.
|
205
|
-
#
|
206
|
-
# Note that subclasses of the supported classes will be encoded as though
|
207
|
-
# they were the supported superclass. Thus, a subclass of (for example)
|
208
|
-
# String will be encoded and decoded as a String, not as the subclass:
|
209
|
-
#
|
210
|
-
# class ExampleString < String
|
211
|
-
# ...
|
212
|
-
# end
|
213
|
-
#
|
214
|
-
# s = ExampleString.new("disquieting plantlike mystery")
|
215
|
-
# encoded_s = binary_plist_obj(s)
|
216
|
-
# decoded_s = decode_binary_plist_obj(encoded_s)
|
217
|
-
# puts decoded_s.class # => String
|
218
|
-
#
|
219
|
-
# +ref_byte_size+ is the number of bytes to use for storing references to
|
220
|
-
# other objects.
|
221
|
-
def self.binary_plist_obj(obj, ref_byte_size = 4)
|
222
|
-
case obj
|
223
|
-
when String
|
224
|
-
obj = obj.to_s if obj.is_a?(Symbol)
|
225
|
-
# This doesn't really work. NKF's guess method is really, really bad
|
226
|
-
# at discovering UTF8 when only a handful of characters are multi-byte.
|
227
|
-
encoding = NKF.guess2(obj)
|
228
|
-
if encoding == NKF::ASCII && obj =~ /[\x80-\xff]/
|
229
|
-
encoding = NKF::UTF8
|
230
|
-
end
|
231
|
-
if [NKF::ASCII, NKF::BINARY, NKF::UNKNOWN].include?(encoding)
|
232
|
-
result = (CFBinaryPlistMarkerASCIIString |
|
233
|
-
(obj.length < 15 ? obj.length : 0xf)).chr
|
234
|
-
result += binary_plist_obj(obj.length) if obj.length >= 15
|
235
|
-
result += obj
|
236
|
-
return result
|
237
|
-
else
|
238
|
-
# Convert to UTF8.
|
239
|
-
if encoding == NKF::UTF8
|
240
|
-
utf8 = obj
|
241
|
-
else
|
242
|
-
utf8 = NKF.nkf("-m0 -w", obj)
|
243
|
-
end
|
244
|
-
# Decode each character's UCS codepoint.
|
245
|
-
codepoints = []
|
246
|
-
i = 0
|
247
|
-
while i < utf8.length
|
248
|
-
byte = utf8[i]
|
249
|
-
if byte & 0xe0 == 0xc0
|
250
|
-
codepoints << ((byte & 0x1f) << 6) + (utf8[i+1] & 0x3f)
|
251
|
-
i += 1
|
252
|
-
elsif byte & 0xf0 == 0xe0
|
253
|
-
codepoints << ((byte & 0xf) << 12) + ((utf8[i+1] & 0x3f) << 6) +
|
254
|
-
(utf8[i+2] & 0x3f)
|
255
|
-
i += 2
|
256
|
-
elsif byte & 0xf8 == 0xf0
|
257
|
-
codepoints << ((byte & 0xe) << 18) + ((utf8[i+1] & 0x3f) << 12) +
|
258
|
-
((utf8[i+2] & 0x3f) << 6) + (utf8[i+3] & 0x3f)
|
259
|
-
i += 3
|
260
|
-
else
|
261
|
-
codepoints << byte
|
262
|
-
end
|
263
|
-
if codepoints.last > 0xffff
|
264
|
-
raise(ArgumentError, "codepoint too high - only the Basic Multilingual Plane can be encoded")
|
265
|
-
end
|
266
|
-
i += 1
|
267
|
-
end
|
268
|
-
# Return string of 16-bit codepoints.
|
269
|
-
data = codepoints.pack("n*")
|
270
|
-
result = (CFBinaryPlistMarkerUnicode16String |
|
271
|
-
(codepoints.length < 15 ? codepoints.length : 0xf)).chr
|
272
|
-
result += binary_plist_obj(codepoints.length) if codepoints.length >= 15
|
273
|
-
result += data
|
274
|
-
return result
|
275
|
-
end
|
276
|
-
when Float
|
277
|
-
return (CFBinaryPlistMarkerReal | 3).chr + [obj].pack("G")
|
278
|
-
when Integer
|
279
|
-
nbytes = min_byte_size(obj)
|
280
|
-
size_bits = { 1 => 0, 2 => 1, 4 => 2, 8 => 3, 16 => 4 }[nbytes]
|
281
|
-
return (CFBinaryPlistMarkerInt | size_bits).chr + pack_int(obj, nbytes)
|
282
|
-
when TrueClass
|
283
|
-
return CFBinaryPlistMarkerTrue.chr
|
284
|
-
when FalseClass
|
285
|
-
return CFBinaryPlistMarkerFalse.chr
|
286
|
-
when Time
|
287
|
-
return CFBinaryPlistMarkerDate.chr +
|
288
|
-
[obj.to_f - NSTimeIntervalSince1970].pack("G")
|
289
|
-
when IO, StringIO
|
290
|
-
obj.rewind
|
291
|
-
return binary_plist_data(obj.read)
|
292
|
-
when Array
|
293
|
-
# Must be an array of object references as returned by flatten_collection.
|
294
|
-
result = (CFBinaryPlistMarkerArray | (obj.length < 15 ? obj.length : 0xf)).chr
|
295
|
-
result += binary_plist_obj(obj.length) if obj.length >= 15
|
296
|
-
result += obj.collect! { |i| pack_int(i, ref_byte_size) }.join
|
297
|
-
when Set
|
298
|
-
# Must be a set of object references as returned by flatten_collection.
|
299
|
-
result = (CFBinaryPlistMarkerSet | (obj.length < 15 ? obj.length : 0xf)).chr
|
300
|
-
result += binary_plist_obj(obj.length) if obj.length >= 15
|
301
|
-
result += obj.to_a.collect! { |i| pack_int(i, ref_byte_size) }.join
|
302
|
-
when Hash
|
303
|
-
# Must be a table of object references as returned by flatten_collection.
|
304
|
-
result = (CFBinaryPlistMarkerDict | (obj.length < 15 ? obj.length : 0xf)).chr
|
305
|
-
result += binary_plist_obj(obj.length) if obj.length >= 15
|
306
|
-
result += obj.keys.collect! { |i| pack_int(i, ref_byte_size) }.join
|
307
|
-
result += obj.values.collect! { |i| pack_int(i, ref_byte_size) }.join
|
308
|
-
else
|
309
|
-
return binary_plist_data(Marshal.dump(obj))
|
310
|
-
end
|
311
|
-
end
|
312
|
-
|
313
|
-
def self.decode_binary_plist_obj(plist, offset, ref_byte_size)
|
314
|
-
case plist[offset]
|
315
|
-
when CFBinaryPlistMarkerASCIIString..(CFBinaryPlistMarkerASCIIString | 0xf)
|
316
|
-
length, offset = decode_length(plist, offset)
|
317
|
-
return plist[offset, length]
|
318
|
-
when CFBinaryPlistMarkerUnicode16String..(CFBinaryPlistMarkerUnicode16String | 0xf)
|
319
|
-
length, offset = decode_length(plist, offset)
|
320
|
-
codepoints = plist[offset, length * 2].unpack("n*")
|
321
|
-
str = ""
|
322
|
-
codepoints.each do |codepoint|
|
323
|
-
if codepoint <= 0x7f
|
324
|
-
ch = ' '
|
325
|
-
ch[0] = to_i
|
326
|
-
elsif codepoint <= 0x7ff
|
327
|
-
ch = ' '
|
328
|
-
ch[0] = ((codepoint & 0x7c0) >> 6) | 0xc0
|
329
|
-
ch[1] = codepoint & 0x3f | 0x80
|
330
|
-
else
|
331
|
-
ch = ' '
|
332
|
-
ch[0] = ((codepoint & 0xf000) >> 12) | 0xe0
|
333
|
-
ch[1] = ((codepoint & 0xfc0) >> 6) | 0x80
|
334
|
-
ch[2] = codepoint & 0x3f | 0x80
|
335
|
-
end
|
336
|
-
str << ch
|
337
|
-
end
|
338
|
-
return str
|
339
|
-
when CFBinaryPlistMarkerReal | 3
|
340
|
-
return plist[offset+1, 8].unpack("G").first
|
341
|
-
when CFBinaryPlistMarkerInt..(CFBinaryPlistMarkerInt | 0xf)
|
342
|
-
num_bytes = 2 ** (plist[offset] & 0xf)
|
343
|
-
return unpack_int(plist[offset+1, num_bytes])
|
344
|
-
when CFBinaryPlistMarkerTrue
|
345
|
-
return true
|
346
|
-
when CFBinaryPlistMarkerFalse
|
347
|
-
return false
|
348
|
-
when CFBinaryPlistMarkerDate
|
349
|
-
secs = plist[offset+1, 8].unpack("G").first + NSTimeIntervalSince1970
|
350
|
-
return Time.at(secs)
|
351
|
-
when CFBinaryPlistMarkerData..(CFBinaryPlistMarkerData | 0xf)
|
352
|
-
length, offset = decode_length(plist, offset)
|
353
|
-
return StringIO.new(plist[offset, length])
|
354
|
-
when CFBinaryPlistMarkerArray..(CFBinaryPlistMarkerArray | 0xf)
|
355
|
-
ary = []
|
356
|
-
length, offset = decode_length(plist, offset)
|
357
|
-
length.times do
|
358
|
-
ary << unpack_int(plist[offset, ref_byte_size])
|
359
|
-
offset += ref_byte_size
|
360
|
-
end
|
361
|
-
return ary
|
362
|
-
when CFBinaryPlistMarkerDict..(CFBinaryPlistMarkerDict | 0xf)
|
363
|
-
hsh = {}
|
364
|
-
keys = []
|
365
|
-
length, offset = decode_length(plist, offset)
|
366
|
-
length.times do
|
367
|
-
keys << unpack_int(plist[offset, ref_byte_size])
|
368
|
-
offset += ref_byte_size
|
369
|
-
end
|
370
|
-
length.times do |i|
|
371
|
-
hsh[keys[i]] = unpack_int(plist[offset, ref_byte_size])
|
372
|
-
offset += ref_byte_size
|
373
|
-
end
|
374
|
-
return hsh
|
375
|
-
end
|
376
|
-
end
|
377
|
-
|
378
|
-
# Returns a binary property list fragment that represents a data object
|
379
|
-
# with the contents of the string +data+. A Cocoa application would decode
|
380
|
-
# this fragment as NSData. Like binary_plist_obj, the value returned by
|
381
|
-
# this method is not usable by itself; it is only useful as part of a
|
382
|
-
# complete binary property list with a header, offset table, and trailer.
|
383
|
-
def self.binary_plist_data(data)
|
384
|
-
result = (CFBinaryPlistMarkerData |
|
385
|
-
(data.length < 15 ? data.length : 0xf)).chr
|
386
|
-
result += binary_plist_obj(data.length) if data.length > 15
|
387
|
-
result += data
|
388
|
-
return result
|
389
|
-
end
|
390
|
-
|
391
|
-
# Determines the minimum number of bytes that is a power of two and can
|
392
|
-
# represent the integer +i+. Raises a RangeError if the number of bytes
|
393
|
-
# exceeds 16. Note that the property list format considers integers of 1,
|
394
|
-
# 2, and 4 bytes to be unsigned, while 8- and 16-byte integers are signed;
|
395
|
-
# thus negative integers will always require at least 8 bytes of storage.
|
396
|
-
def self.min_byte_size(i)
|
397
|
-
if i < 0
|
398
|
-
i = i.abs - 1
|
399
|
-
else
|
400
|
-
if i <= 0xff
|
401
|
-
return 1
|
402
|
-
elsif i <= 0xffff
|
403
|
-
return 2
|
404
|
-
elsif i <= 0xffffffff
|
405
|
-
return 4
|
406
|
-
end
|
407
|
-
end
|
408
|
-
if i <= 0x7fffffffffffffff
|
409
|
-
return 8
|
410
|
-
elsif i <= 0x7fffffffffffffffffffffffffffffff
|
411
|
-
return 16
|
412
|
-
end
|
413
|
-
raise(RangeError, "integer too big - exceeds 128 bits")
|
414
|
-
end
|
415
|
-
|
416
|
-
# Packs an integer +i+ into its binary representation in the specified
|
417
|
-
# number of bytes. Byte order is big-endian. Negative integers cannot be
|
418
|
-
# stored in 1, 2, or 4 bytes.
|
419
|
-
def self.pack_int(i, num_bytes)
|
420
|
-
if i < 0 && num_bytes < 8
|
421
|
-
raise(ArgumentError, "negative integers require 8 or 16 bytes of storage")
|
422
|
-
end
|
423
|
-
case num_bytes
|
424
|
-
when 1
|
425
|
-
[i].pack("c")
|
426
|
-
when 2
|
427
|
-
[i].pack("n")
|
428
|
-
when 4
|
429
|
-
[i].pack("N")
|
430
|
-
when 8
|
431
|
-
[(i >> 32) & 0xffffffff, i & 0xffffffff].pack("NN")
|
432
|
-
when 16
|
433
|
-
[i >> 96, (i >> 64) & 0xffffffff, (i >> 32) & 0xffffffff,
|
434
|
-
i & 0xffffffff].pack("NNNN")
|
435
|
-
else
|
436
|
-
raise(ArgumentError, "num_bytes must be 1, 2, 4, 8, or 16")
|
437
|
-
end
|
438
|
-
end
|
439
|
-
|
440
|
-
def self.combine_ints(num_bits, *ints)
|
441
|
-
i = ints.pop
|
442
|
-
shift_bits = num_bits
|
443
|
-
ints.reverse.each do |i_part|
|
444
|
-
i += i_part << shift_bits
|
445
|
-
shift_bits += num_bits
|
446
|
-
end
|
447
|
-
return i
|
448
|
-
end
|
449
|
-
|
450
|
-
def self.offset_for_index(plist, table_addr, offset_byte_size, index)
|
451
|
-
offset = plist[table_addr + index * offset_byte_size, offset_byte_size]
|
452
|
-
unpack_int(offset)
|
453
|
-
end
|
454
|
-
|
455
|
-
def self.unpack_int(s)
|
456
|
-
case s.length
|
457
|
-
when 1
|
458
|
-
s.unpack("C").first
|
459
|
-
when 2
|
460
|
-
s.unpack("n").first
|
461
|
-
when 4
|
462
|
-
s.unpack("N").first
|
463
|
-
when 8
|
464
|
-
i = combine_ints(32, *(s.unpack("NN")))
|
465
|
-
(i & 0x80000000_00000000 == 0) ?
|
466
|
-
i :
|
467
|
-
-(i ^ 0xffffffff_ffffffff) - 1
|
468
|
-
when 16
|
469
|
-
i = combine_ints(32, *(s.unpack("NNNN")))
|
470
|
-
(i & 0x80000000_00000000_00000000_00000000 == 0) ?
|
471
|
-
i :
|
472
|
-
-(i ^ 0xffffffff_ffffffff_ffffffff_ffffffff) - 1
|
473
|
-
else
|
474
|
-
raise(ArgumentError, "length must be 1, 2, 4, 8, or 16 bytes")
|
475
|
-
end
|
476
|
-
end
|
477
|
-
|
478
|
-
def self.decode_length(plist, offset)
|
479
|
-
if plist[offset] & 0xf == 0xf
|
480
|
-
offset += 1
|
481
|
-
length = decode_binary_plist_obj(plist, offset, 0)
|
482
|
-
offset += min_byte_size(length) + 1
|
483
|
-
return length, offset
|
484
|
-
else
|
485
|
-
return (plist[offset] & 0xf), (offset + 1)
|
486
|
-
end
|
8
|
+
# Adds to_rb functionality to return native ruby types rather than CFTypes
|
9
|
+
module CFPropertyList
|
10
|
+
class CFDictionary
|
11
|
+
def to_rb
|
12
|
+
hash_data = {}
|
13
|
+
value.keys.each do |key|
|
14
|
+
hash_data[key] = value[key].to_hash
|
15
|
+
end
|
16
|
+
hash_data
|
17
|
+
end
|
18
|
+
end
|
19
|
+
class CFType
|
20
|
+
def to_hash
|
21
|
+
self.value
|
22
|
+
end
|
23
|
+
end
|
24
|
+
class CFArray
|
25
|
+
def to_hash
|
26
|
+
hash_data = []
|
27
|
+
value.each do |obj|
|
28
|
+
hash_data << obj.to_hash
|
487
29
|
end
|
30
|
+
hash_data
|
488
31
|
end
|
489
32
|
end
|
490
33
|
end
|
data/lib/ipa_reader/png_file.rb
CHANGED
data/version.txt
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.7.0
|
metadata
CHANGED
@@ -1,70 +1,77 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: ipa_reader
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 6
|
9
|
-
- 1
|
10
|
-
version: 0.6.1
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.7.0
|
5
|
+
prerelease:
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Nicholas Schlueter
|
14
9
|
autorequire:
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
dependencies:
|
21
|
-
- !ruby/object:Gem::Dependency
|
12
|
+
date: 2012-09-11 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
22
15
|
name: zip
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - '='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 2.0.2
|
22
|
+
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
25
|
none: false
|
26
|
-
requirements:
|
27
|
-
- -
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
hash: 11
|
30
|
-
segments:
|
31
|
-
- 2
|
32
|
-
- 0
|
33
|
-
- 2
|
26
|
+
requirements:
|
27
|
+
- - '='
|
28
|
+
- !ruby/object:Gem::Version
|
34
29
|
version: 2.0.2
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: CFPropertyList
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - '='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 2.1.1
|
35
38
|
type: :runtime
|
36
|
-
version_requirements: *id001
|
37
|
-
- !ruby/object:Gem::Dependency
|
38
|
-
name: bones
|
39
39
|
prerelease: false
|
40
|
-
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
41
|
none: false
|
42
|
-
requirements:
|
43
|
-
- -
|
44
|
-
- !ruby/object:Gem::Version
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
42
|
+
requirements:
|
43
|
+
- - '='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 2.1.1
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: bones
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 3.8.0
|
51
54
|
type: :development
|
52
|
-
|
53
|
-
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 3.8.0
|
62
|
+
description: I am using this gem to get version to build the over the air iPhone Ad
|
63
|
+
Hoc distribution plist file.
|
54
64
|
email: schlueter@gmail.com
|
55
|
-
executables:
|
65
|
+
executables:
|
56
66
|
- ipa_reader
|
57
67
|
extensions: []
|
58
|
-
|
59
|
-
extra_rdoc_files:
|
68
|
+
extra_rdoc_files:
|
60
69
|
- History.txt
|
61
|
-
- README.txt
|
62
70
|
- bin/ipa_reader
|
63
|
-
|
64
|
-
|
65
|
-
- .rvmrc
|
71
|
+
files:
|
72
|
+
- .gitignore
|
66
73
|
- History.txt
|
67
|
-
- README.
|
74
|
+
- README.md
|
68
75
|
- Rakefile
|
69
76
|
- bin/ipa_reader
|
70
77
|
- lib/ipa_reader.rb
|
@@ -76,40 +83,31 @@ files:
|
|
76
83
|
- test/MultiG.ipa
|
77
84
|
- test/test_ipa_reader.rb
|
78
85
|
- version.txt
|
79
|
-
|
80
|
-
homepage: http://github.com/schlueter/Ipa-Reader
|
86
|
+
homepage: http://github.com/schlu/Ipa-Reader
|
81
87
|
licenses: []
|
82
|
-
|
83
88
|
post_install_message:
|
84
|
-
rdoc_options:
|
89
|
+
rdoc_options:
|
85
90
|
- --main
|
86
|
-
- README.
|
87
|
-
require_paths:
|
91
|
+
- README.md
|
92
|
+
require_paths:
|
88
93
|
- lib
|
89
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
94
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
90
95
|
none: false
|
91
|
-
requirements:
|
92
|
-
- -
|
93
|
-
- !ruby/object:Gem::Version
|
94
|
-
|
95
|
-
|
96
|
-
- 0
|
97
|
-
version: "0"
|
98
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - ! '>='
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0'
|
100
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
99
101
|
none: false
|
100
|
-
requirements:
|
101
|
-
- -
|
102
|
-
- !ruby/object:Gem::Version
|
103
|
-
|
104
|
-
segments:
|
105
|
-
- 0
|
106
|
-
version: "0"
|
102
|
+
requirements:
|
103
|
+
- - ! '>='
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
107
106
|
requirements: []
|
108
|
-
|
109
107
|
rubyforge_project: ipa_reader
|
110
|
-
rubygems_version: 1.
|
108
|
+
rubygems_version: 1.8.24
|
111
109
|
signing_key:
|
112
110
|
specification_version: 3
|
113
|
-
summary: Reads metadata form iPhone Package Archive Files (ipa)
|
114
|
-
test_files:
|
111
|
+
summary: Reads metadata form iPhone Package Archive Files (ipa).
|
112
|
+
test_files:
|
115
113
|
- test/test_ipa_reader.rb
|
data/.rvmrc
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
rvm 1.8.7@ipa_reader
|