Source code for livemaker.lpm

# -*- coding: utf-8
#
# Copyright (C) 2020 Peter Rowlands <peter@pmrowla.com>
# Copyright (C) 2014 tinfoil <https://bitbucket.org/tinfoil/>
#
# This file is a part of pylivemaker.
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# this program. If not, see <http://www.gnu.org/licenses/>.
"""LiveMaker preview menu file (LPM) module."""

from io import IOBase

import construct

from .exceptions import LiveMakerException


DEFAULT_LPM_VERSION = 106


[docs]class BadLPMError(LiveMakerException): pass
class _LPMVersionAdapter(construct.Adapter): def _decode(self, obj, ctx, path): return int(obj) def _encode(self, obj, ctx, path): return f"{obj:03}".encode("ascii")
[docs]class LPMVersionValidator(construct.Validator): def _validate(self, obj, ctx, path): return obj >= 100 def _decode(self, obj, ctx, path): if not self._validate(obj, ctx, path): raise construct.ValidationError(f"Unsupported LPM version: {obj}") return obj
[docs]class LMLivePrevMenu: """Class for handling preview menu files. RE'd from TLivePrevMenu, TLivePrevImages classes in LiveMaker code. """ def __init__(self, version=DEFAULT_LPM_VERSION, unk1=0, buttons=[], **kwargs): self.version = version self.unk1 = unk1 self.buttons = buttons def __iter__(self): return iter(self.items()) def __getitem__(self, key): if key in self.keys(): return getattr(self, key) raise KeyError
[docs] def keys(self): return [ "version", "project_name", "unk1", "buttons", ]
[docs] def items(self): return [(k, self[k]) for k in self.keys()]
@classmethod def _struct(cls): return construct.Struct( "signature" / construct.Const(b"LivePrevMenu"), "version" / LPMVersionValidator(_LPMVersionAdapter(construct.Bytes(3))), "unk1" / construct.Bytes(8), "buttons" / construct.PrefixedArray( construct.Int32ul, construct.Struct( "width" / construct.Int32ul, "height" / construct.Int32ul, "src" / construct.PascalString(construct.Int32ul, "cp932"), "unk2" / construct.Byte, "name" / construct.PascalString(construct.Int32ul, "cp932"), "src_selected" / construct.PascalString(construct.Int32ul, "cp932"), "unk3" / construct.PascalString(construct.Int32ul, "cp932"), "unk4" / construct.PascalString(construct.Int32ul, "cp932"), "unk5" / construct.If( construct.this._._.version > 100, construct.PascalString(construct.Int32ul, "cp932"), ), "unk6" / construct.If( construct.this._._.version > 102, construct.Struct( construct.PascalString(construct.Int32ul, "cp932"), construct.PascalString(construct.Int32ul, "cp932"), ), ), "unk7" / construct.PascalString(construct.Int32ul, "cp932"), "unk8" / construct.PascalString(construct.Int32ul, "cp932"), "unk9" / construct.PascalString(construct.Int32ul, "cp932"), "unk10" / construct.If( construct.this._._.version > 101, construct.Struct( construct.PascalString(construct.Int32ul, "cp932"), construct.PascalString(construct.Int32ul, "cp932"), ), ), "unk15" / construct.Int32ul, "unk16" / construct.Int32ul, "unk17" / construct.PascalString(construct.Int32ul, "cp932"), "unk18" / construct.If( construct.this._._.version > 103, construct.Struct( construct.PascalString(construct.Int32ul, "cp932"), construct.PascalString(construct.Int32ul, "cp932"), construct.PascalString(construct.Int32ul, "cp932"), construct.PascalString(construct.Int32ul, "cp932"), construct.PascalString(construct.Int32ul, "cp932"), construct.Int32ul, ), ), "unk19" / construct.If( construct.this._._.version > 104, construct.PascalString(construct.Int32ul, "cp932"), ), "unk20" / construct.If( construct.this._._.version > 105, construct.PascalString(construct.Int32ul, "cp932"), ), ), ), )
[docs] @classmethod def from_struct(cls, struct, **kwargs): """Create an LMProject from the specified struct.""" if isinstance(struct, construct.Container): d = {k: v for k, v in struct.items()} d.update(kwargs) return cls(**d) raise NotImplementedError
[docs] @classmethod def from_file(cls, infile): """Parse the specified file into an LMLivePrevMenu. Args: infile: Input .lpm file. Can be string, path-like or file-like object. """ if not isinstance(infile, IOBase): infile = open(infile, "rb") try: return cls.from_struct(cls._struct().parse_stream(infile)) except construct.ConstructError as e: raise BadLPMError(e)
[docs] def to_lpm(self): """Compile settings into binary .lpm format.""" try: return self._struct().build(self) except construct.ConstructError as e: raise BadLPMError(e)