[game-data-packager] 08/17: make-template: add auto-detection for how to unpack things

Simon McVittie smcv at debian.org
Wed Dec 30 22:57:21 UTC 2015


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 <smcv at debian.org>
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



More information about the Pkg-games-commits mailing list