Skip to content

Instantly share code, notes, and snippets.

@admalledd
Created March 13, 2014 06:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save admalledd/9522639 to your computer and use it in GitHub Desktop.
Save admalledd/9522639 to your computer and use it in GitHub Desktop.
'''Read SValue object streams and convert to XML'''
import struct
class SValueType:
Array =1
Object =2
Boolean =3
Double =4
Float =5
Integer =6
String =7
IntegerArray =8
Vector2 =9
Null =0
class Vector2(object):
"""docstring for Vector2"""
def __init__(self, x,y):
self.x=x
self.y=y
class SValue(object):
def __init__(self,val,typ):
'''called from the classmethods, should never be called by hand!
SValues are a complex tree in memory, exportable as XML or .bin
All of the @staticmethod Read* methods advance the file object.
'''
self.val=val
self.type=typ
@staticmethod
def ReadInt32(f):
'''read 4 bytes and convert to an int (from int32)'''
i=struct.unpack("<i",f.read(4))[0]
#print "reading int of size '%s'"%i
return i
@staticmethod
def ReadSingle(f):
'''read 4 byte float'''
i = struct.unpack("<f",f.read(4))[0]
return i
@staticmethod
def ReadDouble(f):
'''read 8 byte float (double)'''
i = struct.unpack("<d",f.read(8))[0]
return i
@staticmethod
def ReadBoolean(f):
'''read one byte, true if non-zero'''
i = f.read(1)
return i!='\x00'
@staticmethod
def ReadString(f):
'''Read a LEB128 string'''
strlen=SValue.leb128_decode(f)
#print "reading string of len(%s)"%strlen
s=f.read(strlen)
#print "string: %r"%s
return s
@staticmethod
def leb128_decode(f):
'decode LEB128 numbers, see http://en.wikipedia.org/wiki/LEB128'
result = 0
shift = 0
size = 0
buff = f.read(1)
while True:
b = ord(buff[size:size+1])
size += 1
result |= (b & 0x7f) << shift
if b & 0x80 == 0:
break
shift += 7
print "looped in leb128_decode"
buff=buff+f.read(1)
#print "LEB128 buffer: '0x%s'"%buff.encode('hex')
return result#, size
@classmethod
def FromArray(cls,val):
return cls(val, SValueType.Array)
@classmethod
def FromObject(cls,val):
return cls(val, SValueType.Object)
@classmethod
def FromBoolean(cls,val):
return cls(val, SValueType.Boolean)
@classmethod
def FromDouble(cls,val):
return cls(val, SValueType.Double)
@classmethod
def FromFloat(cls,val):
return cls(val, SValueType.Float)
@classmethod
def FromInteger(cls,val):
return cls(val, SValueType.Integer)
@classmethod
def FromString(cls,val):
return cls(val, SValueType.String)
@classmethod
def FromIntegerArray(cls,val):
return cls(val, SValueType.IntegerArray)
@classmethod
def FromVector2(cls,val):
return cls(val, SValueType.Vector2)
@classmethod
def LoadStream(cls,f):
b=struct.unpack('<b',f.read(1))[0]#read one and switch!
#print "loading Stream with type '%s'"%(hex(b))
if b == SValueType.Array:
arr=[]
arr_len = cls.ReadInt32(f)
for i in range(arr_len):
arr.append(cls.LoadStream(f))
return cls.FromArray(arr)
elif b == SValueType.Object:
val={}
for i in range(cls.ReadInt32(f)):
key=cls.ReadString(f)
val[key]=cls.LoadStream(f)
return SValue.FromObject(val)
elif b == SValueType.Boolean:
return SValue.FromBoolean(cls.ReadBoolean(f))
elif b == SValueType.Double:
return SValue.FromDouble(cls.ReadDouble(f))
elif b == SValueType.Float:
return SValue.FromFloat(cls.ReadSingle(f))
elif b == SValueType.Integer:
return SValue.FromInteger(cls.ReadInt32(f))
elif b == SValueType.String:
return SValue.FromString(cls.ReadString(f))
elif b == SValueType.IntegerArray:
arr=[]
for i in range(cls.ReadInt32(f)):
arr.append(cls.ReadInt32(f))
return SValue.FromIntegerArray(arr)
elif b == SValueType.Vector2:
return SValue.FromVector2(Vector2(cls.ReadSingle(f), cls.ReadSingle(f)));
else:
#catches case b==0 and errors
return SValue(None, SValueType.Null)
def SaveXML(self,f,indent='',name=None,compact=False):
'''save to XML, recurses into itself!'''
def fmt_name(t):
'''formats xml type "t" with name if needed'''
if name:
return '<%s name="%s">'%(t,name)
else:
return '<%s>'%t
if not compact:
f.write(indent)
if self.type == SValueType.Array:
f.write(fmt_name('array'))
compact1=compact
if len(self.val) <10 and not compact1:
compact1=True
for val in self.val:
if val.type in (SValueType.Object,SValueType.IntegerArray,SValueType.Array):
compact1=False
if not compact1:
f.write('\r\n')
for val in self.val:
val.SaveXML(f,indent+'\t',None,compact1)
if not compact:
f.write(indent)
f.write('</array>')
elif self.type == SValueType.Object:
f.write(fmt_name('dictionary'))
if not compact:
f.write('\r\n')
for key,val in self.val.iteritems():
val.SaveXML(f,indent+'\t',key,compact)
if not compact:
f.write('\r\n'+indent)
f.write('</dictionary>')
elif self.type == SValueType.Boolean:
f.write(fmt_name('bool'))
f.write(repr(self.val))
f.write('</bool>')
elif self.type == SValueType.Double:
f.write(fmt_name('double'))
f.write(repr(self.val))#TODO::: CHECK
f.write('</double>')
elif self.type == SValueType.Float:
f.write(fmt_name('float'))
f.write(repr(self.val))#TODO::: CHECK
f.write('</float>')
elif self.type == SValueType.Integer:
f.write(fmt_name('int'))
f.write(str(self.val))#TODO::: CHECK
f.write('</int>')
elif self.type == SValueType.String:
f.write(fmt_name('string'))
f.write(str(self.val))#TODO::: CHECK
f.write('</string>')
elif self.type == SValueType.IntegerArray:
f.write(fmt_name('int-arr'))
self.sval=[]
for val in self.val:
self.sval.append(str(val))
s=" ".join(self.val)
f.write(s)
f.write('</int-arr>')
elif self.type == SValueType.Vector2:
f.write(fmt_name('vec2'))
f.write(repr(self.val.x)+' '+repr(self.val.y))#TODO::: CHECK
f.write('</vec2>')
elif self.type == SValueType.Null:
if name:
f.write('<null name="%s"/>')
else:
f.write('<null/>')
else:
raise Exception("missing/bad encoding type!")
if compact:
return
else:
f.write('\r\n')
if __name__ == '__main__':
sv= SValue.LoadStream(open("survival.hwm.unpacked/levels/level_1.xml.bin"))
sv.SaveXML(open("survival.hwm.unpacked/levels/level_1.xml",'wb'))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment