Files
Cesarus85--mjolnir-vr/tools/server_ui_embed.py
T

84 lines
2.9 KiB
Python

#!/usr/bin/env python3
"""Embeds a dedicated server's admin page (port/assets/server_ui: index.html,
app.js, app.css, icon.svg) in the game as C data, for its web port
(port/linux/src/server_http.c):
python tools/server_ui_embed.py OUTPUT.c
Each file is checked first against what the page's Content-Security-Policy
(default-src 'self') would block, or the project forbids: inline scripts and
styles, style= and on*= attributes, string evaluation, external requests
(tools/server_sim.py's csp_problems). A file that breaks one stops the build.
The Linux and Windows builds generate it; the Android and web builds have no
dedicated server.
"""
import hashlib
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parent.parent
UI = Path("port/assets/server_ui")
FILES = (
("index.html", "text/html; charset=utf-8"),
("app.js", "text/javascript; charset=utf-8"),
("app.css", "text/css; charset=utf-8"),
("icon.svg", "image/svg+xml"),
)
sys.path.insert(0, str(ROOT / "tools"))
from server_sim import csp_problems # noqa: E402
def inputs():
"""the files the generated C depends on"""
return [UI / name for name, _ in FILES if (ROOT / UI / name).is_file()]
def main() -> None:
if len(sys.argv) != 2:
sys.exit("usage: server_ui_embed.py OUTPUT.c")
lines = [
"/* generated by tools/server_ui_embed.py from port/assets/server_ui: do not edit */",
"",
"struct server_ui_asset",
"{",
"\tconst char *path;",
"\tconst char *content_type;",
"\tconst unsigned char *data;",
"\tunsigned long size;",
"\tconst char *etag;",
"};",
"",
]
table = []
problems = []
for index, (name, content_type) in enumerate(FILES):
path = ROOT / UI / name
if not path.is_file():
continue
data = path.read_bytes()
problems += [f"{UI / name}: {problem}" for problem in csp_problems(name, data.decode("utf-8"))]
etag = hashlib.sha256(data).hexdigest()[:16]
lines.append(f"static const unsigned char server_ui_file_{index}[] =")
lines.append("{")
for start in range(0, len(data), 24):
lines.append("\t" + ", ".join(str(byte) for byte in data[start:start + 24]) + ",")
lines.append("\t0")
lines.append("};")
table.append(f'\t{{ "{name}", "{content_type}", server_ui_file_{index}, {len(data)}UL, "{etag}" }},')
if problems:
sys.exit("the admin page breaks its Content-Security-Policy:\n " + "\n ".join(problems))
lines.append("")
lines.append("const struct server_ui_asset server_ui_assets[] =")
lines.append("{")
lines += table or ['\t{ "", "", 0, 0, "" },']
lines.append("};")
lines.append(f"const unsigned long server_ui_asset_count = {len(table)}UL;")
Path(sys.argv[1]).parent.mkdir(parents=True, exist_ok=True)
Path(sys.argv[1]).write_text("\n".join(lines) + "\n")
if __name__ == "__main__":
main()