einstein/einsteingame/resources.py

220 lines
6.5 KiB
Python

from os import listdir
import struct
import io
import zlib
from .buffer import Buffer
from .i18n import splitFileName, getScore, locale
class ResourceError(Exception):
def __init__(self, code=None):
self.code = code
class ResVariant(object):
def __init__(self, resFile, i18nScore, dirEntry):
self.__file = resFile
self.__i18nScore = i18nScore
self.__offset = dirEntry['offset']
self.__unpackedSize = dirEntry['unpackedSize']
self.__packedSize = dirEntry['packedSize']
self.__level = dirEntry['level']
self.__refCnt = 0
self.__data = None
@property
def i18nScore(self):
return self.__i18nScore
def getData(self):
buf = Buffer()
buf.setSize(self.__unpackedSize)
if self.__refCnt == 0:
self.__data = self.__file.load(self.__offset, self.__packedSize, self.__unpackedSize, self.__level)
return self.__data
class Resource(object):
def __init__(self, file, i18nScore, entry, name):
self.__variants = []
self.__name = name
self.addVariant(file, i18nScore, entry)
def addVariant(self, file, i18nScore, entry):
if len(self.__variants) == 0:
self.__variants.append(ResVariant(file, i18nScore, entry))
return
try:
best_variant = [idx for idx, variant in enumerate(self.__variants) \
if variant.i18nScore == i18nScore][0]
self.__variants[best_variant] = ResVariant(file, i18nScore, entry)
except KeyError:
self.__variants.append(ResVariant(file, i18nScore, entry))
self.__variants.sort(key=lambda variant: variant.getI18nScore())
def getVariantsCount(self):
return len(self.__variants)
@property
def variants(self):
return self.__variants
def getName(self):
return self.__name
class ResourceFile(object):
def __init__(self, fileName, buf=None):
self.__name = fileName
if buf is not None:
self.__buffer = buf
self.__ownBuffer = False
else:
self.__buffer = Buffer()
self.__ownBuffer = True
try:
self.__stream = open(self.__name, 'rb')
sign = self.__stream.read(4)
if sign != b'CRF\x00':
raise ResourceError(code='invalid')
major = self.readInt()
minor = self.readInt()
priority = self.readInt()
if major != 2 or minor < 0:
raise ResourceError(code='incompatible')
except ResourceError as e:
raise Exception("Invalid resource file: {}".format(e.code))
except IOError as e:
raise Exception("Error loading resource file '{}': {}".format(self.__name, e.message))
def load(self, offset, packedSize, unpackedSize, level):
try:
if level == 0:
self.__stream.seek(offset, io.SEEK_SET)
self.__stream.read()
return self.__stream.read(unpackedSize)
self.__stream.seek(offset, io.SEEK_SET)
data = self.__stream.read(packedSize)
return self.unpack(data)
except Exception as e:
print("Eror loading resource: {}".format(e))
raise
# void ResourceFile::unpack(char *in, int inSize, char *out, int outSize)
def unpack(self, data):
return zlib.decompress(data)
def readInt(self):
return struct.unpack('i', self.__stream.read(4))[0]
def readString(self):
string = b''
while True:
b = self.__stream.read(1)
if b == b'\x00' or b == b'':
break
string += b
return string.decode('ascii')
def getDirectory(self):
try:
self.__stream.seek(-8, io.SEEK_END)
except IOError:
raise Exception("Error reading {} directory".format(self.__name))
start = self.readInt()
count = self.readInt()
try:
self.__stream.seek(start, io.SEEK_SET)
except IOError:
raise Exception("Error reading {} directory".format(self.__name))
directory = []
try:
for i in range(0, count):
directory.append({
"name": self.readString(),
"unpackedSize": self.readInt(),
"offset": self.readInt(),
"packedSize": self.readInt(),
"level": self.readInt(),
"group": self.readString(),
})
except IOError:
raise Exception("Error reading {} directory".format(self.__name))
return directory
class ResourcesCollection:
def __init__(self, directories):
self.__files = []
self.__buffer = None
self.__resources = {}
self.__groups = {}
self.loadResourceFiles(directories)
self.processFiles()
def __iter__(self):
return iter(self.__resources.values())
def loadResourceFiles(self, directories):
for directory in directories:
try:
for name in listdir(directory):
if not name.startswith('.') and name.endswith('.res'):
self.__files.append(
ResourceFile(directory + '/' + name,
buf=self.__buffer))
except OSError:
pass
def processFiles(self):
for res_file in self.__files:
directory = res_file.getDirectory()
for de in directory:
name, ext, language, country = splitFileName(de['name'])
score = getScore(language, country, locale)
if score > 0:
resName = name + '.' + ext
res = self.__resources.get(resName, None)
if not res:
res = Resource(res_file, score, de, resName)
self.__resources[resName] = res
if len(de['group']) > 0:
if de['group'] not in self.__groups:
self.__groups[de['group']] = []
self.__groups[de['group']].append(res)
else:
res.addVariant(res_file, score, de)
directory.clear()
def forEachInGroup(self, group, func):
for resource in self.__groups[group]:
func(resource)
resources = None