from base64 import b64encode, b64decode
from struct import pack, unpack
class PlistWriter:
def __init__(self):
self.parts = []
self.indention = 0
def emit(self, s):
self.parts.append(s)
def finish(self):
result = '\n'.join(self.parts)
self.parts = []
self.indention = 0
return result
class NSArchiver:
def __init__(self):
self.map = {}
self.writer = PlistWriter()
self.refs = []
def _archive(self, val):
if isinstance(val, Ref):
idx = None
if val in self.map:
idx = self.map[val]
else:
idx = len(self.refs)
self.map[val] = idx
self.refs.append(val)
self.writer.emit('')
self.writer.emit('CF$UID')
self.writer.emit('{}'.format(idx))
self.writer.emit('')
elif val is True:
self.writer.emit('')
elif val is False:
self.writer.emit('')
elif isinstance(val, str):
self.writer.emit('{}'.format(val))
elif isinstance(val, bytes):
b64 = b64encode(val).decode('ascii')
self.writer.emit('{}'.format(b64))
elif isinstance(val, int):
self.writer.emit('{}'.format(val))
elif isinstance(val, float):
self.writer.emit('{}'.format(val))
elif isinstance(val, list):
self.writer.emit('')
for e in val:
self._archive(e)
self.writer.emit('')
elif isinstance(val, dict):
self.writer.emit('')
for k, v in val.items():
assert(isinstance(k, str))
self.writer.emit('{}'.format(k))
self._archive(v)
self.writer.emit('')
else:
raise Exception("Cannot serialize value of type {}".format(type(val)))
def archive(self, val):
assert(isinstance(val, Ref))
self.writer.emit('')
self.writer.emit('')
self.writer.emit('')
self.writer.emit('')
self.writer.emit(' $archiver')
self.writer.emit(' NSKeyedArchiver')
self.writer.emit(' $objects')
self.writer.emit(' ')
idx = 0
self.refs = [null, val]
while idx < len(self.refs):
r = self.refs[idx]
self.map[r] = idx
self._archive(r.v)
idx += 1
self.writer.emit('')
self.writer.emit('$top')
self.writer.emit('')
self.writer.emit('root')
self.writer.emit('')
self.writer.emit('CF$UID')
self.writer.emit('1')
self.writer.emit('')
self.writer.emit('')
self.writer.emit('$version')
self.writer.emit('100000')
self.writer.emit('')
self.writer.emit('')
return self.writer.finish()
class Ref:
def __init__(self, v):
assert(not isinstance(v, Ref))
self.v = v
def __hash__(self):
return id(self)
def __cmp__(self, other):
return id(self) == id(other)
def __str__(self):
return "ref({})".format(self.v)
# Converts the agument into a reference type.
def ref(obj):
return Ref(obj)
def cls(hierarchy):
return ref({
"$classes": hierarchy,
"$classname": hierarchy[0]
})
def nsstring(content):
return ref({
'$class': nsstring_class,
'NS.string': content,
})
def nsmutablestring(content):
return ref({
'$class': nsmutablestring_class,
'NS.string': content,
})
def nsdictionary(vals):
return ref({
'$class': nsdictionary_class,
'NS.keys': list(vals.keys()),
'NS.objects': list(vals.values()),
})
def nsdata(content):
return ref({
'$class': nsdata_class,
'NS.data': content,
})
def nsmutabledata(content):
return ref({
'$class': nsmutabledata_class,
'NS.data': content,
})
def nsarray(vals):
return ref({
'$class': nsarray_class,
'NS.objects': vals
})
def nsmutarray(vals):
return ref({
'$class': nsmutarray_class,
'NS.objects': vals
})
def old_style_array(vals, typeid, elemsize):
d = {
'$class': nskeyed_coder_old_style_array_class,
'NS.count': len(vals),
'NS.size': elemsize,
'NS.type': typeid
}
for i, v in enumerate(vals):
d['${}'.format(i)] = v
return ref(d)
null = ref("$null")
nsstring_class = cls(["NSString", "NSObject"])
nsmutablestring_class = cls(["NSMutableString", "NSString", "NSObject"])
ac_zeroing_string = cls(["ACZeroingString", "NSString", "NSObject"])
nsdata_class = cls(["NSData", "NSObject"])
nsmutabledata_class = cls(["NSMutableData", "NSData", "NSObject"])
nsvalue_class = cls(["NSValue", "NSObject"])
nskeyed_coder_old_style_array_class = cls(["_NSKeyedCoderOldStyleArray", "NSObject"])
nsarray_class = cls(["NSArray", "NSObject"])
nsdictionary_class = cls(["NSDictionary", "NSObject"])
nsmutarray_class = cls(["NSMutableArray", "NSArray", "NSObject"])
pfarray_class = cls(["_PFArray", "NSArray", "NSObject"])
shared_key_dict_class = cls(["NSSharedKeyDictionary", "NSMutableDictionary", "NSDictionary", "NSObject"])
shared_key_set_class = cls(["NSSharedKeySet", "NSObject"])
cs_localized_string_class = cls(["CSLocalizedString", "NSString", "NSObject"])
ns_localized_string_class = cls(["__NSLocalizedString", "NSMutableString", "NSString", "NSObject"])