def convert_field(self, value, conversion): value, kwargs = value if conversion is None: return value if not conversion: raise ValueError("Conversion specifier can't be empty.") if set(conversion) - set("rstqulci!"): raise ValueError( "Unknown symbols in conversion specifier, this must use only the \"rstqulci\"." ) if "r" in conversion: value = repr(value) conversion = conversion.replace("r", "") elif "s" in conversion: value = str(value) conversion = conversion.replace("s", "") if not conversion: return value # All conversion symbols below assume we have a string. if not isinstance(value, basestring): value = str(value) if "t" in conversion: value = renpy.translation.translate_string(value) if "i" in conversion: try: value = self.vformat(value, (), kwargs) except RuntimeError: # PY3 RecursionError raise ValueError( "Substitution {!r} refers to itself in a loop.".format( value)) if "q" in conversion: value = value.replace("{", "{{") if "u" in conversion: value = value.upper() if "l" in conversion: value = value.lower() if "c" in conversion and value: value = value[0].upper() + value[1:] return value
def cycle_saves(name, count): """ :doc: loadsave Rotates the first `count` saves beginning with `name`. For example, if the name is auto- and the count is 10, then auto-9 will be renamed to auto-10, auto-8 will be renamed to auto-9, and so on until auto-1 is renamed to auto-2. """ for i in range(count - 1, 0, -1): rename_save(name + str(i), name + str(i + 1))
def detect_user_locale(): import locale if renpy.windows: import ctypes windll = ctypes.windll.kernel32 # type: ignore locale_name = locale.windows_locale.get( windll.GetUserDefaultUILanguage()) elif renpy.android: from jnius import autoclass # type: ignore Locale = autoclass('java.util.Locale') locale_name = str(Locale.getDefault().getLanguage()) elif renpy.ios: import pyobjus # type: ignore NSLocale = pyobjus.autoclass("NSLocale") languages = NSLocale.preferredLanguages() locale_name = languages.objectAtIndex_(0).UTF8String().decode("utf-8") locale_name.replace("-", "_") else: locale_name = locale.getdefaultlocale() if locale_name is not None: locale_name = locale_name[0] if locale_name is None: return None, None normalize = locale.normalize(locale_name) if normalize == locale_name: language = region = locale_name else: locale_name = normalize if '.' in locale_name: locale_name, _ = locale_name.split('.', 1) language, region = locale_name.lower().split("_") return language, region
def write(self, s): if not isinstance(s, str): s = str(s, "utf-8", "replace") if not renpy.config.log_to_stdout: self.real_file.write(s) self.real_file.flush() if renpy.ios: return s = self.buffer + s lines = s.split("\n") try: callbacks = self.get_callbacks() except: callbacks = [] for l in lines[:-1]: self.log.write("%s", l) for i in callbacks: try: i(l) except: pass self.buffer = lines[-1]
def enqueue(self, filenames, loop=True, synchro_start=False, fadein=0, tight=None, loop_only=False, relative_volume=1.0): with lock: for filename in filenames: filename, _, _ = self.split_filename(filename, False) renpy.game.persistent._seen_audio[str(filename)] = True # type: ignore if not loop_only: if tight is None: tight = self.tight self.keep_queue += 1 for filename in filenames: qe = QueueEntry(filename, fadein, tight, False, relative_volume) self.queue.append(qe) # Only fade the first thing in. fadein = 0 self.wait_stop = synchro_start self.synchro_start = synchro_start if loop: self.loop = list(filenames) else: self.loop = [ ]
def check_text_tags(s): """ :doc: lint Checks the text tags in s for correctness. Returns an error string if there is an error, or None if there is no error. """ all_tags = dict(text_tags) custom_tags = renpy.config.custom_text_tags if custom_tags: all_tags.update(custom_tags) self_closing_custom_tags = renpy.config.self_closing_custom_text_tags if self_closing_custom_tags: all_tags.update(dict.fromkeys(self_closing_custom_tags, False)) try: tokens = textsupport.tokenize(str(s)) except Exception as e: return e.args[0] tag_stack = [] for type, text in tokens: # @ReservedAssignment if type != TAG: continue if text[0] == "#": continue # Strip off arguments for tags. if text.find('=') != -1: text = text[:text.find('=')] # Closing tag. if text and text[0] == '/': if not tag_stack: return "Close text tag '%s' does not match an open text tag." % text if tag_stack[-1] != text[1:]: return "Close text tag '%s' does not match open text tag '%s'." % ( text, tag_stack[-1]) tag_stack.pop() continue if text not in all_tags: return "Text tag '%s' is not known." % text if all_tags[text]: tag_stack.append(text) if tag_stack: return "One or more text tags were left open at the end of the string: " + ", ".join( ["'" + i + "'" for i in tag_stack]) return None
def write_dialogue(self): """ Writes the dialogue to the file. """ lines = [] translator = renpy.game.script.translator for label, t in translator.file_translates[self.filename]: if label is None: label = "" for n in t.block: if isinstance(n, renpy.ast.Say): if not n.who: who = "" else: who = n.who what = n.what if self.notags: what = notags_filter(what) if self.escape: what = quote_unicode(what) elif self.tdf: what = what.replace("\\", "\\\\") what = what.replace("\t", "\\t") what = what.replace("\n", "\\n") if self.tdf: lines.append([ t.identifier, who, what, n.filename, str(n.linenumber), n.get_code(what_filter) ]) else: lines.append([what]) if self.strings: lines.extend(self.get_strings()) # If we're tab-delimited, we have line number info, which means we # can sort the list so everything's in order, for menus and stuff. if self.tdf: lines.sort(key=lambda x: int(x[4])) for line in lines: self.f.write("\t".join(line) + "\n")
def interact(type='misc', roll_forward=None, **kwargs): # @ReservedAssignment """ :doc: ui :args: (roll_forward=None, mouse='default') Causes an interaction with the user, and returns the result of that interaction. This causes Ren'Py to redraw the screen and begin processing input events. When a displayable returns a value in response to an event, that value is returned from ui.interact, and the interaction ends. This function is rarely called directly. It is usually called by other parts of Ren'Py, including the say statement, menu statement, with statement, pause statement, call screen, :func:`renpy.input`, among many other functions. However, it can be called directly if necessary. When an interaction ends, the transient layer and all screens shown with transient=True are cleared from the scene lists. The following arguments are documented. As other, undocumented arguments exist for Ren'Py's internal use, please pass all arguments as keyword arguments. `roll_forward` The information that will be returned by this function when a roll forward occurs. (If None, the roll forward is ignored.) This should usually be passed the result of the :func:`renpy.roll_forward_info` function. `mouse` The style of mouse cursor to use during this function. """ if stack is None: raise Exception("Interaction not allowed during init phase.") if renpy.config.skipping == "fast": renpy.config.skipping = None if len(stack) != 1: raise Exception( "ui.interact called with non-empty widget/layer stack. Did you forget a ui.close() somewhere?\nStack was " + ('\n'.join([str(item) for item in stack]))) if at_stack: raise Exception("ui.interact called with non-empty at stack.") renpy.game.context().info._current_interact_type = type rv = renpy.game.interface.interact(roll_forward=roll_forward, **kwargs) renpy.game.context().info._last_interact_type = type if renpy.exports.in_fixed_rollback() and roll_forward is not None: return roll_forward else: return rv
def humanize(n): s = str(n) rv = [] for i, c in enumerate(reversed(s)): if i and not (i % 3): rv.insert(0, ',') rv.insert(0, c) return ''.join(rv)
def filter_text_tags(s, allow=None, deny=None): """ :doc: text_utility Returns a copy of `s` with the text tags filtered. Exactly one of the `allow` and `deny` keyword arguments must be given. `allow` A set of tags that are allowed. If a tag is not in this list, it is removed. `deny` A set of tags that are denied. If a tag is not in this list, it is kept in the string. """ if (allow is None) and (deny is None): raise Exception( "Only one of the allow and deny keyword arguments should be given to filter_text_tags." ) if (allow is not None) and (deny is not None): raise Exception( "Only one of the allow and deny keyword arguments should be given to filter_text_tags." ) tokens = textsupport.tokenize(str(s)) rv = [] for tokentype, text in tokens: if tokentype == PARAGRAPH: rv.append("\n") elif tokentype == TAG: kind = text.partition("=")[0] if kind and (kind[0] == "/"): kind = kind[1:] if allow is not None: if kind in allow: rv.append("{" + text + "}") else: if kind not in deny: rv.append("{" + text + "}") else: rv.append(text.replace("{", "{{")) return "".join(rv)
def substitute(s, scope=None, force=False, translate=True): """ Performs translation and formatting on `s`, as necessary. `scope` The scope which is used in formatting, in addition to the default store. `force` Force substitution to occur, even if it's disabled in the config. `translate` Determines if translation occurs. Returns the substituted string, and a flag that is True if substitution occurred, or False if no substitution occurred. """ if not isinstance(s, basestring): s = str(s) if translate: s = renpy.translation.translate_string(s) # Substitute. if not renpy.config.new_substitutions and not force: return s, False if "[" not in s: return s, False old_s = s if scope is not None: kwargs = MultipleDict(scope, renpy.store.__dict__) # @UndefinedVariable else: kwargs = renpy.store.__dict__ # @UndefinedVariable try: s = formatter.vformat(s, (), kwargs) # type: ignore except Exception: if renpy.display.predict.predicting: # @UndefinedVariable return " ", True raise return s, (s != old_s)
def create_store(name): """ Creates the store with `name`. """ parent, _, var = name.rpartition('.') if parent: create_store(parent) name = str(name) if name in initialized_store_dicts: return initialized_store_dicts.add(name) # Create the dict. d = store_dicts.setdefault(name, StoreDict()) d.reset() pyname = pystr(name) # Set the name. d["__name__"] = pyname d["__package__"] = pyname # Set up the default contents of the store. eval("1", d) for k, v in renpy.minstore.__dict__.items(): if k not in d: d[k] = v # Create or reuse the corresponding module. if name in store_modules: sys.modules[pyname] = store_modules[name] else: store_modules[name] = sys.modules[pyname] = StoreModule( d) # type: ignore if parent: store_dicts[parent][var] = sys.modules[pyname]
def parse_keyword(l, expect): name = l.word() if name is None: l.error(expect) if name not in self.keyword: l.error('%r is not a keyword argument or valid child for the %s statement.' % (name, self.name)) if name in seen_keywords: l.error('keyword argument %r appears more than once in a %s statement.' % (name, self.name)) seen_keywords.add(name) expr = self.parse_comma_expression(l) call_node.keywords.append( ast.keyword(arg=str(name), value=expr), )
def get_strings(self): """ Finds the strings in the file. """ lines = [] filename = renpy.parser.elide_filename(self.filename) for ss in renpy.translation.scanstrings.scan_strings(self.filename): line = ss.line s = ss.text stl = renpy.game.script.translator.strings[None] # @UndefinedVariable # don't include s in common.rpym if s in stl.translations: continue # avoid to include same s stl.translations[s] = s s = renpy.translation.translate_string(s, self.language) if self.notags: s = notags_filter(s) if self.escape: s = quote_unicode(s) elif self.tdf: s = s.replace("\\", "\\\\") s = s.replace("\t", "\\t") s = s.replace("\n", "\\n") if self.tdf: lines.append(["", "", s, filename, str(line)]) else: lines.append([s]) return lines
def filter_alt_text(s): """ Returns a copy of `s` with the contents of text tags that shouldn't be in alt text filtered. This returns just the text to say, with no text tags at all in it. """ tokens = textsupport.tokenize(str(s)) if renpy.config.custom_text_tags or renpy.config.self_closing_custom_text_tags or ( renpy.config.replace_text is not None): tokens = renpy.text.text.Text.apply_custom_tags(tokens) rv = [] active = set() for tokentype, text in tokens: if tokentype == PARAGRAPH: rv.append("\n") elif tokentype == TAG: kind = text.partition("=")[0] if kind.startswith("/"): kind = kind[1:] end = True else: end = False if kind in renpy.config.tts_filter_tags: if end: active.discard(kind) else: active.add(kind) else: if not active: rv.append(text) return "".join(rv)
def textwrap(s, width=78, asian=False): """ Wraps the unicode string `s`, and returns a list of strings. `width` The number of half-width characters that fit on a line. `asian` True if we should make ambiguous width characters full-width, as is done in Asian encodings. """ import unicodedata glyphs = [] for c in str(s): eaw = unicodedata.east_asian_width(c) if (eaw == "F") or (eaw == "W"): gwidth = 20 elif (eaw == "A"): if asian: gwidth = 20 else: gwidth = 10 else: gwidth = 10 g = textsupport.Glyph() g.character = ord(c) g.ascent = 10 g.line_spacing = 10 g.width = gwidth g.advance = gwidth glyphs.append(g) textsupport.annotate_unicode(glyphs, False, 2) renpy.text.texwrap.linebreak_tex(glyphs, width * 10, width * 10, False) return textsupport.linebreak_list(glyphs)
def _update(self): """ Updates the persistent data to be the latest version of the persistent data. """ if self._preferences is None: self._preferences = renpy.preferences.Preferences() # Initialize the set of statements seen ever. if not self._seen_ever: self._seen_ever = {} # Initialize the set of images seen ever. if not self._seen_images: self._seen_images = {} # Initialize the set of chosen menu choices. if not self._chosen: self._chosen = {} if not self._seen_audio: self._seen_audio = {} self._seen_audio = {str(i): True for i in self._seen_audio} # The set of seen translate identifiers. if not self._seen_translates: self._seen_translates = set() # A map from the name of a field to the time that field was last # changed at. if self._changed is None: self._changed = { "_preferences": 0, "_seen_ever": 0, "_chosen": 0, "_seen_audio": 0, "_seen_translates": 0, }
def lookup(self, label): """ Looks up the given label in the game. If the label is not found, raises a ScriptError. """ if isinstance(label, renpy.parser.SubParse): label = label.block[0].name label = renpy.config.label_overrides.get(label, label) original = label rv = self.namemap.get(label, None) if (rv is None) and (renpy.config.missing_label_callback is not None): label = renpy.config.missing_label_callback(label) rv = self.namemap.get(label, None) if rv is None: raise ScriptError("could not find label '%s'." % str(original)) return self.namemap[label]
def bootstrap(renpy_base): global renpy # W0602 import renpy.log # @UnusedImport # Remove a legacy environment setting. if os.environ.get("SDL_VIDEODRIVER", "") == "windib": del os.environ["SDL_VIDEODRIVER"] if not isinstance(renpy_base, str): renpy_base = str(renpy_base, FSENCODING) # If environment.txt exists, load it into the os.environ dictionary. if os.path.exists(renpy_base + "/environment.txt"): evars = { } with open(renpy_base + "/environment.txt", "r") as f: code = compile(f.read(), renpy_base + "/environment.txt", 'exec') exec(code, evars) for k, v in evars.items(): if k not in os.environ: os.environ[k] = str(v) # Also look for it in an alternate path (the path that contains the # .app file.), if on a mac. alt_path = os.path.abspath("renpy_base") if ".app" in alt_path: alt_path = alt_path[:alt_path.find(".app") + 4] if os.path.exists(alt_path + "/environment.txt"): evars = { } with open(alt_path + "/environment.txt", "rb") as f: code = compile(f.read(), alt_path + "/environment.txt", 'exec') exec(code, evars) for k, v in evars.items(): if k not in os.environ: os.environ[k] = str(v) # Get a working name for the game. name = os.path.basename(sys.argv[0]) if name.find(".") != -1: name = name[:name.find(".")] # Parse the arguments. import renpy.arguments args = renpy.arguments.bootstrap() if args.trace: enable_trace(args.trace) if args.basedir: basedir = os.path.abspath(args.basedir) if not isinstance(basedir, str): basedir = basedir.decode(FSENCODING) else: basedir = renpy_base if not os.path.exists(basedir): sys.stderr.write("Base directory %r does not exist. Giving up.\n" % (basedir,)) sys.exit(1) # Make game/ on Android. if renpy.android: if not os.path.exists(basedir + "/game"): os.mkdir(basedir + "/game", 0o777) gamedirs = [ name ] game_name = name while game_name: prefix = game_name[0] game_name = game_name[1:] if prefix == ' ' or prefix == '_': gamedirs.append(game_name) gamedirs.extend([ 'game', 'data', 'launcher/game' ]) for i in gamedirs: if i == "renpy": continue gamedir = basedir + "/" + i if os.path.isdir(gamedir): break else: gamedir = basedir sys.path.insert(0, basedir) if renpy.macintosh: # If we're on a mac, install our own os.start. os.startfile = mac_start # type: ignore # Are we starting from inside a mac app resources directory? if basedir.endswith("Contents/Resources/autorun"): renpy.macapp = True # Check that we have installed pygame properly. This also deals with # weird cases on Windows and Linux where we can't import modules. (On # windows ";" is a directory separator in PATH, so if it's in a parent # directory, we won't get the libraries in the PATH, and hence pygame # won't import.) try: import pygame_sdl2 if not ("pygame" in sys.modules): pygame_sdl2.import_as_pygame() except: print("""\ Could not import pygame_sdl2. Please ensure that this program has been built and unpacked properly. Also, make sure that the directories containing this program do not contain : or ; in their names. You may be using a system install of python. Please run {0}.sh, {0}.exe, or {0}.app instead. """.format(name), file=sys.stderr) raise # If we're not given a command, show the presplash. if args.command == "run" and not renpy.mobile: import renpy.display.presplash # @Reimport renpy.display.presplash.start(basedir, gamedir) # Ditto for the Ren'Py module. try: import _renpy; _renpy except: print("""\ Could not import _renpy. Please ensure that this program has been built and unpacked properly. You may be using a system install of python. Please run {0}.sh, {0}.exe, or {0}.app instead. """.format(name), file=sys.stderr) raise # Load the rest of Ren'Py. import renpy renpy.import_all() renpy.loader.init_importer() exit_status = None try: while exit_status is None: exit_status = 1 try: renpy.game.args = args renpy.config.renpy_base = renpy_base renpy.config.basedir = basedir renpy.config.gamedir = gamedir renpy.config.args = [ ] # type: ignore if renpy.android: renpy.config.logdir = os.environ['ANDROID_PUBLIC'] else: renpy.config.logdir = basedir if not os.path.exists(renpy.config.logdir): os.makedirs(renpy.config.logdir, 0o777) renpy.main.main() exit_status = 0 except KeyboardInterrupt: raise except renpy.game.UtterRestartException: # On an UtterRestart, reload Ren'Py. renpy.reload_all() exit_status = None except renpy.game.QuitException as e: exit_status = e.status if e.relaunch: if hasattr(sys, "renpy_executable"): subprocess.Popen([sys.renpy_executable] + sys.argv[1:]) # type: ignore else: subprocess.Popen([sys.executable, "-EO"] + sys.argv) except renpy.game.ParseErrorException: pass except Exception as e: renpy.error.report_exception(e) pass sys.exit(exit_status) finally: if "RENPY_SHUTDOWN_TRACE" in os.environ: enable_trace(int(os.environ["RENPY_SHUTDOWN_TRACE"])) renpy.display.tts.tts(None) renpy.display.im.cache.quit() if renpy.display.draw: renpy.display.draw.quit() renpy.audio.audio.quit() # Prevent subprocess from throwing errors while trying to run it's # __del__ method during shutdown. if not renpy.emscripten: subprocess.Popen.__del__ = popen_del # type: ignore
def report_exception(e, editor=True): """ Reports an exception by writing it to standard error and traceback.txt. If `editor` is True, opens the traceback up in a text editor. Returns a three-item tuple, with the first item being a simplified traceback, the second being a full traceback, and the third being the traceback filename. """ # Note: Doki Doki Literature club calls this as ("Words...", False). # For what it's worth. import codecs type, _value, tb = sys.exc_info() # @ReservedAssignment # Return values - which can be displayed to the user. simple = io.StringIO() full = io.StringIO() full_tl = traceback_list(tb) simple_tl = filter_traceback_list(full_tl) print(str(renpy.game.exception_info), file=simple) write_traceback_list(simple, simple_tl) print(type.__name__ + ":", end=' ', file=simple) print(str(e), file=simple) print("Full traceback:", file=full) write_traceback_list(full, full_tl) print(type.__name__ + ":", end=' ', file=full) print(str(e), file=full) # Write to stdout/stderr. try: sys.stdout.write("\n") sys.stdout.write(full.getvalue()) sys.stdout.write("\n") sys.stdout.write(simple.getvalue()) except Exception: pass print('', file=full) try: print(str(platform.platform()), file=full) print(renpy.version, file=full) print(renpy.config.name + " " + renpy.config.version, file=full) print(str(time.ctime()), file=full) except Exception: pass simple = simple.getvalue() full = full.getvalue() # Inside of the file, which may not be openable. try: f, traceback_fn = open_error_file("traceback.txt", "w") with f: f.write("\ufeff") # BOM print("I'm sorry, but an uncaught exception occurred.", file=f) print('', file=f) f.write(simple) print('', file=f) print("-- Full Traceback ------------------------------------------------------------", file=f) print('', file=f) f.write(full) try: renpy.util.expose_file(traceback_fn) except Exception: pass try: if editor and ((renpy.game.args.command == "run") or (renpy.game.args.errors_in_editor)): # type: ignore renpy.exports.launch_editor([ traceback_fn ], 1, transient=1) except Exception: pass except Exception: traceback_fn = os.path.join(renpy.config.basedir, "traceback.txt") # type: ignore return simple, full, traceback_fn
def __reduce__(self): return (AudioData, (self.data, str(self)))
def __format__(self, spec): return format(str(self), spec)
def find_target(self, scope=None, update=True): if self.locked and (self.target is not None): return if self._args.prefix is None: if self._duplicatable: prefix = self.style.prefix else: prefix = "" else: prefix = self._args.prefix try: search = [] target = renpy.easy.dynamic_image(self.name, scope, prefix=prefix, search=search) except KeyError as ke: raise Exception( "In DynamicImage %r: Could not find substitution '%s'." % (self.name, str(ke.args[0]))) except Exception as e: raise Exception("In DynamicImage %r: %r" % (self.name, e)) if target is None: error = "DynamicImage %r: could not find image." % (self.name, ) if len(search) == 1: error += " (%r)" % (search[0], ) elif len(search) == 2: error += " (%r, %r)" % (search[0], search[1]) elif len(search) > 2: error += " (%r, %r, and %d more.)" % (search[0], search[1], len(search) - 2) raise Exception(error) if self.raw_target == target: return False if not update: return True raw_target = target # type: renpy.display.core.Displayable old_target = self.target if raw_target._duplicatable: target = raw_target._duplicate(self._args) self.raw_target = raw_target self.target = target renpy.display.render.redraw(self, 0) if not old_target: return True if not isinstance(old_target, renpy.display.motion.Transform): return True if not isinstance(target, renpy.display.motion.Transform): self.target = target = renpy.display.motion.Transform(child=target) target.take_state(old_target) return True
def find_target(self): name = self.name if isinstance(name, renpy.display.core.Displayable): self.target = name return True if not isinstance(name, tuple): name = tuple(name.split()) def error(msg): self.target = renpy.text.text.Text(msg, color=(255, 0, 0, 255), xanchor=0, xpos=0, yanchor=0, ypos=0) if renpy.config.debug: raise Exception(msg) target = None # typing args = [] while name: target = images.get(name, None) if target is not None: break args.insert(0, name[-1]) name = name[:-1] if not name: error("Image '%s' not found." % ' '.join(self.name)) return False if name and (self._args.name == name): error("Image '{}' refers to itself.".format(' '.join(name))) return False args += self._args.args try: a = self._args.copy(name=name, args=args) self.target = target._duplicate(a) except Exception as e: if renpy.config.raise_image_exceptions and ( renpy.config.debug or renpy.config.developer): raise error(str(e)) return False # Copy the old transform over. new_transform = self.target._target() if isinstance(new_transform, renpy.display.transform.Transform): if self.old_transform is not None: new_transform.take_state(self.old_transform) self.old_transform = new_transform else: self.old_transform = None return True
import os import zipfile import json import renpy import threading from renpy.loadsave import clear_slot, safe_rename import shutil disk_lock = threading.RLock() # A suffix used to disambguate temporary files being written by multiple # processes. import time tmp = "." + str(int(time.time())) + ".tmp" class FileLocation(object): """ A location that saves files to a directory on disk. """ def __init__(self, directory): self.directory = directory # Make the save directory. try: os.makedirs(self.directory) except: pass
# The tuple giving the version number. version_tuple = (7, 5, 0, vc_version) # The name of this version. version_name = "TBD" else: # The tuple giving the version number. version_tuple = (8, 0, 0, vc_version) # The name of this version. version_name = "Heck Freezes Over" # A string giving the version number only (8.0.1.123), with a suffix if needed. version_only = ".".join(str(i) for i in version_tuple) if not official: version_only += "u" elif nightly: version_only += "n" # A verbose string giving the version. version = "Ren'Py " + version_only # Other versions. script_version = 5003000 savegame_suffix = "-LT1.save" bytecode_version = 1 ################################################################################
def add(msg, *args): if not msg in added: added[msg] = True msg = str(msg) % args print(msg)
def py_compile(source, mode, filename='<none>', lineno=1, ast_node=False, cache=True, py=None): """ Compiles the given source code using the supplied codegenerator. Lists, List Comprehensions, and Dictionaries are wrapped when appropriate. `source` The source code, as a either a string, pyexpr, or ast module node. `mode` One of "exec" or "eval". `filename` The filename the source comes from. If a pyexpr is given, the filename embedded in the pyexpr is used. `lineno` The line number of the first line of source code. If a pyexpr is given, the filename embedded in the pyexpr is used. `ast_node` Rather than returning compiled bytecode, returns the AST object that would be used. """ if ast_node: cache = False if isinstance(source, ast.Module): return compile(source, filename, mode) if isinstance(source, renpy.ast.PyExpr): filename = source.filename lineno = source.linenumber if py is None: py = source.py if py is None: if PY2: py = 2 else: py = 3 if cache: key = (lineno, filename, str(source), mode, renpy.script.MAGIC) rv = py_compile_cache.get(key, None) if rv is not None: return rv rv = old_py_compile_cache.get(key, None) if rv is not None: py_compile_cache[key] = rv return rv bytecode = renpy.game.script.bytecode_oldcache.get(key, None) if bytecode is not None: renpy.game.script.bytecode_newcache[key] = bytecode rv = marshal.loads(bytecode) py_compile_cache[key] = rv return rv else: key = None source = str(source) source = source.replace("\r", "") if mode == "eval": source = quote_eval(source) line_offset = lineno - 1 try: if mode == "hide": py_mode = "exec" else: py_mode = mode if (not PY2) or (filename in py3_files): flags = py3_compile_flags try: tree = compile(source, filename, py_mode, ast.PyCF_ONLY_AST | flags, 1) except SyntaxError as orig_e: try: fixed_source = renpy.compat.fixes.fix_tokens(source) tree = compile(fixed_source, filename, py_mode, ast.PyCF_ONLY_AST | flags, 1) except: raise orig_e else: try: flags = new_compile_flags tree = compile(source, filename, py_mode, ast.PyCF_ONLY_AST | flags, 1) except Exception: flags = old_compile_flags source = escape_unicode(source) tree = compile(source, filename, py_mode, ast.PyCF_ONLY_AST | flags, 1) tree = wrap_node.visit(tree) if mode == "hide": wrap_hide(tree) fix_missing_locations(tree, 1, 0) ast.increment_lineno(tree, lineno - 1) line_offset = 0 if ast_node: return tree.body rv = compile(tree, filename, py_mode, flags, 1) if cache: py_compile_cache[key] = rv renpy.game.script.bytecode_newcache[key] = marshal.dumps(rv) renpy.game.script.bytecode_dirty = True return rv except SyntaxError as e: if e.lineno is not None: e.lineno += line_offset raise e