Source code for livemaker.project

# -*- 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/>.
"""pylivemaker project management module."""

import os
from collections import defaultdict
from pathlib import Path, PureWindowsPath

from .exceptions import LiveMakerException
from .lsb.command import CommandType
from .lsb.lmscript import LMScript


[docs]class PylmProject: def __init__(self, path): self.root = self.find_root(path) if not self.root: raise LiveMakerException(f"{path} is not inside a LM project") self._label_cache = defaultdict(dict)
[docs] @staticmethod def find_root(path): """Return root LM project dir for the specified path.""" path = Path(path).resolve() if not path.exists(): return None if path.is_file(): path = path.parent search_names = {"live.lpb", "ゲームメイン.lsb", "ノベルシステム"} for search_dir in [path] + list(reversed(path.parents)): if set(os.listdir(search_dir)) & search_names: return search_dir return None
[docs] def call_name(self, path): path = Path(path).resolve() if path.suffix not in (".lsb", ".lsc"): raise LiveMakerException(f"{path} is not an LSB") try: relpath = PureWindowsPath(path.relative_to(self.root)) name = f"{relpath.stem}.lsb" return str(relpath.parent / name) except ValueError: raise LiveMakerException(f"{path} is outside this project")
[docs] def update_labels(self, lsb): """Update labels from the specified lsb.""" if not lsb.call_name: raise LiveMakerException("Cannot update labels for lsb without call_name") names = {} lines = {} for cmd in lsb.commands: if cmd.type == CommandType.Label: name = cmd["Name"] names[name] = cmd.LineNo lines[cmd.LineNo] = name cache = self._label_cache[lsb.call_name] if "names" in cache: cache["names"].update(names) else: cache["names"] = names if "lines" in cache: cache["lines"].update(lines) else: cache["lines"] = lines
[docs] def resolve_label(self, ref): """Return tuple(line_no, name) for the specified label reference.""" # Page is a rel path with windows slash, we need to convert it to # system path on posix if isinstance(ref.Label, int) and ref.Label == 0: # start of script is not labeled return None, None path = Path(PureWindowsPath(ref.Page)) call_name = self.call_name(path) if call_name not in self._label_cache: path = self.root / PureWindowsPath(call_name) try: lsb = LMScript.from_file(path, call_name=call_name) self.update_labels(lsb) except LiveMakerException: raise LiveMakerException(f"Could not update labels from {call_name}") cache = self._label_cache[call_name] if isinstance(ref.Label, int): name = cache["lines"].get(ref.Label) if name: return (ref.Label, name) else: line_no = cache["names"].get(ref.Label) if line_no is not None: return (line_no, ref.Label) return None, None