def test_init(self, tmpdir, cls): t = Path(str(tmpdir)) f = t.joinpath('f.txt') with pytest.raises(FileNotFoundError): cls(f) with pytest.raises(TypeError): cls(t) f.write_text('') with pytest.raises(ValueError): cls(f) f = t.joinpath('f.miz') f.write_text('') cls(f)
class Miz: def __init__(self, path_to_miz_file, temp_dir=None, keep_temp_dir: bool = False): self.miz_path = Path(path_to_miz_file) if not self.miz_path.exists(): raise FileNotFoundError(path_to_miz_file) if not self.miz_path.isfile(): raise TypeError(path_to_miz_file) if not self.miz_path.ext == '.miz': raise ValueError(path_to_miz_file) if temp_dir is not None: raise PendingDeprecationWarning() self.keep_temp_dir = keep_temp_dir self.tmpdir = Path(tempfile.mkdtemp('EMFT_')) logger.debug('temporary directory: {}'.format(self.tmpdir.abspath())) self.zip_content = None self._mission = None self._mission_qual = None self._l10n = None self._l10n_qual = None self._map_res = None self._map_res_qual = None def __enter__(self): logger.debug('instantiating new Mission object as a context') self.unzip() self._decode() return self def __exit__(self, exc_type, exc_val, exc_tb): if exc_type: logger.error('there were error with this mission, keeping temp dir at "{}" and re-raising'.format( self.tmpdir.abspath())) logger.error('{}\n{}'.format(exc_type, exc_val)) return False else: logger.debug('closing Mission object context') if not self.keep_temp_dir: logger.debug('removing temp dir: {}'.format(self.tmpdir.abspath())) self.tmpdir.rmtree() @property def mission_file(self): return self.tmpdir.joinpath('mission') @property def dictionary_file(self): return self.tmpdir.joinpath('l10n', 'DEFAULT', 'dictionary') @property def map_res_file(self): return self.tmpdir.joinpath('l10n', 'DEFAULT', 'mapResource') @property def mission(self) -> Mission: if self._mission is None: raise RuntimeError() return self._mission @property def l10n(self) -> dict: if self._l10n is None: raise RuntimeError() return self._l10n @property def map_res(self) -> dict: if self._map_res is None: raise RuntimeError() return self._map_res @staticmethod def reorder(miz_file_path, target_dir, skip_options_file): with Miz(miz_file_path) as m: def mirror_dir(src, dst): logger.debug('{} -> {}'.format(src, dst)) diff_ = dircmp(src, dst, ignore) diff_list = diff_.left_only + diff_.diff_files logger.debug('differences: {}'.format(diff_list)) for x in diff_list: source = Path(diff_.left).joinpath(x) target = Path(diff_.right).joinpath(x) logger.debug('looking at: {}'.format(x)) if source.isdir(): logger.debug('isdir: {}'.format(x)) if not target.exists(): logger.debug('creating: {}'.format(x)) target.mkdir() mirror_dir(source, target) else: logger.debug('copying: {}'.format(x)) source.copy2(diff_.right) for sub in diff_.subdirs.values(): assert isinstance(sub, dircmp) mirror_dir(sub.left, sub.right) m.unzip(overwrite=True) m._decode() m._encode() if skip_options_file: ignore = ['options'] else: ignore = [] mirror_dir(m.tmpdir, target_dir) def _decode(self): logger.info('decoding lua tables') if not self.zip_content: self.unzip(overwrite=False) Progress.start('Decoding MIZ file', length=3) Progress.set_label('Decoding map resource') logger.debug('reading map resource file') with open(self.map_res_file, encoding=ENCODING) as f: self._map_res, self._map_res_qual = SLTP().decode(f.read()) Progress.set_value(1) Progress.set_label('Decoding dictionary file') logger.debug('reading l10n file') with open(self.dictionary_file, encoding=ENCODING) as f: self._l10n, self._l10n_qual = SLTP().decode(f.read()) Progress.set_value(2) Progress.set_label('Decoding mission file') logger.debug('reading mission file') with open(self.mission_file, encoding=ENCODING) as f: mission_data, self._mission_qual = SLTP().decode(f.read()) self._mission = Mission(mission_data, self._l10n) Progress.set_value(3) logger.info('decoding done') def _encode(self): logger.info('encoding lua tables') Progress.start('Decoding MIZ file', length=3) Progress.set_label('Encoding map resource') logger.debug('encoding map resource') with open(self.map_res_file, mode='w', encoding=ENCODING) as f: f.write(SLTP().encode(self._map_res, self._map_res_qual)) Progress.set_value(1) Progress.set_label('Encoding map resource') logger.debug('encoding l10n dictionary') with open(self.dictionary_file, mode='w', encoding=ENCODING) as f: f.write(SLTP().encode(self.l10n, self._l10n_qual)) Progress.set_value(2) Progress.set_label('Encoding map resource') logger.debug('encoding mission dictionary') with open(self.mission_file, mode='w', encoding=ENCODING) as f: f.write(SLTP().encode(self.mission.d, self._mission_qual)) Progress.set_value(3) logger.info('encoding done') def unzip(self, overwrite: bool = False): if self.zip_content and not overwrite: raise FileExistsError(self.tmpdir.abspath()) logger.debug('unzipping miz to temp dir') try: with ZipFile(self.miz_path.abspath()) as zip_file: logger.debug('reading infolist') self.zip_content = [f.filename for f in zip_file.infolist()] for item in zip_file.infolist(): # not using ZipFile.extractall() for security reasons assert isinstance(item, ZipInfo) logger.debug('unzipping item: {}'.format(item)) try: zip_file.extract(item, self.tmpdir.abspath()) except: logger.error('failed to extract archive member: {}'.format(item)) raise except BadZipFile: raise BadZipFile(self.miz_path.abspath()) except: logger.exception('error while unzipping miz file: {}'.format(self.miz_path.abspath())) raise logger.debug('checking miz content') # noinspection PyTypeChecker for miz_item in map( join, [self.tmpdir.abspath()], [ 'mission', 'options', 'warehouses', 'l10n/DEFAULT/dictionary', 'l10n/DEFAULT/mapResource' ]): if not exists(miz_item): logger.error('missing file in miz: {}'.format(miz_item)) raise FileNotFoundError(miz_item) for filename in self.zip_content: p = self.tmpdir.joinpath(filename) if not p.exists(): raise FileNotFoundError(p.abspath()) logger.debug('all files have been found, miz successfully unzipped') def zip(self, destination=None): self._encode() if destination is None: destination = Path(self.miz_path.dirname()).joinpath('{}_EMFT.miz'.format(self.miz_path.namebase)) destination = Path(destination).abspath() logger.debug('zipping mission to: {}'.format(destination)) with open(destination, mode='wb') as f: f.write(dummy_miz) with ZipFile(destination, mode='w', compression=8) as _z: for f in self.zip_content: abs_path = self.tmpdir.joinpath(f).abspath() logger.debug('injecting in zip file: {}'.format(abs_path)) _z.write(abs_path, arcname=f) return destination