################################################################################# # This file is part of Mana Launcher. # Copyright (C) 2021 Jesusalva # # Distributed under the MIT license. ################################################################################# init -3 python: renpy.add_python_directory("python-extra") import requests, zlib, base64, sys, copy, uuid, time, json, traceback import os.path, os, shutil, subprocess, hashlib, zipfile, pbkdf2 import hmac, struct # non-free imports import discord_rpc print("\n[STDBY] Loading Basic functions.......") ############################################################################# ## Compatibility # set PYTHON_VERSION variable (e.g. 2715, 3605 etc.) PYTHON_VERSION="%d%d%02d" % (sys.version_info.major, sys.version_info.minor, sys.version_info.micro) PYTHON_VERSION=int(PYTHON_VERSION) # Ren'Py should come with Python 2.7.10 (2710), but just in case # After all, I only tested with 2.7.10, 2.7.13 and 2.7.15 if (PYTHON_VERSION < 2700): raise KeyboardInterrupt("Unsupported Python version: %d" % PYTHON_VERSION) elif (PYTHON_VERSION > 3000): # This is Python 3.x execute=subprocess.run LEGACY = False if config.developer: import mwclient else: mwclient = None print("Python 3.x detected! Version is %d." % PYTHON_VERSION) else: # This is Python 2.7 execute=subprocess.call LEGACY = True mwclient = None print("Python 2.7 detected! Compatibility mode enabled! (ver: %d)" % PYTHON_VERSION) ############################################################################# # Functions # Encodes something to md5 def md5(string): return hashlib.md5(string.encode()).hexdigest() def md5sum(f): md5=hashlib.md5() fp=open(f, "rb") ct=fp.read() md5.update(ct) rt=copy.copy(md5.hexdigest()) fp.close() del ct return rt # Sanitize a command (strip some flow control chars) # While it covers all control operators and most metacharacters, # it doesn't covers well the reserved words. # ...Of course, it relies on this client not being compromised. def san(cmd): return cmd.replace(";", "").replace("|", "").replace(">", "").replace("<", "").replace("&", "").replace("(", "").replace(")", "").replace("\n", "").replace("[[", "").replace("]]", "") # Smart Print command def stdout(message, bd=False): if config.developer: if renpy.android: if not renpy.is_init_phase(): renpy.notify(message) else: if bd: print("\033[1m%s\033[0m" % message) else: print(message) renpy.write_log("[DEBUG] %s" % message) else: renpy.write_log("[GAME] %s" % message) return # Smart wait def sdelay(delta=0.02): try: renpy.pause(delta, hard=True) except: time.sleep(delta) return # IF Then Else (IFTE) def ifte(ifs, then, elses): if (ifs): return then else: return elses # Returns number of seconds since UNIX EPOCH def now(): return int(time.time()) # File Managment Functions def get_path(path): # Not all systems can record on the game folder if renpy.android: path=path.replace("/", "_") return renpy.config.savedir + "/" + path else: return renpy.config.basedir + "/" + path ############################################################################# # Global classes # We need to override standard list method. Original by Triptych (stackoverflow) class dlist(list): def __setitem__(self, index, value): size = len(self) if index >= size: self.extend(None for _ in range(size, index + 1)) list.__setitem__(self, index, value) # Search for array[?][key]==search in an array of dicts # Returns the dictionary, or returns None def dl_search(array, key, search): try: r=(item for item in array if item[key] == search).next() except: r=None if r is None: stdout("dlsearch: r is None") return r # Search for array[?][key]==search in an array of dicts # Returns the index, or returns -1 def dl_search_idx(array, key, search): try: r=next((i for i, item in enumerate(array) if item[key] == search), None) except: traceback.print_exc() r=-1 return ifte(r is None, -1, r) # Calculates a 2FA Token def calcOTP(key): if not LEGACY and not isinstance(key, bytes): key = bytes(key, 'ascii') msg = struct.pack(">Q", int(time.time()/30)) h = hmac.new(key, msg, hashlib.sha1).digest() if LEGACY: o = ord(h[19]) & 15 else: o = (h[19] & 15) _return = (struct.unpack(">I", h[o:o+4])[0] & 0x7fffffff) % 1000000 _return = "%06d" % _return print("TOTP: %s" % _return) del msg, h, o return _return ############################################################################# ## Some other stuff if renpy.linux: os.environ["APPIMAGELAUNCHER_DISABLE"]="1" ############################################################################# ## Configuration and Defaults if (persistent.discord is None): persistent.discord = True if (persistent.steam is None): persistent.steam = False if (persistent.evol2cli is None): persistent.evol2cli = "manaverse" if (persistent.iconify is None): persistent.iconify = ifte(renpy.windows, False, True) if (persistent.autologin is None): persistent.autologin = True if (persistent.maskpass is None): persistent.maskpass = True ############################################################################# ## Conditional imports if persistent.steam: try: import _renpysteam as steam except: persistent.steam = False ############################################################################# # ["themanaworld.org", "germantmw.de", "moubootaurlegends.org"] if config.developer: VAULT_HOST = "https://localhost:13370" VAULT_CERT = "http://localhost/launcher/cert.pem" else: VAULT_HOST = "https://api.themanaworld.org:13370" VAULT_CERT = False#"https://tmw2.org/launcher/cert.pem" ################### # Vault SSL wrapper vault=requests.Session() if VAULT_CERT: vault.cert = get_path("cert.pem") vault.verify = False #get_path("cert.pem") ############################################################################ ## Retrieve the Vault certificate. Otherwise, we cannot proceed. ## From here and until the end of this code block, ## ALL ERRORS ARE FATAL def build_vault(): while VAULT_CERT: try: ###################################### # Fetch a new PEM Certificate stdout("Fetching SSL Certificate from %s" % VAULT_CERT) r = requests.get(VAULT_CERT) if (r.status_code != 200): raise Exception("\nFailed to fetch Vault SSL Certificate.\n\n Returned HTTP error %d.\n\nClick \"Ignore\" to retry.\n\n" % r.status_code) # Save it with open(get_path("cert.pem"), 'wb') as fd: for chunk in r.iter_content(chunk_size=128): fd.write(chunk) # Presumably all good, so do not continue and break the loop stdout("PEM Download OK") break except: traceback.print_exc() raise stdout("Automatically retry in 5 seconds...") time.sleep(5.0) ## End FATAL mode ############################################################# init 10 python: ######## ## Force Update #if persistent.version != config.version: # persistent.version = config.version # if (persistent.evol2cli is not None and # persistent.host is not None and # handle_client(launch=True, download=False)): # md5check_client(silent=True) persistent.last_run = now() mp = MultiPersistent("vault.themanaworld.org", True) ######## ## Fix keymap config.keymap['screenshot']=[] #config.keymap['screenshot'].remove('s') config.keymap['screenshot'].append('K_F10') config.keymap['screenshot'].append('K_F12') config.keymap['toggle_skip'].remove('K_TAB') config.keymap['fast_skip'].remove('>') config.keymap['toggle_skip'].append('>') config.keymap['focus_down'].append('K_TAB') config.keymap['toggle_fullscreen'].remove('f') ######### Done with pre-init label splashscreen: show TMW2 at truecenter with None #fade python: if persistent.hello is None: p1=2.5 p2=1.5 else: p1=0.5 p2=0.5 renpy.pause(p1) hide TMW2 with None #Dissolve(p2) call before_main_menu if persistent.hello is None: call screen preferences $ persistent.hello=True return label die: $ stdout("Program died.") pause return ####### Defaults default statusmsg = "Not yet initialized" default progress = 0 default responsive = True default has_steam = False default SCR_PROMPT = None default SCR_RESULT = None default SCR_CUTIN = "" default MLP_DEST = None default running = False default mySoul = None ## Command Line Interface init -4 python: USE_DUMMY_DATA = False AUTOLAUNCH = True AUTOWORLD = 0 def parse_command(): global USE_DUMMY_DATA, AUTOLAUNCH, AUTOWORLD global vault, VAULT_HOST, VAULT_CERT parser = renpy.arguments.ArgumentParser() ## Collect args parser.add_argument('args', nargs='+', help="Command-line arguments.") targs = parser.parse_args() args = targs.args ## Debug information if config.developer: print("Args: %s" % str(args)) print("") ## Command Line Defaults for cmd in args: if (cmd == "steam"): print("Steam Mode Enabled") persistent.steam = True elif (cmd == "dummy"): print("Dummy Mode Enabled") USE_DUMMY_DATA = True elif (cmd == "launch"): print("Auto-launch mode Enabled") AUTOLAUNCH = True elif (cmd == "select"): print("Auto-launch mode Disabled") AUTOLAUNCH = True elif (cmd == "remote"): print("Using remote servers...") VAULT_HOST = "https://api.themanaworld.org:13370" VAULT_CERT = False vault=requests.Session() elif (cmd == "local"): print("Using local servers...") VAULT_HOST = "https://localhost:13370" VAULT_CERT = "http://localhost/launcher/cert.pem" vault=requests.Session() vault.cert = get_path("cert.pem") vault.verify = get_path("cert.pem") elif (cmd == "version"): print("%s v%s %s" % (config.name, config.version, "Mirror Lake")) print("%s %s" % (str(renpy.version()), renpy.version_name)) if renpy.linux: print("Linux") elif renpy.windows: print("Windows") else: print("Unsupported plataform") renpy.quit() elif (cmd == "help"): print("=========================================================") print("Commands:") print("steam - Enables Steam login") print("dummy - do not login, use a dummy data for testing") print("launch - skip main menu screen") print("select - show main menu screen") print("version - shows the program version and exits") print("help - shows this help screen and exits") print("") print("Advanced:") print("w - logins to world upon launching") print("") print("Latter commands have precedence over earlier ones.") renpy.quit() elif (cmd.startswith("w")): try: AUTOWORLD=int(cmd[1:]) except: pass # Invalid world id else: print("\033[31;1mWARNING:\033[0m Unrecognized argument: %s" % cmd) return True renpy.arguments.register_command('adv', parse_command) label main_menu: ## Autolaunch - Skip main menu if AUTOLAUNCH and persistent.last_run in range(now()-5, now()+1): $stdout("Auto-launching...") $AUTOLAUNCH=False return call screen main_menu() $ renpy.quit()