def open_tl_file(fn): if fn in tl_file_cache: return tl_file_cache[fn] if not os.path.exists(fn): dn = os.path.dirname(fn) try: os.makedirs(dn) except: pass f = open(fn, "a", encoding="utf-8") f.write(u"\ufeff") else: f = open(fn, "a", encoding="utf-8") if todo: f.write(u"# TO" + "DO: Translation updated at {}\n".format( time.strftime("%Y-%m-%d %H:%M"))) f.write(u"\n") tl_file_cache[fn] = f return f
def init(): """ Creates the editor object, based on the contents of the RENPY_EDIT_PY file. """ global editor editor = SystemEditor() path = os.environ.get("RENPY_EDIT_PY", None) if path is None: return with open(path, "r") as f: source = f.read() code = compile(source, path, "exec") scope = {"__file__": path} exec(code, scope, scope) if "Editor" in scope: editor = scope["Editor"]() # type: ignore return raise Exception("{0} did not define an Editor class.".format(path))
def save_persistent(self, data): """ Saves `data` as the persistent data. Data is a binary string giving the persistent data in python format. """ with disk_lock: if not self.active: return fn = self.persistent fn_tmp = fn + tmp fn_new = fn + ".new" with open(fn_tmp, "wb") as f: f.write(data) safe_rename(fn_tmp, fn_new) safe_rename(fn_new, fn) # Prevent persistent from unpickle just after save self.persistent_mtime = os.path.getmtime(fn) renpy.util.expose_file(fn) self.sync()
def load(filename): """ Loads persistence data from `filename`. Returns None if the data could not be loaded, or a Persistent object if it could be loaded. """ if not os.path.exists(filename): return None # Unserialize the persistent data. try: with open(filename, "rb") as f: s = zlib.decompress(f.read()) persistent = loads(s) except: import renpy.display try: renpy.display.log.write("Loading persistent.") renpy.display.log.exception() except: pass return None persistent._update() return persistent
def scandirfiles_from_remote_file(add, seen): """ Fills out game_files from renpyweb_remote_files.txt. """ # HTML5 remote files index_filename = os.path.join(renpy.config.gamedir, 'renpyweb_remote_files.txt') if os.path.exists(index_filename): files = game_files with open(index_filename, 'r') as remote_index: while True: f = remote_index.readline() metadata = remote_index.readline() if f == '' or metadata == '': # end of file break f = f.rstrip("\r\n") metadata = metadata.rstrip("\r\n") (entry_type, entry_size) = metadata.split(' ') if entry_type == 'image': entry_size = [int(i) for i in entry_size.split(',')] add('/game', f, files, seen) remote_files[f] = {'type': entry_type, 'size': entry_size}
def __init__(self, directory): self.directory = directory # Make the save directory. try: os.makedirs(self.directory) except: pass renpy.util.expose_directory(self.directory) # Try to write a test file. try: fn = os.path.join(self.directory, "text.txt") with open(fn, "w") as f: f.write("Test.") os.unlink(fn) self.active = True except: self.active = False # A map from slotname to the mtime of that slot. self.mtimes = {} # The persistent file. self.persistent = os.path.join(self.directory, "persistent") # The mtime of the persistent file. self.persistent_mtime = 0 # The data loaded from the persistent file. self.persistent_data = None
def open_file(name, mode): f = open(name, mode) f.seek(0, 2) length = f.tell() f.seek(0, 0) return SubFile(f, 0, length, b'')
def save(self): fn = self._filename with open(fn + ".new", "wb") as f: dump(self, f) try: os.rename(fn + ".new", fn) except: os.unlink(fn) os.rename(fn + ".new", fn)
def extract_strings_core(language, destination, merge=False, force=False): if (not force) and (language not in renpy.game.script.translator.strings ): # @UndefinedVariable raise Exception("Language %r does not have any translations." % language) st = renpy.game.script.translator.strings[language] # @UndefinedVariable result = {} if merge: with open(destination, "r") as f: result.update(json.load(f, encoding="utf-8")) for k, v in st.translations.items(): if v and v != k: result[k] = v with open(destination, "w") as f: json.dump(result, f, ensure_ascii=True)
def enable_trace(level): global trace_file global trace_local trace_file = open("trace.txt", "w", buffering=1, encoding="utf-8") if level > 1: trace_local = trace_function else: trace_local = None sys.settrace(trace_function)
def save_bytecode(self): if renpy.macapp: return if self.bytecode_dirty: try: fn = renpy.loader.get_path(BYTECODE_FILE) with open(fn, "wb") as f: data = (BYTECODE_VERSION, self.bytecode_newcache) f.write(zlib.compress(dumps(data), 3)) except Exception: pass
def process_file(fn): """ Adds missing from clauses to `fn`. """ if not os.path.exists(fn): return edits = missing[fn] edits.sort() with open(fn, "rb") as f: data = f.read().decode("utf-8") # How much of the input has been consumed. consumed = 0 # The output. output = u"" for position, target in edits: output += data[consumed:position] consumed = position output += " from {}".format(generate_label(target)) output += data[consumed:] with open(fn + ".new", "wb") as f: f.write(output.encode("utf-8")) try: os.unlink(fn + ".bak") except Exception: pass os.rename(fn, fn + ".bak") os.rename(fn + ".new", fn)
def save_cache(): if not ccache.updated: return if renpy.macapp: return try: data = zlib.compress(dumps(new_ccache, True), 3) with open(renpy.loader.get_path(CACHE_FILENAME), "wb") as f: f.write(data) except Exception: pass
def dialogue_command(): """ The dialogue command. This updates dialogue.txt, a file giving all the dialogue in the game. """ ap = renpy.arguments.ArgumentParser(description="Generates or updates translations.") ap.add_argument("language", help="The language to extract dialogue for.") ap.add_argument("--text", help="Output the dialogue as plain text, instead of a tab-delimited file.", dest="text", action="store_true") ap.add_argument("--strings", help="Output all translatable strings, not just dialogue.", dest="strings", action="store_true") ap.add_argument("--notags", help="Strip text tags from the dialogue.", dest="notags", action="store_true") ap.add_argument("--escape", help="Escape quotes and other special characters.", dest="escape", action="store_true") args = ap.parse_args() tdf = not args.text if tdf: output = os.path.join(renpy.config.basedir, "dialogue.tab") else: output = os.path.join(renpy.config.basedir, "dialogue.txt") with open(output, "w") as f: if tdf: line = [ "Identifier", "Character", "Dialogue", "Filename", "Line Number", "Ren'Py Script", ] f.write("\t".join(line) + "\n") for dirname, filename in renpy.loader.listdirfiles(): if dirname is None: continue filename = os.path.join(dirname, filename) if not (filename.endswith(".rpy") or filename.endswith(".rpym")): continue filename = os.path.normpath(filename) language = args.language if language in ("None", ""): language = None DialogueFile(filename, output, tdf=tdf, strings=args.strings, notags=args.notags, escape=args.escape, language=language) return False
def open_error_file(fn, mode): """ Opens an error/log/file. Returns the open file, and the filename that was opened. """ try: new_fn = os.path.join(renpy.config.logdir, fn) # type: ignore f = open(new_fn, mode) return f, new_fn except Exception: pass try: f = open(fn, mode) return f, fn except Exception: pass import tempfile new_fn = os.path.join(tempfile.gettempdir(), "renpy-" + fn) return open(new_fn, mode), new_fn
def load_face(fn): if fn in face_cache: return face_cache[fn] orig_fn = fn # Figure out the font index. index = 0 if "@" in fn: index, fn = fn.split("@", 1) index = int(index) font_file = None try: font_file = renpy.loader.load(fn) except IOError: if (not renpy.config.developer) or renpy.config.allow_sysfonts: # Let's try to find the font on our own. fonts = [i.strip().lower() for i in fn.split(",")] pygame.sysfont.initsysfonts() for v in pygame.sysfont.Sysfonts.values(): # type: ignore if v is not None: for _flags, ffn in v.items(): for i in fonts: if ffn.lower().endswith(i): font_file = open(ffn, "rb") break if font_file: break if font_file: break if font_file is None: raise Exception("Could not find font {0!r}.".format(orig_fn)) rv = ftfont.FTFace(font_file, index, orig_fn) # @UndefinedVariable face_cache[orig_fn] = rv return rv
def load(fn): """ Returns a file-like object for the given filename. """ try: rv = renpy.loader.load(fn) except renpy.webloader.DownloadNeeded as exception: if exception.rtype == 'music': renpy.webloader.enqueue(exception.relpath, 'music', None) elif exception.rtype == 'voice': # prediction failed, too late pass # temporary 1s placeholder, will retry loading when looping: rv = open(os.path.join(renpy.config.commondir, '_dl_silence.ogg'), 'rb') # type: ignore return rv
def __init__(self): """ Loads the script by parsing all of the given files, and then walking the various ASTs to initialize this Script object. """ # Set us up as renpy.game.script, so things can use us while # we're loading. renpy.game.script = self if os.path.exists(renpy.config.renpy_base + "/lock.txt"): self.key = open(renpy.config.renpy_base + "/lock.txt", "rb").read() else: self.key = None self.namemap = { } self.all_stmts = [ ] self.all_pycode = [ ] self.all_pyexpr = [ ] # A list of statements that haven't been analyzed. self.need_analysis = [ ] self.record_pycode = True # Bytecode caches. self.bytecode_oldcache = { } self.bytecode_newcache = { } self.bytecode_dirty = False self.translator = renpy.translation.ScriptTranslator() self.init_bytecode() self.scan_script_files() self.translator.chain_translates() self.serial = 0 self.digest = hashlib.md5(renpy.version_only.encode("utf-8")) self.loaded_rpy = False self.backup_list = [ ] self.duplicate_labels = [ ]
def load_mappings(): try: with renpy.loader.load("renpycontrollerdb.txt", False) as f: pygame_sdl2.controller.add_mappings(f) except Exception: pass try: with renpy.loader.load("gamecontrollerdb.txt", False) as f: pygame_sdl2.controller.add_mappings(f) except Exception: pass try: with open(os.path.join(renpy.config.renpy_base, "gamecontrollerdb.txt"), "rb") as f: pygame_sdl2.controller.add_mappings(f) except Exception: pass
def scan_comments(filename): rv = [] if filename not in renpy.config.translate_comments: return rv comment = [] start = 0 with open(filename, "r", encoding="utf-8") as f: lines = [ i.rstrip() for i in f.read().replace(u"\ufeff", "").split('\n') ] for i, l in enumerate(lines): if not comment: start = i + 1 m = re.match(r'\s*## (.*)', l) if m: c = m.group(1) if comment: c = c.strip() comment.append(c) elif comment: s = "## " + " ".join(comment) if s.endswith("#"): s = s.rstrip("# ") comment = [] rv.append((start, s)) return rv
def load_from_archive(name): """ Returns an open python file object of the given type from an archive file. """ for prefix, index in archives: if not name in index: continue afn = transfn(prefix) data = [ ] # Direct path. if len(index[name]) == 1: t = index[name][0] if len(t) == 2: offset, dlen = t start = b'' else: offset, dlen, start = t rv = SubFile(afn, offset, dlen, start) # Compatibility path. else: with open(afn, "rb") as f: for offset, dlen in index[name]: f.seek(offset) data.append(f.read(dlen)) rv = io.BytesIO(b''.join(data)) return rv return None
def __init__(self, filename, output, tdf=True, strings=False, notags=True, escape=True, language=None): # @ReservedAssignment """ `filename` The file we're extracting dialogue from. `tdf` If true, dialogue is extracted in tab-delimited format. If false, dialogue is extracted by itself. `strings` If true, extract all translatable strings, not just dialogue. `notags` If true, strip text tags from the extracted dialogue. `escape` If true, escape special characters in the dialogue. """ self.filename = filename commondir = os.path.normpath(renpy.config.commondir) # type: ignore if filename.startswith(commondir): return self.tdf = tdf self.notags = notags self.escape = escape self.strings = strings self.language = language self.f = open(output, "a", encoding="utf-8") self.write_dialogue() self.f.close()
def lint(): """ The master lint function, that's responsible for staging all of the other checks. """ ap = renpy.arguments.ArgumentParser( description= "Checks the script for errors and prints script statistics.", require_command=False) ap.add_argument("filename", nargs='?', action="store", help="The file to write to.") ap.add_argument( "--error-code", action="store_true", help= "If given, the error code is 0 if the game has no lint errros, 1 if lint errors are found." ) args = ap.parse_args() if args.filename: f = open(args.filename, "w", encoding="utf-8") sys.stdout = f renpy.game.lint = True print("\ufeff" + renpy.version + " lint report, generated at: " + time.ctime()) # This supports check_hide. global image_prefixes image_prefixes = {} for k in renpy.display.image.images: image_prefixes[k[0]] = True # Iterate through every statement in the program, processing # them. We sort them in filename, linenumber order. all_stmts = list(renpy.game.script.all_stmts) all_stmts.sort(key=lambda n: n.filename) # The current count. counts = collections.defaultdict(Count) charastats = collections.defaultdict(int) # The current language. language = None menu_count = 0 screen_count = 0 image_count = 0 global report_node for node in all_stmts: if isinstance(node, (renpy.ast.Show, renpy.ast.Scene)): precheck_show(node) for node in all_stmts: if common(node): continue report_node = node if isinstance(node, renpy.ast.Image): image_count += 1 check_image(node) elif isinstance(node, renpy.ast.Show): check_show(node, False) elif isinstance(node, renpy.ast.Scene): check_show(node, True) elif isinstance(node, renpy.ast.Hide): check_hide(node) elif isinstance(node, renpy.ast.With): check_with(node) elif isinstance(node, renpy.ast.Say): check_say(node) counts[language].add(node.what) if language is None: charastats[node.who if node.who else 'narrator'] += 1 elif isinstance(node, renpy.ast.Menu): check_menu(node) menu_count += 1 elif isinstance(node, renpy.ast.Jump): check_jump(node) elif isinstance(node, renpy.ast.Call): check_call(node) elif isinstance(node, renpy.ast.While): check_while(node) elif isinstance(node, renpy.ast.If): check_if(node) elif isinstance(node, renpy.ast.UserStatement): check_user(node) elif isinstance(node, renpy.ast.Label): check_label(node) elif isinstance(node, renpy.ast.Translate): language = node.language elif isinstance(node, renpy.ast.EndTranslate): language = None elif isinstance(node, renpy.ast.Screen): screen_count += 1 check_screen(node) elif isinstance(node, renpy.ast.Define): check_define(node, "define") check_redefined(node, "define") elif isinstance(node, renpy.ast.Default): check_define(node, "default") check_redefined(node, "default") report_node = None check_styles() check_filename_encodings() for f in renpy.config.lint_hooks: f() lines = [] def report_language(language): count = counts[language] if count.blocks <= 0: return if language is None: s = "The game" else: s = "The {0} translation".format(language) s += """ contains {0} dialogue blocks, containing {1} words and {2} characters, for an average of {3:.1f} words and {4:.0f} characters per block. """.format(humanize(count.blocks), humanize(count.words), humanize(count.characters), 1.0 * count.words / count.blocks, 1.0 * count.characters / count.blocks) lines.append(s) print("") print("") print("Statistics:") print("") languages = list(counts) languages.sort(key=lambda a: "" if not a else a) for i in languages: report_language(i) lines.append( "The game contains {0} menus, {1} images, and {2} screens.".format( humanize(menu_count), humanize(image_count), humanize(screen_count))) if renpy.config.developer and renpy.config.lint_character_statistics: lines.append(report_character_stats(charastats)) # Format the lines and lists of lines. for l in lines: if not isinstance(l, (tuple, list)): l = (l, ) for ll in l: if ll.startswith(" * "): prefix = " * " altprefix = " " ll = ll[3:] else: prefix = "" altprefix = "" for lll in textwrap.wrap(ll, 78 - len(prefix)): print(prefix + lll) prefix = altprefix print("") for i in renpy.config.lint_stats_callbacks: i() print("") if renpy.config.developer and (renpy.config.original_developer != "auto"): print("Remember to set config.developer to False before releasing.") print("") print( "Lint is not a substitute for thorough testing. Remember to update Ren'Py" ) print("before releasing. New releases fix bugs and improve compatibility.") if error_reported and args.error_code: renpy.exports.quit(status=1) return False
def __init__(self): self.buffer = '' self.log = open("log", developer=False, append=False, flush=True)
def load_appropriate_file(self, compiled, source, dir, fn, initcode): # @ReservedAssignment data = None # This can only be a .rpyc file, since we're loading it # from an archive. if dir is None: rpyfn = fn + source lastfn = fn + compiled data, stmts = self.load_file(dir, fn + compiled) if data is None: raise Exception("Could not load from archive %s." % (lastfn,)) with renpy.loader.load(fn + compiled) as f: f.seek(-hashlib.md5().digest_size, 2) digest = f.read(hashlib.md5().digest_size) else: # Otherwise, we're loading from disk. So we need to decide if # we want to load the rpy or the rpyc file. rpyfn = dir + "/" + fn + source rpycfn = dir + "/" + fn + compiled renpy.loader.add_auto(rpyfn) if os.path.exists(rpyfn): with open(rpyfn, "rb") as f: rpydigest = hashlib.md5(f.read()).digest() else: rpydigest = None try: if os.path.exists(rpycfn): with open(rpycfn, "rb") as f: f.seek(-hashlib.md5().digest_size, 2) rpycdigest = f.read(hashlib.md5().digest_size) else: rpycdigest = None except Exception: rpycdigest = None digest = None if os.path.exists(rpyfn) and os.path.exists(rpycfn): # Are we forcing a compile? force_compile = renpy.game.args.compile # type: ignore # Use the source file here since it'll be loaded if it exists. lastfn = rpyfn data, stmts = None, None try: if rpydigest == rpycdigest and not force_compile: data, stmts = self.load_file(dir, fn + compiled) if data is None: print("Could not load " + rpycfn) except Exception: renpy.display.log.write("While loading %r", rpycfn) renpy.display.log.exception() if "RENPY_RPYC_EXCEPTIONS" in os.environ: print("While loading", rpycfn) raise if data is None: data, stmts = self.load_file(dir, fn + source) digest = rpydigest elif os.path.exists(rpycfn): lastfn = rpycfn data, stmts = self.load_file(dir, fn + compiled) digest = rpycdigest elif os.path.exists(rpyfn): lastfn = rpyfn data, stmts = self.load_file(dir, fn + source) digest = rpydigest if digest is not None: self.backup_list.append((rpyfn, digest)) if data is None: raise Exception("Could not load file %s." % lastfn) # type: ignore # Check the key. if self.key is None: self.key = data['key'] elif self.key != data['key']: raise Exception(fn + " does not share a key with at least one .rpyc file. To fix, delete all .rpyc files, or rerun Ren'Py with the --lock option.") self.finish_load(stmts, initcode, filename=lastfn) # type: ignore self.digest.update(digest) # type: ignore
def load_file(self, dir, fn): # @ReservedAssignment if fn.endswith(".rpy") or fn.endswith(".rpym"): if not dir: raise Exception("Cannot load rpy/rpym file %s from inside an archive." % fn) base, _, game = dir.rpartition("/") olddir = base + "/old-" + game fullfn = dir + "/" + fn rpycfn = fullfn + "c" oldrpycfn = olddir + "/" + fn + "c" stmts = renpy.parser.parse(fullfn) data = { } data['version'] = script_version data['key'] = self.key or 'unlocked' if stmts is None: return data, [ ] used_names = set() for mergefn in [ oldrpycfn, rpycfn ]: # See if we have a corresponding .rpyc file. If so, then # we want to try to upgrade our .rpy file with it. try: self.record_pycode = False with open(mergefn, "rb") as rpycf: bindata = self.read_rpyc_data(rpycf, 1) if bindata is not None: old_data, old_stmts = loads(bindata) self.merge_names(old_stmts, stmts, used_names) del old_data del old_stmts except Exception: pass finally: self.record_pycode = True self.assign_names(stmts, renpy.parser.elide_filename(fullfn)) pickle_data_before_static_transforms = dumps((data, stmts)) self.static_transforms(stmts) pickle_data_after_static_transforms = dumps((data, stmts)) if not renpy.macapp: try: with open(rpycfn, "wb") as f: self.write_rpyc_header(f) self.write_rpyc_data(f, 1, pickle_data_before_static_transforms) self.write_rpyc_data(f, 2, pickle_data_after_static_transforms) with open(fullfn, "rb") as fullf: rpydigest = hashlib.md5(fullf.read()).digest() self.write_rpyc_md5(f, rpydigest) except Exception: import traceback traceback.print_exc() self.loaded_rpy = True elif fn.endswith(".rpyc") or fn.endswith(".rpymc"): data = None stmts = None with renpy.loader.load(fn) as f: for slot in [ 2, 1 ]: try: bindata = self.read_rpyc_data(f, slot) if bindata: data, stmts = loads(bindata) break except Exception: pass f.seek(0) else: return None, None if data is None: print("Failed to load", fn) return None, None if not isinstance(data, dict): return None, None if self.key and data.get('key', 'unlocked') != self.key: return None, None if data['version'] != script_version: return None, None if slot < 2: self.static_transforms(stmts) else: return None, None return data, stmts
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 MultiPersistent(name, save_on_quit=False): name = renpy.exports.fsdecode(name) if not renpy.game.context().init_phase: raise Exception( "MultiPersistent objects must be created during the init phase.") if renpy.android or renpy.ios: # Due to the security policy of mobile devices, we store MultiPersistent # in the same place as common persistent. # This is better than not working at all. files = [renpy.config.savedir] elif renpy.windows: files = [os.path.expanduser("~/RenPy/Persistent")] if 'APPDATA' in os.environ: files.append( renpy.exports.fsdecode(os.environ['APPDATA']) + "/RenPy/persistent") elif renpy.macintosh: files = [ os.path.expanduser("~/.renpy/persistent"), os.path.expanduser("~/Library/RenPy/persistent") ] else: files = [os.path.expanduser("~/.renpy/persistent")] if "RENPY_MULTIPERSISTENT" in os.environ: files = [renpy.exports.fsdecode(os.environ["RENPY_MULTIPERSISTENT"])] # Make the new persistent directory, why not? try: os.makedirs(files[-1]) # type: ignore except: pass fn = "" # prevent a warning from happening. data = None # Find the first file that actually exists. Otherwise, use the last # file. for fn in files: fn = os.path.join(fn, name) # type: ignore if os.path.isfile(fn): try: data = open(fn, "rb").read() break except: pass rv = _MultiPersistent() if data is not None: try: rv = loads(data) except: renpy.display.log.write("Loading MultiPersistent at %r:" % fn) renpy.display.log.exception() rv._filename = fn if save_on_quit: save_MP_instances.add(rv) return rv
def index_archives(): """ Loads in the indexes for the archive files. Also updates the lower_map. """ # Index the archives. global old_config_archives if old_config_archives == renpy.config.archives: return old_config_archives = renpy.config.archives[:] # Update lower_map. lower_map.clear() cleardirfiles() global archives archives = [ ] max_header_length = 0 for handler in archive_handlers: for header in handler.get_supported_headers(): header_len = len(header) if header_len > max_header_length: max_header_length = header_len archive_extensions = [ ] for handler in archive_handlers: for ext in handler.get_supported_extensions(): if not (ext in archive_extensions): archive_extensions.append(ext) for prefix in renpy.config.archives: for ext in archive_extensions: fn = None f = None try: fn = transfn(prefix + ext) f = open(fn, "rb") except Exception: continue with f: file_header = f.read(max_header_length) for handler in archive_handlers: archive_handled = False for header in handler.get_supported_headers(): if file_header.startswith(header): f.seek(0, 0) index = handler.read_index(f) archives.append((prefix + ext, index)) archive_handled = True break if archive_handled == True: break for dir, fn in listdirfiles(): # @ReservedAssignment lower_map[unicodedata.normalize('NFC', fn.lower())] = fn for fn in remote_files: lower_map[unicodedata.normalize('NFC', fn.lower())] = fn
def open(self): self.f = open(self.fn, "rb") self.f.seek(self.base)