Source code for livemaker.lpb

# -*- 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 project settings file (LPB) module."""

from io import IOBase

import construct
import numpy

from .exceptions import BadLpbError
from .lsb.core import ParamType
from .lsb.lmscript import DEFAULT_LSB_VERSION, LsbVersionValidator, lsb_to_lm_ver


[docs]class LMProject: """Class for handling project settings files. RE'd from TProject, TProjectSettings classes in LiveMaker code. """ def __init__( self, version=DEFAULT_LSB_VERSION, project_name="", unk1=0, unk2=0, init_lsb="", exit_lsb="", project_dir="", unk3=0, bool1=0, bool2=0, audio_formats=".wav.wma.ogg.mid.mp3", bool3=0, bool4=0, bool5=1, insert_disk_prompt="", exit_prompt="", system_settings=[], **kwargs, ): self.version = version self.project_name = project_name self.unk1 = unk1 self.unk2 = unk2 self.init_lsb = init_lsb self.exit_lsb = exit_lsb self.project_dir = project_dir self.unk3 = unk3 self.bool1 = bool1 self.bool2 = bool2 self.audio_formats = audio_formats self.bool3 = bool3 self.bool4 = bool4 self.bool5 = bool5 self.insert_disk_prompt = insert_disk_prompt self.exit_prompt = exit_prompt if isinstance(system_settings, construct.ListContainer): settings = [] for setting in system_settings: settings.append({"name": setting.name, "type": str(setting.type), "value": setting.value}) self.system_settings = settings else: self.system_settings = system_settings 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", "unk2", "init_lsb", "exit_lsb", "project_dir", "unk3", "bool1", "bool2", "audio_formats", "bool3", "bool4", "bool5", "insert_disk_prompt", "exit_prompt", "system_settings", ]
[docs] def items(self): return [(k, self[k]) for k in self.keys()]
@property def lm_version(self): return lsb_to_lm_ver(self.version) @classmethod def _struct(cls): return construct.Struct( "version" / LsbVersionValidator(construct.Int32ul), "project_name" / construct.PascalString(construct.Int32ul, "cp932"), "unk1" / construct.Int64ul, "unk2" / construct.Int64ul, "init_lsb" / construct.PascalString(construct.Int32ul, "cp932"), "exit_lsb" / construct.If( construct.this.version > 0x6D, construct.PascalString(construct.Int32ul, "cp932"), ), "project_dir" / construct.PascalString(construct.Int32ul, "cp932"), "unk3" / construct.Int32ul, "bool1" / construct.Byte, "bool2" / construct.If( construct.this.version >= 0x6A, construct.Byte, ), "audio_formats" / construct.If( construct.this.version >= 0x6D, construct.PascalString(construct.Int32ul, "cp932"), ), "bool3" / construct.If( construct.this.version >= 0x71, construct.Byte, ), "bool4" / construct.If( construct.this.version >= 0x72, construct.Byte, ), "bool5" / construct.If( construct.this.version >= 0x74, construct.Byte, ), "insert_disk_prompt" / construct.PascalString(construct.Int32ul, "cp932"), "exit_prompt" / construct.PascalString(construct.Int32ul, "cp932"), "system_settings" / construct.PrefixedArray( construct.Int32ul, construct.Struct( "type" / construct.Enum(construct.Byte, ParamType), "name" / construct.PascalString(construct.Int32ul, "cp932"), "value" / construct.Switch( construct.this.type, { "Int": construct.Int32sl, "Float": construct.ExprAdapter( construct.Bytes(10), lambda obj, ctx: numpy.frombuffer(obj.rjust(16, b"\x00"), dtype=numpy.longdouble), lambda obj, ctx: numpy.longdouble(obj).tobytes()[-10:], ), "Flag": construct.Byte, "Str": 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 LMProject. Args: infile: Input .lpb 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 BadLpbError(e)
[docs] def to_lpb(self): """Compile settings into binary .lpb format.""" try: return self._struct().build(self) except construct.ConstructError as e: raise BadLpbError(e)