[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