This is an automated email from the git hooks/post-receive script. smcv pushed a commit to branch master in repository game-data-packager.
commit 0962ad6638e2ea2fb97ce0bfaf519a10b948343c Author: Simon McVittie <[email protected]> Date: Wed Dec 30 22:24:04 2015 +0000 make-template: add auto-detection for how to unpack things --- game_data_packager/make_template.py | 10 +++ game_data_packager/unpack/__main__.py | 26 +++----- game_data_packager/unpack/{__main__.py => auto.py} | 38 +++++------ game_data_packager/unpack/umod.py | 77 +++++++++++++++------- 4 files changed, 90 insertions(+), 61 deletions(-) diff --git a/game_data_packager/make_template.py b/game_data_packager/make_template.py index ce7e4c1..bc4db2b 100644 --- a/game_data_packager/make_template.py +++ b/game_data_packager/make_template.py @@ -38,6 +38,7 @@ except ImportError: from . import (HashedFile, WantedFile) from .gog import GOG from .steam import parse_acf +from .unpack.auto import automatic_unpacker from .util import ( check_output, which, @@ -179,6 +180,15 @@ class GameData(object): self.add_file(name, out_name=out_name, group=self.archives) + unpacker = automatic_unpacker(name) + + if unpacker is not None: + with unpacker: + for entry in unpacker: + if entry.is_regular_file and entry.is_extractable: + self.add_file(entry.name, opened=unpacker.open(entry), + size=entry.size) + def add_file(self, path, out_name=None, group=None, opened=None, size=None, lang=None): if out_name is None: diff --git a/game_data_packager/unpack/__main__.py b/game_data_packager/unpack/__main__.py index ea44770..3f13f2e 100644 --- a/game_data_packager/unpack/__main__.py +++ b/game_data_packager/unpack/__main__.py @@ -16,10 +16,8 @@ # /usr/share/common-licenses/GPL-2. import argparse -import tarfile -import zipfile -from . import (TarUnpacker, ZipUnpacker) +from .auto import automatic_unpacker if __name__ == '__main__': parser = argparse.ArgumentParser() @@ -28,17 +26,13 @@ if __name__ == '__main__': parser.add_argument('archive') args = parser.parse_args() - if zipfile.is_zipfile(args.archive): - unpacker = ZipUnpacker(args.archive) - elif tarfile.is_tarfile(args.archive): - unpacker = TarUnpacker(args.archive) - elif args.archive.endswith('.umod'): - from .umod import Umod - unpacker = Umod(args.archive) - else: - raise SystemExit('Cannot work out how to unpack %r' % args.archive) + unpacker = automatic_unpacker(args.archive) - if args.output: - unpacker.extractall(args.output) - else: - unpacker.printdir() + if unpacker is None: + raise ValueError('Cannot work out how to unpack %r' % args.archive) + + with unpacker: + if args.output: + unpacker.extractall(args.output) + else: + unpacker.printdir() diff --git a/game_data_packager/unpack/__main__.py b/game_data_packager/unpack/auto.py similarity index 50% copy from game_data_packager/unpack/__main__.py copy to game_data_packager/unpack/auto.py index ea44770..c0199db 100644 --- a/game_data_packager/unpack/__main__.py +++ b/game_data_packager/unpack/auto.py @@ -15,30 +15,28 @@ # You can find the GPL license text on a Debian system under # /usr/share/common-licenses/GPL-2. -import argparse import tarfile import zipfile from . import (TarUnpacker, ZipUnpacker) -if __name__ == '__main__': - parser = argparse.ArgumentParser() - parser.add_argument('--output', '-o', help='extract to OUTPUT', - default=None) - parser.add_argument('archive') - args = parser.parse_args() - - if zipfile.is_zipfile(args.archive): - unpacker = ZipUnpacker(args.archive) - elif tarfile.is_tarfile(args.archive): - unpacker = TarUnpacker(args.archive) - elif args.archive.endswith('.umod'): - from .umod import Umod - unpacker = Umod(args.archive) +def automatic_unpacker(archive, reader=None): + if reader is None: + is_plain_file = True + reader = open(archive, 'rb') else: - raise SystemExit('Cannot work out how to unpack %r' % args.archive) + is_plain_file = False - if args.output: - unpacker.extractall(args.output) - else: - unpacker.printdir() + if reader.seekable(): + if zipfile.is_zipfile(reader): + return ZipUnpacker(reader) + + if archive.endswith(('.umod', '.exe')): + from .umod import (Umod, is_umod) + if is_umod(reader): + return Umod(reader) + + if is_plain_file and tarfile.is_tarfile(archive): + return TarUnpacker(archive) + + return None diff --git a/game_data_packager/unpack/umod.py b/game_data_packager/unpack/umod.py index 6b0ed26..a0bc84b 100644 --- a/game_data_packager/unpack/umod.py +++ b/game_data_packager/unpack/umod.py @@ -230,6 +230,49 @@ class UmodRequirement(UmodSection): raise ValueError('Unexpected key in requirement: %r (value %r)' % (k, v)) +class NotUmod(ValueError): + pass + +def _open(path_or_file): + if isinstance(path_or_file, (str, bytes, int)): + close_file = True + name = str(path_or_file) + reader = open(path_or_file, 'rb') + else: + close_file = False + reader = path_or_file + + if hasattr(path_or_file, 'name'): + name = path_or_file.name + else: + name = repr(path_or_file) + + if reader.read(0) != b'': + raise ValueError('%r is not open in binary mode' % reader) + + if not isinstance(reader, io.BufferedIOBase): + reader = io.BufferedReader(reader) + + reader.seek(-20, os.SEEK_END) + trailer_offset = reader.tell() + block = reader.read(20) + + if len(block) != 20: + raise NotUmod('"%s" is not a .umod: unable to read 20 bytes at end' % + name) + + magic, toc_offset, eof_offset, flags, checksum = struct.unpack('<IIIII', + block) + + if magic != 0x9fe3c5a3: + raise NotUmod('"%s" is not a .umod: magic number does not match' % name) + + if reader.tell() != eof_offset: + raise NotUmod('"%s" is not a .umod: length field does not match' % name) + + return (reader, name, toc_offset, trailer_offset, flags, checksum, \ + close_file) + class Umod(StreamUnpackable): """Object representing an Unreal Tournament modification package, or an executable installer in a similar format. @@ -255,31 +298,8 @@ class Umod(StreamUnpackable): self.entry_order = [] self.unparsed = [] - if isinstance(path_or_file, (str, bytes, int)): - self.__close_file = True - self.name = str(path_or_file) - self.reader = open(path_or_file, 'rb') - else: - self.__close_file = False - self.reader = path_or_file - - if hasattr(path_or_file, 'name'): - self.name = path_or_file.name - else: - self.name = repr(path_or_file) - - if self.reader.read(0) != b'': - raise ValueError('%r is not open in binary mode' % self.reader) - - if not isinstance(self.reader, io.BufferedIOBase): - self.reader = io.BufferedReader(self.reader) - - self.reader.seek(-20, os.SEEK_END) - trailer_offset = self.reader.tell() - magic, toc_offset, eof_offset, flags, checksum = struct.unpack( - '<IIIII', self.reader.read(20)) - assert magic == 0x9fe3c5a3 - assert self.reader.tell() == eof_offset + self.reader, self.name, toc_offset, trailer_offset, flags, \ + checksum, self.__close_file = _open(path_or_file) self.reader.seek(toc_offset) @@ -434,6 +454,13 @@ class Umod(StreamUnpackable): return -value return value +def is_umod(path_or_file): + try: + _open(path_or_file) + return True + except: + return False + if __name__ == '__main__': import argparse -- Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-games/game-data-packager.git _______________________________________________ Pkg-games-commits mailing list [email protected] http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-games-commits

