Files

84 lines
2.5 KiB
Python

"""Fluffy-compatible modinfo.ini and screenshot discovery."""
from __future__ import annotations
from dataclasses import dataclass
from pathlib import Path
IMAGE_EXTS = (".png", ".jpg", ".jpeg", ".webp")
@dataclass
class ModInfo:
name: str = ""
version: str = ""
author: str = ""
description: str = ""
category: str = ""
screenshot: str = "" # as written in the ini (relative)
nexus_mod_id: str = ""
def parse_modinfo(text: str) -> ModInfo:
"""Tolerant key=value parser; Fluffy files have no section header, but accept one."""
vals: dict[str, str] = {}
for line in text.splitlines():
line = line.strip()
if not line or line[0] in ";#[" or "=" not in line:
continue
k, _, v = line.partition("=")
vals[k.strip().lower()] = v.strip()
return ModInfo(
name=vals.get("name", ""),
version=vals.get("version", ""),
author=vals.get("author", ""),
description=vals.get("description", "").replace("\\n", "\n"),
category=vals.get("category", ""),
screenshot=vals.get("screenshot", ""),
nexus_mod_id=vals.get("nexusid", vals.get("nexus_mod_id", "")),
)
def _read_text(path: Path) -> str:
raw = path.read_bytes()
for enc in ("utf-8-sig", "utf-16", "latin-1"):
try:
return raw.decode(enc)
except UnicodeError:
continue
return raw.decode("utf-8", "replace")
def find_modinfo(root: Path) -> Path | None:
"""modinfo.ini at the root or one directory down."""
for base in [root, *sorted(p for p in root.iterdir() if p.is_dir())]:
for p in base.iterdir():
if p.is_file() and p.name.lower() == "modinfo.ini":
return p
return None
def read_modinfo(root: Path) -> tuple[ModInfo, Path | None]:
p = find_modinfo(root)
if p is None:
return ModInfo(), None
try:
return parse_modinfo(_read_text(p)), p.parent
except OSError:
return ModInfo(), None
def find_screenshot(base: Path, hint: str = "") -> Path | None:
"""Explicit `screenshot=` wins, else screenshot.png|jpg in the mod root (Fluffy convention)."""
if not base.is_dir():
return None
files = {p.name.lower(): p for p in base.iterdir() if p.is_file()}
if hint:
p = files.get(Path(hint).name.lower())
if p:
return p
for ext in IMAGE_EXTS:
p = files.get("screenshot" + ext)
if p:
return p
return None