First public commit.

This commit is contained in:
The Dust Council 2026-07-03 19:35:35 -07:00
parent 2a48f52979
commit 4bac9d83ed
288 changed files with 18417 additions and 1076 deletions

74
lenser/exporter.py Normal file
View file

@ -0,0 +1,74 @@
"""Orchestrate: converted image -> self-contained viewer -> Commodore disk image."""
from __future__ import annotations
import os
from . import basicgen, crt, diskimage, imginfo
from .convert.base import Conversion
from .viewer.assemble import (SOURCES, build_cart_rom, build_data_prg,
build_viewer_prg)
def export_disk(conv: Conversion, output_path: str,
disk_format: str | None = None,
disk_name: str | None = None,
include_graphic_file: bool = True,
source_path: str | None = None,
video: str = "pal",
display: str = "key", seconds: int = 0,
layout: str = "unified") -> str:
"""Write ``conv`` to a disk image at ``output_path``.
The disk's first (bootable) file is a self-contained viewer that already
embeds the picture, so ``LOAD"*",8,1`` then ``RUN`` displays it. When
``include_graphic_file`` is set, the picture is also written in a standard
interchange format (e.g. Koala) for use in other C64 art tools. When
``source_path`` is given and there is room, a colourful BASIC "info" program
describing the original image is added too. Returns the absolute path written.
"""
fmt = diskimage.fmt_from_path(output_path, disk_format)
stem = os.path.splitext(os.path.basename(output_path))[0]
name = diskimage.petscii_name(disk_name or stem or "8bitlenser")
# Timing-sensitive viewers (FLI) have an NTSC variant; others run on both.
vkey = conv.viewer
if video == "ntsc" and f"{conv.viewer}_ntsc" in SOURCES:
vkey = f"{conv.viewer}_ntsc"
# Separate layout (viewer code + a standalone "data" file the viewer loads) is
# supported by the simple viewers; FLI/interlace fall back to unified.
separate = layout == "separate" and vkey in ("hires", "multicolor")
viewer_prg = build_viewer_prg(vkey, conv.data, conv.data_addr, display=display,
seconds=seconds, video=video, separate=separate)
files: list[tuple[str, bytes]] = [(name, viewer_prg)]
if separate:
files.append(("data", build_data_prg(conv.data, conv.data_addr)))
if include_graphic_file:
files.extend(conv.extra_files)
if source_path:
# Best effort: never let the info file break a successful export.
try:
info = basicgen.build_info_prg(imginfo.gather(source_path))
budget = diskimage.BLOCKS_FREE[fmt] * 254
info_name = "info" if name != "info" else "picinfo"
if sum(len(b) for _, b in files) + len(info) <= budget:
files.append((info_name, info))
except Exception:
pass
return diskimage.build_disk(output_path, fmt, name, "01", files)
def export_cart(conv: Conversion, output_path: str, source_path: str | None = None,
video: str = "pal", display: str = "forever", seconds: int = 0) -> str:
"""Write ``conv`` as an autostarting 16K C64 .crt cartridge.
Only hires/multicolor/mono fit a 16K cart; FLI/interlace must use a disk."""
if not output_path.lower().endswith(".crt"):
output_path += ".crt"
name = os.path.splitext(os.path.basename(output_path))[0]
rom = build_cart_rom(conv.viewer, conv.data, display=display,
seconds=seconds, video=video)
return crt.write_crt(rom, output_path, name)