diff --git a/einsteingame/buffer.py b/einsteingame/buffer.py index 62f6638..b21ec5f 100644 --- a/einsteingame/buffer.py +++ b/einsteingame/buffer.py @@ -1,2 +1,10 @@ class Buffer(object): - pass + def __init__(self): + self.__size = 0 + self.__data = '' + + def setSize(self, size): + self.__size = size + + def getData(self): + return self.__data diff --git a/einsteingame/formatter.py b/einsteingame/formatter.py new file mode 100644 index 0000000..f86dc55 --- /dev/null +++ b/einsteingame/formatter.py @@ -0,0 +1,97 @@ +import struct +from enum import Enum + +class Formatter(object): + class CmdType(Enum): + EMPTY_CMD = 0 + TEXT_COMMAND = 1 + INT_ARG = 2 + STRING_ARG = 3 + DOUBLE_ARG = 4 + FLOAT_ARG = 5 + + def __init__(self, data, offset): + cnt = struct.unpack('i', data[offset:offset + 4])[0] + + if cnt == 0: + commandsCnt = argsCnt = 0 + commands = NULL + args = NULL + + offset += 4 + commands = [] + commandsCnt = 0 + + maxArg = 0 + argNo = None + + for i in range(0, cnt): + typ = data[offset] + offset += 1 + size = struct.unpack('i', data[offset:offset + 4])[0] + offset += 4 + + if typ == 1: + commands.append({ + "type": self.CmdType.TEXT_COMMAND, + "data": data[offset:offset + size].decode('utf-8'), + }) + elif typ == 2: + argNo = struct.unapack('i', data[offset:offset + 4])[0] + + if argNo > maxArg: + maxArg = argNo + + commands.append({ + "type": self.CmdType.INT_ARG, + "data": argNo, + }) + elif typ == 3: + argNo = struct.unapack('i', data[offset:offset + 4])[0] + + if argNo > maxArg: + maxArg = argNo + + commands.append({ + "type": self.CmdType.STRING_ARG, + "data": argNo, + }) + elif typ == 3: + argNo = struct.unapack('i', data[offset:offset + 4])[0] + + if argNo > maxArg: + maxArg = argNo + + commands.append({ + "type": self.CmdType.FLOAT_ARG, + "data": argNo, + }) + elif typ == 3: + argNo = struct.unapack('i', data[offset:offset + 4])[0] + + if argNo > maxArg: + maxArg = argNo + + commands.append({ + "type": self.CmdType.DOUBLE_ARG, + "data": argNo, + }) + + offset += size; + + argsCnt = maxArg + + if argsCnt == 0: + args = None + else: + args = [] + + for i in range(0, len(commands)): + args.append[0] + + for i in range(0, len(commands)): + c = commands[i] + + if c.type == INT_ARG or c.type == STRING_ARG or c.type == FLOAT_ARG or c.type == DOUBLE_ARG: + no = c.data + args[no - 1] = c.type diff --git a/einsteingame/i18n.py b/einsteingame/i18n.py index aa88ba7..5047d5f 100644 --- a/einsteingame/i18n.py +++ b/einsteingame/i18n.py @@ -2,10 +2,21 @@ from locale import setlocale, LC_ALL, LC_NUMERIC class Locale(object): def __init__(self): + self.__country = None + self.__language = None + self.parseLocale(setlocale(LC_ALL, "")) # hack because of numbers in Lua setlocale(LC_NUMERIC, "C") + @property + def country(self): + return self.__country + + @property + def language(self): + return self.__language + def parseLocale(self, name): pos = name.find('.') @@ -21,17 +32,16 @@ class Locale(object): pos = langAndCountry.find('_') if pos < 0: - language = langAndCountry - country = "" + self.__language = langAndCountry + self.__country = '' else: - language = langAndCountry[0:pos] - country = langAndCountry[pos + 1:] + self.__language = langAndCountry[0:pos] + self.__country = langAndCountry[pos + 1:] - language = language.lower() - country = country.lower() + self.__language = self.__language.lower() + self.__country = self.__country.lower() encoding = encoding.upper() - def splitFileName(fileName): """ Returns tuple of name, extension, lang, country @@ -87,10 +97,10 @@ def getScore(lang, country, locale): score = 0 - if locale.getCountry() == country: + if locale.country == country: score += 2 - if locale.getLanguage() == lang: + if locale.language == lang: score += 4 return score diff --git a/einsteingame/main.py b/einsteingame/main.py index 084dbc0..047c9f8 100644 --- a/einsteingame/main.py +++ b/einsteingame/main.py @@ -11,7 +11,8 @@ gi.require_version('Clutter', '1.0') from gi.repository import Clutter from gi.repository import GLib -from .resources import ResourcesCollection +from .messages import msg +from .resources import ResourcesCollection, resources from .utils import ensureDirExists win32 = False @@ -19,8 +20,7 @@ apple = False developer = True PREFIX = '/usr' -# Clutter.init() -# + # stage = Clutter.Stage() # stage.connect('destroy', lambda x: Clutter.main_quit()) # stage.title = 'Test' @@ -48,6 +48,21 @@ PREFIX = '/usr' # stage.show() # Clutter.main() +stage = None +sound = None + +def initScreen(): + Clutter.init() + + stage = Clutter.Stage() + stage.connect('destroy', lambda x: Clutter.main_quit()) + stage.set_size(800, 600) + stage.title = 'Einstein' + +def initAudio(): + # sound = Sound() + pass + def loadResources(selfPath): dirs = [] @@ -65,7 +80,7 @@ def loadResources(selfPath): dirs.append("res") dirs.append(".") resources = ResourcesCollection(dirs) - msg.load() + msg.load(resources) def main(script, *args): ensureDirExists(os.environ["HOME"] + "/.einstein") @@ -73,7 +88,5 @@ def main(script, *args): loadResources(sys.argv[0]) initScreen() initAudio() - menu() - getStorage().flush() - - # screen.doneCursors(); + # menu() + # getStorage().flush() diff --git a/einsteingame/messages.py b/einsteingame/messages.py new file mode 100644 index 0000000..96eac5d --- /dev/null +++ b/einsteingame/messages.py @@ -0,0 +1,66 @@ +import struct + +from .buffer import Buffer +from .formatter import Formatter + +class Messages(object): + def __init__(self): + self.__messages = {} + + def load(self, resources): + buf = Buffer() + + resources.forEachInGroup('messages', lambda res: self.loadFromResource(res, buf)) + + def loadFromResource(self, resource, buf): + if not resource: + return + + cnt = resource.getVariantsCount() + + for variant in resource.variants: + if variant: + try: + score = variant.i18nScore + data = variant.getData() + self.loadBundle(score, data) + except Exception as e: + print("Error loading text bundle {}: {}".format(resource.getName(), e)) + + raise + + def loadBundle(self, score, data): + if data[0:3] != b"CMF": + raise Exception("Invalid format of message file!") + + version = struct.unpack('i', data[3:7])[0] + if version != 1: + raise Exception("Unknown version of message file!") + + offset = struct.unpack('i', data[len(data) - 4:])[0] + cnt = struct.unpack('i', data[offset:offset + 4])[0] + offset += 4 + + for i in range(0, cnt): + sz = struct.unpack('i', data[offset:offset + 4])[0] + offset += 4; + + if sz > 0: + name = data[offset:offset + sz].decode('utf-8') + msgOffset = struct.unpack('i', data[offset + sz:offset + sz + 4])[0] + + if name in self.__messages: + ss = self.__messages[name] + if ss['score'] <= score: + ss['score'] = score + ss['message'] = Formatter(data, msgOffset) + else: + self.__messages[name] = { + "score": score, + "message": Formatter(data, msgOffset), + } + + offset += sz + 4 + + +msg = Messages() diff --git a/einsteingame/resources.py b/einsteingame/resources.py index c5b5f2d..f3ef771 100644 --- a/einsteingame/resources.py +++ b/einsteingame/resources.py @@ -1,6 +1,7 @@ from os import listdir import struct import io +import zlib from .buffer import Buffer from .i18n import splitFileName, getScore, locale @@ -9,16 +10,73 @@ 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: - buffer = buf - ownBuffer = False + self.__buffer = buf + self.__ownBuffer = False else: - buffer = Buffer() - ownBuffer = True + self.__buffer = Buffer() + self.__ownBuffer = True try: self.__stream = open(self.__name, 'rb') @@ -37,6 +95,27 @@ class ResourceFile(object): 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] @@ -55,7 +134,7 @@ class ResourceFile(object): def getDirectory(self): try: - self.__stream.seek(-8, io.SEEK_END); + self.__stream.seek(-8, io.SEEK_END) except IOError: raise Exception("Error reading {} directory".format(self.__name)) @@ -63,7 +142,7 @@ class ResourceFile(object): count = self.readInt() try: - self.__stream.seek(start, io.SEEK_SET); + self.__stream.seek(start, io.SEEK_SET) except IOError: raise Exception("Error reading {} directory".format(self.__name)) @@ -89,9 +168,13 @@ class ResourcesCollection: 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: @@ -119,9 +202,18 @@ class ResourcesCollection: res = Resource(res_file, score, de, resName) self.__resources[resName] = res - if len(de.group) > 0: - groups[de.group].append(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(file, score, de) + res.addVariant(res_file, score, de) directory.clear() + + def forEachInGroup(self, group, func): + for resource in self.__groups[group]: + func(resource) + +resources = None diff --git a/einsteingame/screen.py b/einsteingame/screen.py new file mode 100644 index 0000000..f984633 --- /dev/null +++ b/einsteingame/screen.py @@ -0,0 +1,8 @@ +class Screen(object): + def __init__(self): + self.__screen = None + self.__mouseImage = None + self.__mouseSave = None + self.__mouseVisible = None + self.__regionsList = None + self.__maxRegionsList = 0