Source code for livemaker.lsb.core

# -*- 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/>.
"""Core lmscript classes."""

import enum
from abc import ABC, abstractmethod

import construct
import numpy
from loguru import logger
from lxml import etree


[docs]class BaseSerializable(ABC): """Base class for serializable LiveMaker objects. Note: LiveMaker uses 3 different script serialization formats: - LSC (old text .lsc) - XML (new XML .lsc) - LSB (compiled binary .lsb) In pylivemaker, we currently only support serializing to and from the binary LSB format. Subclasses of `BaseSerializable` do support serialization to pseudo-LSC and pseudo-XML formats so that a script can be examined for patching purposes, however, these exported formats cannot currently be re-read as input by pylivemaker. This means that pylivemaker cannot be used to compile .lsc files from a LiveMaker/LiveNovel template or project directory. """ def __init__(*args, **kwargs): pass # __iter__ and/or __getitem__ need to be implemented in subclasses for # construct to be able to build an object. def __iter__(self): raise NotImplementedError def __getitem__(self, key): raise NotImplementedError
[docs] def get(self, key, default=None): try: return self[key] except KeyError: return default
[docs] @abstractmethod def to_lsc(self): """Serialize this object as pseudo-LSC data.""" pass
[docs] @classmethod def from_lsc(cls, data): """Parse text .lsc data into an object.""" raise NotImplementedError
[docs] @abstractmethod def to_xml(self): """Serialize an object as pseudo-LSC XML.""" pass
[docs] @classmethod def from_xml(cls, root): """Parse XML into an object.""" raise NotImplementedError
@classmethod def _struct(cls): """Return a construct Struct for this class.""" return construct.Struct()
[docs] @classmethod def from_struct(cls, struct, **kwargs): """Instantiate an object from a construct Struct.""" if isinstance(struct, construct.Container): d = {k: v for k, v in struct.items()} d.update(kwargs) return cls(**d) raise NotImplementedError
[docs]class ParamType(enum.IntEnum): """:class:`Param` data type.""" Var = 0x00 """Variable name. Internally, LiveMaker stores TParamVar as a Delphi Variant type which in theory supports any possible Delphi type, but LiveMaker only uses it as a variable length string. """ Int = 0x01 """Integer.""" Float = 0x02 """Floating point value. LiveMaker TParamFloats are IEEE 80-bit precision floats, in pylivemaker we handle them as numpy `longdouble`. According to numpy docs, ``np.longdouble` is either `float96` or ``float128`` depending on platform, and in both cases they are actually ``float80`` padded with zeroes to 96 or 128 bits. """ Flag = 0x03 """1-byte Enum/Flag type.""" Str = 0x04 """CP932 encoded string. Internally, pylivemaker handles all strings as Python unicode strings. """
[docs]class OpeDataType(enum.IntEnum): """:class:`OpeData` operator type. Operator type determines how an expression will be evaluated. """ None_ = 0x00 To = 0x01 """Operator ``=`` (assignment).""" Plus = 0x02 """Operator ``+``. Note: In LiveMaker, + can be used for both addition and string concatenation, depending on the data type of the result variable. If the result variable is a numeric type and one of the arguments is a string, the string will be coerced to number (i.e. for ``x = 1 + "2"`` and ``x`` is an ``Int``, the final value of ``x`` will be ``3``). """ Minus = 0x03 """Operator ``-``.""" Mul = 0x04 """Operator ``*``.""" Div = 0x05 """Operator ``/``.""" Mod = 0x06 """Operator ``%``.""" Or = 0x07 """Operator ``|``. Note: In LiveMaker, ``|`` is used for both bitwise OR and logical OR, depending on the data type of the operands. """ And = 0x08 """Operator ``&``. Note: In LiveMaker, ``&`` is used for both bitwise AND and logical AND, depending on the data type of the operands. """ Xor = 0x09 """Operator ``^`` (bitwise XOR).""" DimTo = 0x0A """Operator ``[]`` (array access).""" Func = 0x0B """Operator ``()`` (function call). Available functions are listed in :class:`OpeFuncType`. """ Equal = 0x0C """Operator ``==`` (equals).""" Big = 0x0D """Operator ``>`` (greater than).""" Small = 0x0E """Operator ``<`` (less than).""" EBig = 0x0F """Operator ``>=`` (greater than or equals).""" ESmall = 0x10 """Operator ``<=`` (less than or equals).""" ShiftL = 0x11 """Operator ``<<`` (bitwise shift left).""" ShiftR = 0x12 """Operator ``>>`` (bitwise shift right).""" ComboStr = 0x13 """Operator ``++`` (string concatenation).""" NEqual = 0x14 """Operator ``!=`` (not equals)."""
[docs]class OpeFuncType(enum.IntEnum): """Function type. See LiveNovel docs for details on each available function. """ IntToStr = 0x00 IntToHex = 0x01 GetProp = 0x02 SetProp = 0x03 GetArraySize = 0x04 Length = 0x05 JLength = 0x06 Copy = 0x07 JCopy = 0x08 Delete = 0x09 JDelete = 0x0A Insert = 0x0B JInsert = 0x0C CompareStr = 0x0D CompareText = 0x0E Pos = 0x0F JPos = 0x10 Trim = 0x11 JTrim = 0x12 Exists = 0x13 Not = 0x14 SetArray = 0x15 FillMem = 0x16 CopyMem = 0x17 GetCheck = 0x18 SetCheck = 0x19 Random = 0x1A GetSaveCaption = 0x1B ArrayToString = 0x1C StringToArray = 0x1D IndexOfStr = 0x1E SortStr = 0x1F ListCompo = 0x20 ToClientX = 0x21 ToClientY = 0x22 ToScreenX = 0x23 ToScreenY = 0x24 Int = 0x25 Float = 0x26 Sin = 0x27 Cos = 0x28 Tan = 0x29 ArcSin = 0x2A ArcCos = 0x2B ArcTan = 0x2C ArcTan2 = 0x2D Hypot = 0x2E IndexOfMenu = 0x2F Abs = 0x30 Fabs = 0x31 VarExists = 0x32 EncodeDate = 0x33 EncodeTime = 0x34 DecodeDate = 0x35 DecodeTime = 0x36 GetYear = 0x37 GetMonth = 0x38 GetDay = 0x39 GetHour = 0x3A GetMin = 0x3B GetSec = 0x3C GetWeek = 0x3D GetWeekStr = 0x3E GetWeekJStr = 0x3F FixStr = 0x40 GetDisplayMode = 0x41 AddArray = 0x42 InsertArray = 0x43 DeleteArray = 0x44 InPrimary = 0x45 CopyArray = 0x46 FileExists = 0x47 LoadTextFile = 0x48 LowerCase = 0x49 UpperCase = 0x4A ExtractFilePath = 0x4B ExtractFileName = 0x4C ExtractFileExt = 0x4D IsPathDelimiter = 0x4E AddBackSlash = 0x4F ChangeFileExt = 0x50 IsDelimiter = 0x51 StringOfChar = 0x52 StringReplace = 0x53 AssignTemp = 0x54 HanToZen = 0x55 ZenToHan = 0x56 DBCreateTable = 0x57 DBSetActive = 0x58 DBAddField = 0x59 DBSetRecNo = 0x5A DBInsert = 0x5B DBDelete = 0x5C DBGetInt = 0x5D DBSetInt = 0x5E DBGetFloat = 0x5F DBSetFloat = 0x60 DBGetBool = 0x61 DBSetBool = 0x62 DBGetStr = 0x63 DBSetStr = 0x64 DBRecordCount = 0x65 DBFindFirst = 0x66 DBFindLast = 0x67 DBFindNext = 0x68 DBFindPrior = 0x69 DBLocate = 0x6A DBLoadTsvFile = 0x6B DBDirectGetInt = 0x6C DBDirectSetInt = 0x6D DBDirectGetFloat = 0x6E DBDirectSetFloat = 0x6F DBDirectGetBool = 0x70 DBDirectSetBool = 0x71 DBDirectGetStr = 0x72 DBDirectSetStr = 0x73 DBCopyTable = 0x74 DBDeleteTable = 0x75 DBInsertTable = 0x76 DBCopy = 0x77 DBClearTable = 0x78 DBSort = 0x79 DBGetActive = 0x7A DBGetRecNo = 0x7B DBClearRecord = 0x7C SetWallPaper = 0x7D Min = 0x7E Max = 0x7F Fmin = 0x80 Fmax = 0x81 GetVarType = 0x82 GetEnabled = 0x83 SetEnabled = 0x84 AddDelimiter = 0x85 ListSaveCaption = 0x86 OpenUrl = 0x87 Calc = 0x88 SaveScreen = 0x89 StrToIntDef = 0x8A StrToFloatDef = 0x8B GetVisible = 0x8C SetVisible = 0x8D GetHistoryCount = 0x8E GetHistoryMaxCount = 0x8F SetHistoryMaxCount = 0x90 GetGroupIndex = 0x91 GetSelected = 0x92 SetSelected = 0x93 SelectOpenFile = 0x94 SelectSaveFile = 0x95 SelectDirectory = 0x96 ExtractFile = 0x97 Chr = 0x98 Ord = 0x99 InCabinet = 0x9A PushVar = 0x9B PopVar = 0x9C DeleteStack = 0x9D CopyFile = 0x9E DBGetTableCount = 0x9F DBGetTable = 0xA0 CreateObject = 0xA1 DeleteObject = 0xA2 GetItem = 0xA3 UniqueArray = 0xA4 TrimArray = 0xA5 GetImeOpened = 0xA6 SetImeOpened = 0xA7 Alert = 0xA8 GetCinemaProp = 0xA9 SetCinemaProp = 0xAA
[docs]class Param(BaseSerializable): """Expression parameter (operand). Internally, LiveMaker subclasses each possible TParam type, but in pylivemaker we handle them all here. Args: value: The value for this parameter. type (:class:`ParamType`): The data type for this parameter. If type is not specified, it will be guessed based on value. Note: If `value` is a variable name, ``Var`` type must be explicity specified, otherwise it will incorrectly be guessed to be ``Str``. If `value` is an integer flag, ``Flag`` type must be explicitly specified, otherwise it will be incorrectly guessed to be ``Int``. """ def __init__(self, value=None, type=None, **kwargs): self.value = value if type is None: if isinstance(value, int): self.type = ParamType.Int elif isinstance(value, (float, numpy.longdouble)): self.type = ParamType.Float self.value = numpy.longdouble(value) elif isinstance(value, bool): self.type = ParamType.Flag elif isinstance(value, str): self.type = ParamType.Str else: raise ValueError("Could not guess datatype for {}".format(value)) else: self.type = ParamType(int(type)) def __str__(self): return str(self.value) def __iter__(self): return iter(self.items()) def __getitem__(self, key): if key == "type": return self.type.name elif key == "value": return self.value raise KeyError
[docs] def keys(self): return ["type", "value"]
[docs] def items(self): return [(k, self[k]) for k in self.keys()]
[docs] def to_lsc(self): if self.type == ParamType.Str: return '"{}"'.format(self.value) return str(self)
[docs] def to_xml(self): xml = self.to_lsc() if self.type == ParamType.Var and "\x01" in xml: logger.warning('Replacing invalid xml char "\\x01" in varname {}'.format(self.value)) xml = xml.replace("\x01", "*") return xml
@classmethod def _struct(cls): return construct.Struct( "type" / construct.Enum(construct.Byte, ParamType), "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"), }, # else 'Var' variable name type construct.Select( construct.PascalString(construct.Int32ul, "cp932"), ), ), )
[docs]class OpeData(BaseSerializable): """Expression operator class. Internal LiveMaker TOpeData class. Args: type (:class:`OpeDataType`): Operator type for this expression. name (str): The name of result variable for this expression. func (:class:`OpeFuncType`): Function for this expression (only applicable if `type` is `OpeDataType.Func`. operands (list(:class:`Param`)): The operands for this expression. """ def __init__(self, type=OpeDataType.None_, name="", func=None, operands=[], **kwargs): self.type = OpeDataType(int(type)) self.name = name if self.type == OpeDataType.Func: self.func = OpeFuncType(int(func)) else: self.func = None if isinstance(operands, construct.ListContainer): operands = [Param.from_struct(x) for x in operands] self.operands = operands def __str__(self): out = [] for token in self.tokenize(): if isinstance(token, Param): out.append(token.to_lsc()) else: out.append(str(token)) return "".join(out) def __iter__(self): return iter(self.items()) def __len__(self): return len(self.operands) def __getitem__(self, key): if key in self.keys(): v = getattr(self, key) if isinstance(v, enum.Enum): v = v.name return v raise KeyError
[docs] def keys(self): return ["type", "name", "count", "func", "operands"]
[docs] def items(self): return [(k, self[k]) for k in self.keys()]
@property def count(self): """Return the number of operands in this expression.""" return len(self.operands)
[docs] def to_lsc(self): return str(self)
[docs] def to_xml(self): out = [] for token in self.tokenize(): if isinstance(token, Param): out.append(token.to_xml()) else: out.append(str(token)) return "".join(out)
@classmethod def _struct(cls): return construct.Struct( "type" / construct.Enum(construct.Byte, OpeDataType), "name" / construct.PascalString(construct.Int32ul, "cp932"), "count" / construct.Int32ul, "func" / construct.Switch(construct.this.type, {"Func": construct.Enum(construct.Byte, OpeFuncType)}), "operands" / construct.Array(construct.this.count, Param._struct()), ) def _to(self): return [self.operands[-1]] # TODO: LiveMaker allows for doing math operators on strings via type # conversion - the docs say "50" + 100 should result in either 150 or "150" # depending on the result var's datatype. # # For our purposes don't worry about dealing with type coercion unless # someone finds a script that actually requires supporting it def _plus(self): (p1, p2) = self.operands if p1.type == ParamType.Var or p2.type == ParamType.Var: return [p1, " + ", p2] elif p1.type == ParamType.Str or p2.type == ParamType.Str: raise NotImplementedError("Plus() expected numeric type") return [Param(value=p1.value + p2.value)] def _minus(self): (p1, p2) = self.operands if p1.type == ParamType.Var or p2.type == ParamType.Var: return [p1, " - ", p2] elif p1.type == ParamType.Str or p2.type == ParamType.Str: raise NotImplementedError("Minus() expected numeric type") return [Param(value=p1.value - p2.value)] def _mul(self): (p1, p2) = self.operands if p1.type == ParamType.Var or p2.type == ParamType.Var: return [p1, " * ", p2] elif p1.type == ParamType.Str or p2.type == ParamType.Str: raise NotImplementedError("Mul() expected numeric type") return [Param(value=p1.value * p2.value)] def _div(self): (p1, p2) = self.operands if p1.type == ParamType.Var or p2.type == ParamType.Var: return [p1, " / ", p2] elif p1.type == ParamType.Str or p2.type == ParamType.Str: raise NotImplementedError("Div() expected numeric type") return [Param(value=p1.value / p2.value)] def _mod(self): (p1, p2) = self.operands if p1.type == ParamType.Var or p2.type == ParamType.Var: return [p1, " % ", p2] elif p1.type == ParamType.Str or p2.type == ParamType.Str: raise NotImplementedError("Mod() expected numeric type") return [Param(value=p1.value % p2.value)] def _or(self): # LiveMaker uses | to specify both bitwise and boolean OR (p1, p2) = self.operands if p1.type == ParamType.Var or p2.type == ParamType.Var: return ["(", p1, " | ", p2, ")"] elif p1.type == ParamType.Str or p2.type == ParamType.Str: raise NotImplementedError("Or() expected numeric type") if p1.type == ParamType.Flag and p2.type == ParamType.Flag: return [Param(value=p1.value or p2.value)] return [Param(value=p1.value | p2.value)] def _and(self): # LiveMaker uses | to specify both bitwise and boolean AND (p1, p2) = self.operands if p1.type == ParamType.Var or p2.type == ParamType.Var: return ["(", p1, " & ", p2, ")"] elif p1.type == ParamType.Str or p2.type == ParamType.Str: raise NotImplementedError("And() expected numeric type") if p1.type == ParamType.Flag and p2.type == ParamType.Flag: return [Param(value=p1.value and p2.value)] return [p1.value & p2.value] def _xor(self): (p1, p2) = self.operands if p1.type == ParamType.Var or p2.type == ParamType.Var: return [p1, " ^ ", p2] elif p1.type == ParamType.Str or p2.type == ParamType.Str: raise NotImplementedError("Xor() expected numeric type") return [Param(p1.value ^ p2.value)] def _dimto(self): # Array access x = [self.operands[0]] for p in self.operands[1:]: x.extend(["[", p, "]"]) return x def _func(self): x = ["{}(".format(self.func.name)] for i, p in enumerate(self.operands): x.append(p) if i != len(self.operands) - 1: x.append(", ") x.append(")") return x def _equal(self): (p1, p2) = self.operands if p1.type == ParamType.Var or p2.type == ParamType.Var: return [p1, " == ", p2] return [Param(value=p1.value == p2.value)] def _big(self): (p1, p2) = self.operands if p1.type == ParamType.Var or p2.type == ParamType.Var: return [p1, " > ", p2] return [Param(value=p1.value > p2.value)] def _small(self): (p1, p2) = self.operands if p1.type == ParamType.Var or p2.type == ParamType.Var: return [p1, " < ", p2] return [Param(value=p1.value < p2.value)] def _ebig(self): (p1, p2) = self.operands if p1.type == ParamType.Var or p2.type == ParamType.Var: return [p1, " >= ", p2] return [Param(value=p1.value >= p2.value)] def _esmall(self): (p1, p2) = self.operands if p1.type == ParamType.Var or p2.type == ParamType.Var: return [p1, " <= ", p2] return [Param(value=p1.value <= p2.value)] def _shiftl(self): (p1, p2) = self.operands if p1.type == ParamType.Var or p2.type == ParamType.Var: return [p1, " << ", p2] elif p1.type == ParamType.Str or p2.type == ParamType.Str: raise NotImplementedError("ShiftL() expected numeric type") return [Param(p1.value << p2.value)] def _shiftr(self): (p1, p2) = self.operands if p1.type == ParamType.Var or p2.type == ParamType.Var: return [p1, " >> ", p2] elif p1.type == ParamType.Str or p2.type == ParamType.Str: raise NotImplementedError("ShiftR() expected numeric type") return [Param(p1.value >> p2.value)] def _combostr(self): # String join (p1, p2) = self.operands if p1.type == ParamType.Var or p2.type == ParamType.Var: return [p1, " ++ ", p2] elif p1.type != ParamType.Str or p2.type != ParamType.Str: raise NotImplementedError("ComboStr() expected string type") return [Param(value="".join([p1.value, p2.value]))] def _nequal(self): (p1, p2) = self.operands if p1.type == ParamType.Var or p2.type == ParamType.Var: return [p1, " != ", p2] return [Param(value=p1.value != p2.value)]
[docs] def tokenize(self): """Return a tokenized version of this expression. Returns: list(str, :class:`Param`): List of tokens. Raises: NotImplementedError: If an operator does not support this combination of operands. """ try: tokens = { OpeDataType.To: self._to, OpeDataType.Plus: self._plus, OpeDataType.Minus: self._minus, OpeDataType.Mul: self._mul, OpeDataType.Div: self._div, OpeDataType.Mod: self._mod, OpeDataType.Or: self._or, OpeDataType.And: self._and, OpeDataType.Xor: self._xor, OpeDataType.DimTo: self._dimto, OpeDataType.Func: self._func, OpeDataType.Equal: self._equal, OpeDataType.Big: self._big, OpeDataType.Small: self._small, OpeDataType.EBig: self._ebig, OpeDataType.ESmall: self._esmall, OpeDataType.ShiftL: self._shiftl, OpeDataType.ShiftR: self._shiftr, OpeDataType.ComboStr: self._combostr, OpeDataType.NEqual: self._nequal, }[self.type]() return tokens except KeyError: raise NotImplementedError("Cannot compute value for {} types.".format(self.type))
[docs]class LiveParser(BaseSerializable): """Parses a list of OpeData expressions into one result expression. Args: entries (list(:class:`OpeData`)): List of child expressions """ def __init__(self, entries=[], **kwargs): if isinstance(entries, construct.ListContainer): entries = [OpeData.from_struct(x) for x in entries] self.entries = entries def __str__(self): return self._simplify() def __iter__(self): return iter(self.items()) def __len__(self): return len(self.entries) def __getitem__(self, key): if key == "entries": return self.entries raise KeyError
[docs] def keys(self): return ["entries"]
[docs] def items(self): return [(k, self[k]) for k in self.keys()]
[docs] def to_lsc(self): return str(self)
[docs] def to_xml(self): xml = self.to_lsc() if "\x01" in xml: logger.warning('Replacing invalid xml char "\\x01"') xml = xml.replace("\x01", "*") return xml
@classmethod def _struct(cls): return construct.Struct( "entries" / construct.PrefixedArray(construct.Int32ul, OpeData._struct()), ) # @classmethod # def from_struct(cls, struct): # """Return a LiveParser for the specified struct.""" # return cls(entries=[OpeData.from_struct(x) for x in struct.entries]) def _simplify(self): """Return a simplified expression for all expressions in this parser.""" def _resolve(var, exprs): if var in exprs: operands = exprs[var] for i, op in enumerate(operands): if isinstance(op, Param): if op.type == ParamType.Var: if op.value.startswith("____"): # LiveParser parameter name operands[i] = _resolve(op.value, exprs) else: operands[i] = op.value elif op.type == ParamType.Str: operands[i] = '"{}"'.format(op.value).replace("\n", "\\n").replace("\r", "\\r") else: operands[i] = op.value else: operands = [var] return "".join([str(x) for x in operands]) exprs = {} for e in self.entries: exprs.update({e.name: e.tokenize()}) if self.entries: e = self.entries[-1] if e.type == OpeDataType.To: if e.name == "____arg": return _resolve("____arg", exprs) return "{} = {}".format(e.name, _resolve(e.name, exprs)) else: logger.warning("Last entry in LiveParser was not a To statement: {}".format(self.entries[-1])) return ""
[docs]class LiveParserArray(BaseSerializable): """Internal use convenience class for handling arrays of :obj:`LiveParser` objects. Args: parsers (iterable): Iterable containing this array's parsers. name (str): Name for this field, used as XML tag name when serializing. """ def __init__(self, parsers=[], prefixed=True): if isinstance(parsers, construct.ListContainer): parsers = [LiveParser.from_struct(x) for x in parsers] self.parsers = parsers self.prefixed = prefixed def __str__(self): return " ".join([str(x) for x in self.parsers]) def __iter__(self): return iter(self.parsers) def __len__(self): return len(self.parsers)
[docs] def to_lsc(self): """Return this command in text .lsc format.""" out = [] for parser in self.parsers: if parser: out.append(parser.to_lsc()) else: out.append("") return "\t".join(out)
# @classmethod # def from_lsc(cls, *args, **kwargs): # parsers = [] # for arg in args: # if arg: # parsers.append(LiveParser.from_lsc(arg)) # else: # parsers.append('') # return cls(parsers)
[docs] def to_xml(self): # NOTE: For whatever reason, LiveMaker uses a '\x02' separated list of # strings for variable width lists, and child <Item> tags for fixed # size lists. # # '\x02' is not a legal XMl character and lxml will not accept it, so # we just use \t. out = [] if self.prefixed: for parser in self.parsers: if hasattr(parser, "to_xml"): out.append(parser.to_xml()) else: out.append(str(parser)) return "\t".join(out) for parser in self.parsers: item = etree.Element("Item") if hasattr(parser, "to_xml"): item.text = parser.to_xml() else: item.text = str(parser) out.append(item) return out
# @classmethod # def from_xml(cls, root, **kwargs): # parsers = [] # prefixed = True # if len(root): # prefixed = False # for child in root: # if child.tag == 'Item': # parsers.append(LiveParser.from_xml(child)) # else: # for parser in root.text.split('\x02'): # if parser: # parsers.append(LiveParser.from_xml(child)) # else: # parsers.append('') # return LiveParserArray(parsers, prefixed=prefixed) @classmethod def _struct(cls, subcon, prefixed=True): """Return a construct Struct for this class. Args: subcon: Can be a subcon or fixed size, depending on value of `prefixed`. prefixed: True if this is a PrefixedArray. """ if prefixed: return construct.PrefixedArray(subcon, LiveParser._struct()) else: return construct.Array(subcon, LiveParser._struct())
[docs] @classmethod def from_struct(cls, struct, prefixed=True): return cls(struct, prefixed=prefixed)
[docs]class PropertyType(enum.IntEnum): """LiveMaker object property constants.""" PR_NONE = 0x00 PR_NAME = 0x01 PR_PARENT = 0x02 PR_SOURCE = 0x03 PR_LEFT = 0x04 PR_TOP = 0x05 PR_WIDTH = 0x06 PR_HEIGHT = 0x07 PR_ZOOMX = 0x08 PR_COLOR = 0x09 PR_BORDERWIDTH = 0x0A PR_BORDERCOLOR = 0x0B PR_ALPHA = 0x0C PR_PRIORITY = 0x0D PR_OFFSETX = 0x0E PR_OFFSETY = 0x0F PR_FONTNAME = 0x10 PR_FONTHEIGHT = 0x11 PR_FONTSTYLE = 0x12 PR_LINESPACE = 0x13 PR_FONTCOLOR = 0x14 PR_FONTLINKCOLOR = 0x15 PR_FONTBORDERCOLOR = 0x16 PR_FONTHOVERCOLOR = 0x17 PR_FONTHOVERSTYLE = 0x18 PR_HOVERCOLOR = 0x19 PR_ANTIALIAS = 0x1A PR_DELAY = 0x1B PR_PAUSED = 0x1C PR_VOLUME = 0x1D PR_REPEAT = 0x1E PR_BALANCE = 0x1F PR_ANGLE = 0x20 PR_ONPLAYING = 0x21 PR_ONNOTIFY = 0x22 PR_ONMOUSEMOVE = 0x23 PR_ONMOUSEOUT = 0x24 PR_ONLBTNDOWN = 0x25 PR_ONLBTNUP = 0x26 PR_ONRBTNDOWN = 0x27 PR_ONRBTNUP = 0x28 PR_ONWHEELDOWN = 0x29 PR_ONWHEELUP = 0x2A PR_BRIGHTNESS = 0x2B PR_ONPLAYEND = 0x2C PR_INDEX = 0x2D PR_COUNT = 0x2E PR_ONLINK = 0x2F PR_VISIBLE = 0x30 PR_COLCOUNT = 0x31 PR_ROWCOUNT = 0x32 PR_TEXT = 0x33 PR_MARGINX = 0x34 PR_MARGINY = 0x35 PR_HALIGN = 0x36 PR_BORDERSOURCETL = 0x37 PR_BORDERSOURCETC = 0x38 PR_BORDERSOURCETR = 0x39 PR_BORDERSOURCECL = 0x3A PR_BORDERSOURCECC = 0x3B PR_BORDERSOURCECR = 0x3C PR_BORDERSOURCEBL = 0x3D PR_BORDERSOURCEBC = 0x3E PR_BORDERSOURCEBR = 0x3F PR_BORDERHALIGNT = 0x40 PR_BORDERHALIGNC = 0x41 PR_BORDERHALIGNB = 0x42 PR_BORDERVALIGNL = 0x43 PR_BORDERVALIGNC = 0x44 PR_BORDERVALIGNR = 0x45 PR_SCROLLSOURCE = 0x46 PR_CHECKSOURCE = 0x47 PR_AUTOSCRAP = 0x48 PR_ONSELECT = 0x49 PR_RCLICKSCRAP = 0x4A PR_ONOPENING = 0x4B PR_ONOPENED = 0x4C PR_ONCLOSING = 0x4D PR_ONCLOSED = 0x4E PR_CARETX = 0x4F PR_CARETY = 0x50 PR_IGNOREMOUSE = 0x51 PR_TEXTPAUSED = 0x52 PR_TEXTDELAY = 0x53 PR_HOVERSOURCE = 0x54 PR_PRESSEDSOURCE = 0x55 PR_GROUPINDEX = 0x56 PR_ALLOWALLUP = 0x57 PR_SELECTED = 0x58 PR_CAPTUREMASK = 0x59 PR_POWER = 0x5A PR_ORIGWIDTH = 0x5B PR_ORIGHEIGHT = 0x5C PR_APPEARX = 0x5D PR_APPEARY = 0x5E PR_PARTMOTION = 0x5F PR_PARAM = 0x60 PR_PARAM2 = 0x61 PR_TOPINDEX = 0x62 PR_READONLY = 0x63 PR_CURSOR = 0x64 PR_POSZOOMED = 0x65 PR_ONPLAYSTART = 0x66 PR_PARAM3 = 0x67 PR_ONMOUSEIN = 0x68 PR_ONMAPIN = 0x69 PR_ONMAPOUT = 0x6A PR_MAPSOURCE = 0x6B PR_AMP = 0x6C PR_WAVELEN = 0x6D PR_SCROLLX = 0x6E PR_SCROLLY = 0x6F PR_FLIPH = 0x70 PR_FLIPV = 0x71 PR_ONIDLE = 0x72 PR_DISTANCEX = 0x73 PR_DISTANCEY = 0x74 PR_CLIPLEFT = 0x75 PR_CLIPTOP = 0x76 PR_CLIPWIDTH = 0x77 PR_CLIPHEIGHT = 0x78 PR_DURATION = 0x79 PR_THUMBSOURCE = 0x7A PR_BUTTONSOURCE = 0x7B PR_MIN = 0x7C PR_MAX = 0x7D PR_VALUE = 0x7E PR_ORIENTATION = 0x7F PR_SMALLCHANGE = 0x80 PR_LARGECHANGE = 0x81 PR_MAPTEXT = 0x82 PR_GLYPHWIDTH = 0x83 PR_GLYPHHEIGHT = 0x84 PR_ZOOMY = 0x85 PR_CLICKEDSOURCE = 0x86 PR_ANIPAUSED = 0x87 PR_ONHOLD = 0x88 PR_ONRELEASE = 0x89 PR_REVERSE = 0x8A PR_PLAYING = 0x8B PR_REWINDONLOAD = 0x8C PR_COMPOTYPE = 0x8D PR_FONTSHADOWCOLOR = 0x8E PR_FONTBORDER = 0x8F PR_FONTSHADOW = 0x90 PR_ONKEYDOWN = 0x91 PR_ONKEYUP = 0x92 PR_ONKEYREPEAT = 0x93 PR_HANDLEKEY = 0x94 PR_ONFOCUSIN = 0x95 PR_ONFOCUSOUT = 0x96 PR_OVERLAY = 0x97 PR_TAG = 0x98 PR_CAPTURELINK = 0x99 PR_FONTHOVERBORDER = 0x9A PR_FONTHOVERBORDERCOLOR = 0x9B PR_FONTHOVERSHADOW = 0x9C PR_FONTHOVERSHADOWCOLOR = 0x9D PR_BARSIZE = 0x9E PR_MUTEONLOAD = 0x9F PR_PLUSX = 0xA0 PR_PLUSY = 0xA1 PR_CARETHEIGHT = 0xA2 PR_REPEATPOS = 0xA3 PR_BLURSPAN = 0xA4 PR_BLURDELAY = 0xA5 PR_FONTCHANGEABLED = 0xA6 PR_IMEMODE = 0xA7 PR_FLOATANGLE = 0xA8 PR_FLOATZOOMX = 0xA9 PR_FLOATZOOMY = 0xAA PR_CAPMASKLEVEL = 0xAB PR_PADDINGLEFT = 0xAC PR_PADDING_RIGHT = 0xAD