mirror of
https://github.com/SickGear/SickGear.git
synced 2024-12-01 00:43:37 +00:00
199 lines
6.6 KiB
Python
199 lines
6.6 KiB
Python
"""
|
|
Unreal 4 .uasset file parser
|
|
|
|
Author: Robert Xiao
|
|
Creation date: 2015-01-17
|
|
"""
|
|
|
|
from hachoir_parser import Parser
|
|
from hachoir_core.field import (FieldSet, StaticFieldSet, SeekableFieldSet, Int32, UInt32,
|
|
String, PascalString32, PaddingBytes, Bytes, RawBytes)
|
|
from hachoir_core.endian import LITTLE_ENDIAN
|
|
|
|
class StringTable(FieldSet):
|
|
def __init__(self, parent, name, count, *args):
|
|
FieldSet.__init__(self, parent, name, *args)
|
|
self.count = count
|
|
|
|
def createFields(self):
|
|
for i in xrange(self.count):
|
|
yield PascalString32(self, "string[]", strip='\0')
|
|
|
|
def getObject(self, val):
|
|
if val == 0:
|
|
return None
|
|
elif val < 0:
|
|
return self['/header/refs/ref[%d]' % (-val-1)]
|
|
else:
|
|
return self['/header/assets/asset[%d]' % (val-1)]
|
|
|
|
|
|
class AssetHeader(FieldSet):
|
|
def createFields(self):
|
|
yield Int32(self, "type1")
|
|
yield Int32(self, "type2")
|
|
yield Int32(self, "parent") # 0 = no parent
|
|
yield Int32(self, "name_index")
|
|
yield Int32(self, "unk[]")
|
|
yield Int32(self, "unk[]")
|
|
yield Int32(self, "size")
|
|
yield Int32(self, "offset")
|
|
yield Int32(self, "unk[]")
|
|
yield Int32(self, "unk[]")
|
|
yield Int32(self, "unk[]")
|
|
yield Int32(self, "unk[]")
|
|
yield Int32(self, "unk[]")
|
|
yield Int32(self, "unk[]")
|
|
yield Int32(self, "unk[]")
|
|
yield Int32(self, "unk[]")
|
|
yield Int32(self, "unk[]")
|
|
|
|
@property
|
|
def typeName(self):
|
|
return getObject(self, self["type1"].value).objectName
|
|
|
|
@property
|
|
def objectName(self):
|
|
name_index = self['name_index'].value
|
|
return self['/header/strings/string[%d]' % name_index].value
|
|
|
|
@property
|
|
def fullObjectName(self):
|
|
name = self.objectName
|
|
if self['parent'].value:
|
|
name = '%s.%s' % (getObject(self, self['parent'].value).fullObjectName, name)
|
|
return name
|
|
|
|
def createValue(self):
|
|
return '<Asset %s of type %s, size %d>' % (
|
|
self.fullObjectName, self.typeName, self['size'].value)
|
|
|
|
def createDescription(self):
|
|
return str([t.value for t in self.array('unk')])
|
|
|
|
class AssetTable(FieldSet):
|
|
def __init__(self, parent, name, count, *args):
|
|
FieldSet.__init__(self, parent, name, *args)
|
|
self.count = count
|
|
|
|
def createFields(self):
|
|
for i in xrange(self.count):
|
|
yield AssetHeader(self, "asset[]")
|
|
|
|
class ReferenceHeader(FieldSet):
|
|
def createFields(self):
|
|
yield Int32(self, "unk[]")
|
|
yield Int32(self, "unk[]")
|
|
yield Int32(self, "type_index")
|
|
yield Int32(self, "unk[]")
|
|
yield Int32(self, "parent")
|
|
yield Int32(self, "name_index")
|
|
yield Int32(self, "unk[]")
|
|
|
|
@property
|
|
def typeName(self):
|
|
type_index = self['type_index'].value
|
|
return self['/header/strings/string[%d]' % type_index].value
|
|
|
|
@property
|
|
def objectName(self):
|
|
name_index = self['name_index'].value
|
|
return self['/header/strings/string[%d]' % name_index].value
|
|
|
|
@property
|
|
def fullObjectName(self):
|
|
name = self.objectName
|
|
if self['parent'].value:
|
|
name = '[%s].%s' % (getObject(self, self['parent'].value).fullObjectName, name)
|
|
return name
|
|
|
|
def createValue(self):
|
|
return '<Reference %s of type %s>' % (self.fullObjectName, self.typeName)
|
|
|
|
def createDescription(self):
|
|
return str([t.value for t in self.array('unk')])
|
|
|
|
class ReferenceTable(FieldSet):
|
|
def __init__(self, parent, name, count, *args):
|
|
FieldSet.__init__(self, parent, name, *args)
|
|
self.count = count
|
|
|
|
def createFields(self):
|
|
for i in xrange(self.count):
|
|
yield ReferenceHeader(self, "ref[]")
|
|
|
|
|
|
|
|
class UAssetHeader(SeekableFieldSet):
|
|
def __init__(self, *args):
|
|
SeekableFieldSet.__init__(self, *args)
|
|
self._size = self["header_size"].value * 8
|
|
|
|
def createFields(self):
|
|
yield UInt32(self, "magic")
|
|
yield Int32(self, "version")
|
|
yield RawBytes(self, "unk[]", 16)
|
|
yield UInt32(self, "header_size")
|
|
yield PascalString32(self, "none", strip='\0')
|
|
yield RawBytes(self, "unk[]", 4)
|
|
|
|
yield UInt32(self, "num_strings", "Number of strings in the header")
|
|
yield UInt32(self, "offset_strings", "Offset to string table within the header")
|
|
yield UInt32(self, "num_assets", "Number of assets described in the header")
|
|
yield UInt32(self, "offset_assets", "Offset to asset table within the header")
|
|
yield UInt32(self, "num_refs", "Number of references? described in the header")
|
|
yield UInt32(self, "offset_refs", "Offset to reference table within the header")
|
|
|
|
yield UInt32(self, "offset_unk[]", "Offset to something")
|
|
yield UInt32(self, "unk[]")
|
|
yield UInt32(self, "offset_unk[]", "Offset to some other thing")
|
|
yield UInt32(self, "unk[]")
|
|
|
|
yield RawBytes(self, "signature", 16, "Some kind of hash")
|
|
|
|
yield UInt32(self, "unk[]")
|
|
yield UInt32(self, "num_assets2", "num_assets again")
|
|
assert self['num_assets'].value == self['num_assets2'].value
|
|
yield UInt32(self, "num_strings2", "num_strings again")
|
|
assert self['num_strings'].value == self['num_strings2'].value
|
|
yield RawBytes(self, "unk[]", 34)
|
|
yield UInt32(self, "unk[]")
|
|
yield UInt32(self, "size_unk", "Size of something")
|
|
yield RawBytes(self, "unk[]", 12)
|
|
|
|
self.seekByte(self["offset_strings"].value)
|
|
yield StringTable(self, "strings", self["num_strings"].value)
|
|
|
|
self.seekByte(self["offset_assets"].value)
|
|
yield AssetTable(self, "assets", self["num_assets"].value)
|
|
|
|
self.seekByte(self["offset_refs"].value)
|
|
yield ReferenceTable(self, "refs", self["num_refs"].value)
|
|
|
|
class Asset(FieldSet):
|
|
def createFields(self):
|
|
yield UInt32(self, "type")
|
|
|
|
class UAssetFile(Parser):
|
|
MAGIC = "\xc1\x83\x2a\x9e"
|
|
PARSER_TAGS = {
|
|
"id": "uasset",
|
|
"category": "game",
|
|
"description": "Unreal .uasset file",
|
|
"min_size": 32,
|
|
"file_ext": (".uasset",),
|
|
"magic": ((MAGIC, 0),),
|
|
}
|
|
endian = LITTLE_ENDIAN
|
|
|
|
def validate(self):
|
|
temp = self.stream.readBytes(0, 4)
|
|
if temp != self.MAGIC:
|
|
return "Wrong header"
|
|
return True
|
|
|
|
def createFields(self):
|
|
yield UAssetHeader(self, "header")
|
|
for asset in self['/header/assets'].array('asset'):
|
|
self.seekByte(asset['offset'].value)
|
|
yield RawBytes(self, "asset[]", asset['size'].value, description="Data for asset %s" % asset.fullObjectName)
|