from dataclasses import dataclass import datetime import re import os from flask import Flask, send_from_directory, render_template, make_response, url_for from livereload import Server # Importing performs monkeypatching import xml_lexer # NOQA: F401 import ini_lexer # NOQA: F401 app = Flask(__name__) app.jinja_options.setdefault('extensions', []).append('jinja2_highlight.HighlightExtension') HTAG = re.compile(r"]*id=\"([^\"]+)\"[^>]*>([^<]*){part.name}' f'ASSY IDSEGA {part.id}' f'Description{part.description}' '' ) return f'{id_}' def generate_xrpc_list(): output = "" def generate_toc(base, name, route, start=1): parts = route.strip("/").split("/") toc = CONTENTS for i in parts: if i in toc: toc = toc[i] if isinstance(toc, tuple): toc = toc[1] if not isinstance(toc, dict): return "" else: return "" def walk(toc, path, start=1): unordered = len(toc) == 1 and 0 in toc out = f'<{"u" if unordered else "o"}l start="{start}">' for url in toc: if isinstance(toc[url], tuple): name, children = toc[url] elif isinstance(toc[url], str): name, children = toc[url], -1 out += "
  • " if isinstance(url, str): fqu = f"{ROOT}/{path}" if not url.startswith("#"): fqu += "/" fqu += url while "//" in fqu: fqu = fqu.replace("//", "/") if not fqu.endswith((".html", "/")) and "#" not in fqu: fqu += "/" out += f'{name}' else: out += name out += "
  • " if children == -1: continue if children is None: filename = "/".join((TEMPLATES, PAGES_BASE, path, url)) while "//" in filename: filename = filename.replace("//", "/") if url == "": filename += "index.html" if "." not in filename: filename += "/index.html" with open(filename) as page: headers = HTAG.findall(page.read()) children = {} for level, anchor, text in headers: if level in TOC_HTAG_LEVELS: children[f"#{anchor}"] = text if not children: children = None if children is not None: out += walk(children, f"{path}/{url}" if isinstance(url, str) else path) out += f'' return out return walk(toc, route, start) def generate_footer(base, name, route): parts = route.strip("/").split("/") if not parts: return "" toc = CONTENTS path = [] for i in parts[:-1]: if i in toc: path.append(i) toc = toc[i] if isinstance(toc, tuple): toc = toc[1] if not isinstance(toc, dict): toc = None break elif toc == CONTENTS: toc = toc[""] else: toc = None break if toc == CONTENTS and len(parts) == 1: toc = toc[""] if toc is None: siblings = None else: siblings = [i for i in toc.keys() if isinstance(i, str)] try: us_idx = siblings.index(parts[-1]) except ValueError: us_idx = -1 parent = ROOT + "/" + "/".join(parts[:-1]) if not parent.endswith("/"): parent += "/" footer = "" return footer def ioctl(original): original = eval(original) # Unsafe as hell def CTL_CODE(DeviceType, Function, Method, Access): return ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) deviceType = original >> 16 access = (original >> 14) & 0x3 function = (original >> 2) & 0xfff method = original & 0x3 assert hex(CTL_CODE(deviceType, function, method, access)) == hex(original) deviceType = hex(deviceType) if deviceType == "0x9c40": deviceType = "FILE_DEVICE_SEGA" function = hex(function) method = "METHOD_" + ["BUFFERED", "IN_DIRECT", "OUT_DIRECT", "NEITHER"][method] access = ["FILE_ANY_ACCESS", "FILE_READ_ACCESS", "FILE_WRITE_ACCESS"][access] return (f"CTL_CODE({deviceType}, {function}, {method}, {access})") @app.route("/styles.css") def styles(): return send_from_directory(".", "styles.css") @app.route("/tango.css") def tango(): return send_from_directory(".", "tango.css") @app.route("/headers.js") def header_script(): return send_from_directory(".", "headers.js") for i in STATIC: for base, _, files in os.walk(i): for name in files: def handler(base, name): def handler(): return send_from_directory(base, name) return handler local_base = base.replace("\\", "/").strip(".").strip("/") route = local_base + "/" + name if not route.startswith("/"): route = "/" + route handler = handler(base, name) handler.__name__ == route app.add_url_rule(route, route, handler) for base, _, files in os.walk(TEMPLATES + "/" + PAGES_BASE): if ".git" in base: continue if base.startswith(TEMPLATES): base = base[len(TEMPLATES):] for name in files: if name.endswith(".html"): def handler(base, name, route): def handler(): return render_template( os.path.join(base, name).strip("/").replace("\\", "/"), ROOT=ROOT, CANONICAL=ROOT + route, generate_xrpc_list=generate_xrpc_list, generate_toc=lambda start=1: generate_toc(base, name, route, start), generate_footer=lambda: generate_footer(base, name, route), part=part, ioctl=ioctl, ) return handler local_base = base.replace("\\", "/").strip(".").strip("/") if local_base.startswith(PAGES_BASE): local_base = local_base[len(PAGES_BASE):] route = local_base + "/" + name if route.endswith("/index.html"): route = route[:-10] if not route.startswith("/"): route = "/" + route handler = handler(base, name, route) handler.__name__ == route app.add_url_rule(route, route, handler) @app.route("/sitemap.xml") def sitemap(): host_base = HOST + ROOT links = [] for rule in app.url_map.iter_rules(): if "GET" in rule.methods and len(rule.arguments) == 0: url = url_for(rule.endpoint, **(rule.defaults or {})) if not url.endswith(("/", ".html", ".png")): continue path = rule.endpoint if path.endswith("/"): path += "index.html" path = os.path.join(TEMPLATES, PAGES_BASE, path.lstrip("/")) if os.path.exists(path): mod_time = os.path.getmtime(path) mod_time = datetime.datetime.fromtimestamp(mod_time).strftime("%Y-%m-%dT%H:%M:%SZ") else: mod_time = None links.append((host_base + url, mod_time)) response = make_response(render_template("sitemap.xml", urls=links[::-1])) response.headers["Content-Type"] = "application/xml" return response def run_dev(): app.config['TEMPLATES_AUTO_RELOAD'] = True app.config['DEBUG'] = True # app.run(debug=True, port=3000, host="0.0.0.0") server = Server(app.wsgi_app) server.watch(".") server.serve(port=3000) if __name__ == '__main__': run_dev()