diff --git a/__init__.py b/einsteingame/__init__.py similarity index 100% rename from __init__.py rename to einsteingame/__init__.py diff --git a/buffer.py b/einsteingame/buffer.py similarity index 100% rename from buffer.py rename to einsteingame/buffer.py diff --git a/einsteingame/i18n.py b/einsteingame/i18n.py new file mode 100644 index 0000000..833853b --- /dev/null +++ b/einsteingame/i18n.py @@ -0,0 +1,94 @@ +class Locale(object): + def __init__(self): + self.parseLocale(setlocale(LC_ALL, "")) + # hack because of numbers in Lua + setlocale(LC_NUMERIC, "C") + + def parseLocale(name): + pos = name.find('.') + + langAndCountry = None + + if pos >= 0: + encoding = name[pos + 1:] + langAndCountry = name[0:pos] + else: + encoding = "" + langAndCountry = name + + pos = langAndCountry.find('_') + + if pos < 0: + language = langAndCountry + country = "" + else: + language = langAndCountry[0:pos] + country = langAndCountry[pos + 1:] + + language = language.lower() + country = country.lower() + encoding = encoding.upper() + + +def splitFileName(fileName): + """ + Returns tuple of name, extension, lang, country + """ + + pos = fileName.rfind('.') + + if pos <= 0: + ext = ""; + name = fileName + else: + name = fileName[0:pos] + ext = fileName[pos + 1:] + + pos = name.rfind('_') + + if pos <= 0 or len(name) - pos != 3: + lang = "" + country = "" + else: + s = name[0:pos] + l = name[pos + 1:] + + if l.isupper(): + name = s + country = l + pos = name.rfind('_') + + if pos <= 0 or len(name) - pos != 3: + lang = "" + else: + l = name[pos + 1:] + s = name[0:pos] + + if l.islower(): + name = s + lang = l + else: + lang = "" + elif l.islower(): + name = s + lang = l + country = "" + else: + lang = "" + country = "" + + return name, ext, lang, country + +def getScore(lang, country, locale): + if len(country) == 0 and len(locale) == 0: + return 1 + + score = 0 + + if (locale.getCountry().length() && (locale.getCountry() == country)) + score += 2; + if (locale.getLanguage().length() && (locale.getLanguage() == lang)) + score += 4; + + return score; +} diff --git a/main.py b/einsteingame/main.py old mode 100755 new mode 100644 similarity index 94% rename from main.py rename to einsteingame/main.py index 37918d7..084dbc0 --- a/main.py +++ b/einsteingame/main.py @@ -11,8 +11,8 @@ gi.require_version('Clutter', '1.0') from gi.repository import Clutter from gi.repository import GLib -from resources import ResourcesCollection -from utils import ensureDirExists +from .resources import ResourcesCollection +from .utils import ensureDirExists win32 = False apple = False @@ -67,7 +67,7 @@ def loadResources(selfPath): resources = ResourcesCollection(dirs) msg.load() -if __name__ == '__main__': +def main(script, *args): ensureDirExists(os.environ["HOME"] + "/.einstein") loadResources(sys.argv[0]) diff --git a/einsteingame/resources.py b/einsteingame/resources.py new file mode 100644 index 0000000..742da6e --- /dev/null +++ b/einsteingame/resources.py @@ -0,0 +1,125 @@ +from os import listdir +import struct +import io + +from .buffer import Buffer +from .i18n import splitFileName, getScore + +class ResourceError(Exception): + def __init__(self, code=None): + self.code = code + +class ResourceFile(object): + def __init__(self, fileName, buf=None): + self.__name = fileName + + if buf is not None: + buffer = buf + ownBuffer = False + else: + buffer = Buffer() + 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 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.loadResourceFiles(directories) + self.processFiles() + + 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 = resources[resName]; + if not res: + res = Resource(res_file, score, de, resName) + resources[resName] = res; + + if len(de.group) > 0: + groups[de.group].append(res) + else: + res.addVariant(file, score, de) + + directory.clear() diff --git a/utils.py b/einsteingame/utils.py similarity index 100% rename from utils.py rename to einsteingame/utils.py diff --git a/resources.py b/resources.py deleted file mode 100644 index 9142766..0000000 --- a/resources.py +++ /dev/null @@ -1,54 +0,0 @@ -from os import listdir - -from .buffer import Buffer - -class ResourceFile(object): - def __init__(self, fileName, buf=None): - # Ooooh, what does this button do? - # name(fileName) - - if buf is not None: - buffer = buf - ownBuffer = False - else: - buffer = Buffer() - ownBuffer = true - - try: - with open(fileName, 'rb') as f: - try: - sign = f.read(4) - if sign[0] != 'C' \ - or sign[1] != 'R' \ - or sign[2] != 'F' \ - or sign[3]: - raise IOError("Failover") - - major = f.unpack('i', 4) - minor = f.unpack('i', 4) - priority = f.unpack('i', 4) - - if major != 2 or minor < 0: - raise Exception("Incompatible version of resource file '" + name + "'") - except IOError: - raise Exception("Invalid resource file '" + name + "'") - except IOError: - raise Exception("Error loading resource file '" + name + "'") - -class ResourcesCollection: - def __init__(self, directories): - self.__files = [] - self.__buffer = None - self.loadResourceFiles(directories) - processFiles() - - 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