100 lines
2.9 KiB
Python
100 lines
2.9 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
"""
|
||
|
|
Generate src/logos_data.inc from the ANSI art files under logos/.
|
||
|
|
|
||
|
|
Each ANSI logo is converted into a NULL-terminated array of C string literals
|
||
|
|
so render.c can embed them directly without needing to parse files at runtime.
|
||
|
|
"""
|
||
|
|
|
||
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
import argparse
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
LOGO_SOURCES = [
|
||
|
|
("logo_86_dos", "logos/86.ans"),
|
||
|
|
("logo_concurrentdos", "logos/concurrentdos.ans"),
|
||
|
|
("logo_dos", "logos/dos.ans"),
|
||
|
|
("logo_dosbian", "logos/dosbian.ans"),
|
||
|
|
("logo_dosbox", "logos/dosbox.ans"),
|
||
|
|
("logo_dosbox_staging", "logos/dosbox_staging.ans"),
|
||
|
|
("logo_dosbox_x", "logos/dosbox_x.ans"),
|
||
|
|
("logo_dr_dos", "logos/drdos.ans"),
|
||
|
|
("logo_freedos", "logos/freedos.ans"),
|
||
|
|
("logo_ms_dos", "logos/msdos.ans"),
|
||
|
|
("logo_novell_dos", "logos/novell.ans"),
|
||
|
|
("logo_pc_dos", "logos/ibmdos.ans"),
|
||
|
|
]
|
||
|
|
|
||
|
|
|
||
|
|
def split_lines(path: Path) -> list[bytes]:
|
||
|
|
"""Return the ANSI file as a list of raw lines without line endings."""
|
||
|
|
data = path.read_bytes()
|
||
|
|
# splitlines() handles CRLF/CR/LF and preserves empty lines.
|
||
|
|
return data.splitlines()
|
||
|
|
|
||
|
|
|
||
|
|
def to_c_literal(line: bytes) -> str:
|
||
|
|
"""Convert a raw line to a C string literal with escaped bytes."""
|
||
|
|
pieces = ['"']
|
||
|
|
for byte in line:
|
||
|
|
if byte == 0x5C: # backslash
|
||
|
|
pieces.append("\\\\")
|
||
|
|
elif byte == 0x22: # double quote
|
||
|
|
pieces.append('\\"')
|
||
|
|
elif byte == 0x07:
|
||
|
|
pieces.append("\\a")
|
||
|
|
elif byte == 0x08:
|
||
|
|
pieces.append("\\b")
|
||
|
|
elif byte == 0x09:
|
||
|
|
pieces.append("\\t")
|
||
|
|
elif byte == 0x0C:
|
||
|
|
pieces.append("\\f")
|
||
|
|
elif byte == 0x0B:
|
||
|
|
pieces.append("\\v")
|
||
|
|
elif 0x20 <= byte <= 0x7E:
|
||
|
|
pieces.append(chr(byte))
|
||
|
|
else:
|
||
|
|
pieces.append(f"\\x{byte:02x}")
|
||
|
|
pieces.append('"')
|
||
|
|
return "".join(pieces)
|
||
|
|
|
||
|
|
|
||
|
|
def generate(output: Path) -> None:
|
||
|
|
root = Path(__file__).resolve().parents[1]
|
||
|
|
lines = [
|
||
|
|
"/* Auto-generated by tools/generate_logos_data.py. Do not edit. */",
|
||
|
|
"",
|
||
|
|
]
|
||
|
|
|
||
|
|
for symbol, rel_path in LOGO_SOURCES:
|
||
|
|
logo_path = root / rel_path
|
||
|
|
if not logo_path.exists():
|
||
|
|
raise FileNotFoundError(f"Missing logo file: {logo_path}")
|
||
|
|
lines.append(f"static const char *const {symbol}[] = {{")
|
||
|
|
for raw_line in split_lines(logo_path):
|
||
|
|
lines.append(f" {to_c_literal(raw_line)},")
|
||
|
|
lines.append(" NULL,")
|
||
|
|
lines.append("};")
|
||
|
|
lines.append("")
|
||
|
|
|
||
|
|
output_path = root / output
|
||
|
|
output_path.write_text("\n".join(lines), encoding="utf-8")
|
||
|
|
|
||
|
|
|
||
|
|
def main() -> None:
|
||
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
||
|
|
parser.add_argument(
|
||
|
|
"-o",
|
||
|
|
"--output",
|
||
|
|
default="src/logos_data.inc",
|
||
|
|
type=Path,
|
||
|
|
help="Path to write the generated include (default: %(default)s)",
|
||
|
|
)
|
||
|
|
args = parser.parse_args()
|
||
|
|
generate(args.output)
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
main()
|