#!/usr/bin/env bash
#
# The kiosk owns `[Graphics]`; the game may not keep its own opinion of it.
#
# UltraStar rewrites config.ini every time it exits, and among what it writes
# back is the graphics section - the resolution it settled on, the number of
# screens, the depth. On a computer with a keyboard that is a convenience. On
# this machine it is a loaded gun: nobody can reach the options screen to undo
# it, and a resolution the display will not take is a television showing
# nothing while the game runs happily behind it, drawing frames into a mode
# that never reaches the panel.
#
# That is not a hypothetical. On 31 August 2026 this kiosk lost its picture
# after a reboot: the game ran, answered the remote, played the music, and the
# television stayed black. Every package was rolled back one by one - the game
# to four earlier builds, the web service, the theme, two kernels - and the
# screen stayed black through all of it, because a package downgrade cannot
# touch a file in the user's home. Reinstalling from the ISO fixed it in one
# step, which is the same thing said the other way round: what was wrong was
# state the reinstallation wiped, and this file is the only graphics state on
# the machine that the game itself writes.
#
# So before every start the graphics section is put back to what the machine
# decided, and everything the game added to it is dropped. The installer
# records that decision in ARCHSTAR_GAME_GRAPHICS at installation time; a
# machine installed before this existed has no such file, and then the two keys
# the installer has always written are used - which is exactly what it would
# have recorded. An empty resolution is not an oversight: it means "whatever
# the display reports", so the game asks the panel afresh on every start
# instead of trusting a memory of a panel that may not be the one plugged in.
#
# **Safe mode.** The machine's decision is still a decision, and it can be
# wrong - a resolution recorded against a panel that is no longer plugged in,
# a value somebody typed into install.conf. So there is one step below it: with
# `safe-mode` set to `on`, this ignores what the machine decided and writes the
# plainest thing that works on any display, then says so in the journal and
# leaves the setting on until somebody turns it off. That is the lever the 31st
# did not have. It is asked for from the console rather than detected: a game
# drawing into a mode the panel refuses reports nothing wrong - it created its
# OpenGL context, it is playing the music, it answers the remote - so the only
# thing on this machine that knows the television is black is the person
# looking at it.
#
# Nothing here may keep the game from starting. Every failure is reported and
# swallowed: a kiosk whose configuration could not be tidied is a kiosk that
# still plays karaoke, and the unit calls this with a leading `-` as well.

set -uo pipefail

CONFIG="${ARCHSTAR_GAME_CONFIG:-${HOME:-/home/ultrastar}/.ultrastardx/config.ini}"
WANTED="${ARCHSTAR_GAME_GRAPHICS:-/etc/archstar/game-graphics.conf}"
SETTINGS="${ARCHSTAR_SETTINGS_DIR:-/etc/archstar/settings}"

say() { printf '[archstar-fix-game-config] %s\n' "$*"; }

# The two the installer has always written, for a machine installed before the
# file above existed. `FullScreen=On` because a windowed kiosk is a desktop
# nobody can reach, and `Screens=1` because a game splitting itself across two
# displays on a machine with one shows half a picture on it.
fallback_keys() {
    printf '%s\n' 'FullScreen=On' 'Screens=1'
}

# Whether the operator has asked for the step below the machine's own decision.
# Read straight from the settings file the root side writes: this runs before
# the game and long before anything could be told.
safe_mode() {
    [[ "$(cat "${SETTINGS}/safe-mode" 2>/dev/null || true)" == 'on' ]]
}

if [[ ! -f "${CONFIG}" ]]; then
    # No configuration at all: the game writes its own and there is nothing
    # here to correct. Said rather than silent - on this machine the installer
    # writes one, so its absence is worth a line in the journal.
    say "no ${CONFIG}; the game will write its own"
    exit 0
fi

work="$(mktemp)" || { say 'no temporary file; the configuration is left alone'; exit 0; }
keys="$(mktemp)" || { rm -f "${work}"; say 'no temporary file; the configuration is left alone'; exit 0; }
trap 'rm -f "${work}" "${keys}"' EXIT

if safe_mode; then
    # The plainest thing that works on any display, and nothing else: no
    # resolution, no depth, no second screen. `Resolution` left unwritten is
    # not an omission - it means "ask the panel", which is the one answer that
    # cannot be wrong about a panel nobody can see.
    say 'safe mode: ignoring what this machine decided and starting plain'
    fallback_keys >"${keys}"
elif [[ -r "${WANTED}" ]]; then
    # Comments and blank lines are the installer's own; only `Key=value` is a
    # decision, and only decisions are enforced.
    grep -E '^[A-Za-z][A-Za-z0-9_]*=' "${WANTED}" >"${keys}" || true
fi
[[ -s "${keys}" ]] || fallback_keys >"${keys}"

# The section is replaced whole rather than edited key by key: what makes a
# machine blind is as likely to be a key the game *added* as one it changed,
# and a rule that only corrects what it recognises leaves the rest behind.
awk -v keys="${keys}" '
    function emit_wanted(   line) {
        while ((getline line < keys) > 0) print line
        close(keys)
    }
    /^[[:space:]]*\[/ {
        insection = 0
        if (tolower($0) ~ /^[[:space:]]*\[graphics\][[:space:]]*$/) {
            insection = 1
            seen = 1
            print
            emit_wanted()
            next
        }
        print
        next
    }
    insection { next }
    { print }
    END {
        if (!seen) {
            print ""
            print "[Graphics]"
            emit_wanted()
        }
    }
' "${CONFIG}" >"${work}" || { say 'the configuration could not be read; left alone'; exit 0; }

if cmp -s "${CONFIG}" "${work}"; then
    say 'the graphics section is already what this machine decided'
    exit 0
fi

# What is being taken away, named in the journal. A machine that silently
# rewrites somebody's configuration and says nothing is how a day gets lost.
say 'the game had changed the graphics section; putting it back'
diff "${CONFIG}" "${work}" 2>/dev/null | sed 's/^/    /' || true

# Written in place rather than renamed over: the file has an owner and a mode
# the installer set, and a rename would replace them with this process's.
if cat "${work}" >"${CONFIG}"; then
    say 'the graphics section is this machine own again'
else
    say 'the configuration could not be written; the game starts on what it had'
fi
exit 0
