Skip to content

Commit a5cad44

Browse files
committed
Add PNG encoding and screenshot rendering script
- Implement a lightweight PNG writer in `installer/src/png.rs` for encoding 8-bit RGB pixels. - Include functions for generating PNG file structure, CRC checks, and zlib compression. - Add tests to validate PNG encoding and checksum calculations. - Create `tools/render_installer_screenshots.sh` to automate the process of rendering installer screenshots on Windows via WSL. - The script builds the installer, captures screenshots, and recompresses the images for documentation.
1 parent 4e1f76a commit a5cad44

21 files changed

Lines changed: 662 additions & 73 deletions

.github/release-notes.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,8 @@ On Windows:
102102
Get-FileHash .\cinder-installer-windows-x64.exe -Algorithm SHA256
103103
```
104104

105-
Every file above also has a GitHub build attestation. With the [GitHub CLI](https://cli.github.com):
105+
Every file above also has a GitHub build attestation. With the [GitHub CLI](https://cli.github.com)
106+
2.49 or later (older versions, including the 2.45 Ubuntu packages, do not have this command):
106107

107108
```
108109
gh attestation verify cinder-installer-windows-x64.exe -R superwilso/Cinder

CHANGELOG.md

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,18 @@ level the commit history supports; from `v0.1.6` onward, entries are written as
2020

2121
### Fixed
2222

23+
- **An install or update through the installer ended on Sony's player.** *Launcher-tested;
24+
device-unverified.* The installer tells people not to unplug the player while it updates, so the
25+
boot after an install always had a cable in — and a cable at power-on is the escape to Sony's
26+
player, so the install looked as if it had failed. The installer now leaves a one-shot pass on
27+
`/data`: the next boot starts Cinder with the cable in, and the launcher spends the pass on that
28+
boot whether or not Cinder starts, so from the boot after it a cable at power-on reaches Sony's
29+
player again. The launcher honours the pass only if it could delete it; POWER in the logo and the
30+
bad-boot counter still cover that boot, and cinder-home removes any pass left over once it is
31+
healthy. `tools/cinder-install.sh` uses the same pass in place of the persistent opt-out it used
32+
to borrow and give back.
2333
- **The Library sorted lowercase and accented names below "Z".** *Host-tested against names from
24-
the reference library; device-unverified.* Every list compared names byte by byte, so every
34+
the reference library; device-verified 2026-09-14.* Every list compared names byte by byte, so every
2535
capital came before every lowercase letter and every accented letter after `z`: 12 of 310
2636
artists (`alt‐J`, `bôa`, `julie`, `the north` …) and 127 song titles sat under `Zola Jesus`,
2737
while the A–Z rail filed them under their letters. Artists, albums, songs, folders and playlists
@@ -33,12 +43,22 @@ level the commit history supports; from `v0.1.6` onward, entries are written as
3343

3444
### Added
3545

36-
- **Settings ▸ Ignore "The" in artists.** *Host-tested; device-unverified.* Off by default, so
46+
- **Settings ▸ Ignore "The" in artists.** *Host-tested; device-verified 2026-09-14.* Off by default, so
3747
artists sort as written. On, "The Beatles" sorts among the B's — in the Artists tab, the Albums
3848
tab's artist groups and Songs sorted by artist, and on the A–Z rail — the way Sony's own player
3949
files it. Song titles and album names keep their "The" either way. The lists re-sort the moment
4050
it is switched, an open artist or album page stays on what it was showing, and Reset settings
4151
turns it off.
52+
53+
### Changed
54+
55+
- **The README shows the installer's real window** instead of an ASCII sketch of it. The pictures
56+
come from the installer itself: `--screenshots <dir>` (Windows, not in `--help`) renders the
57+
Home, Options and Confirm pages for a stand-in player, reading no drive, and
58+
`tools/render_installer_screenshots.sh` runs it from WSL. `tools/release.sh` re-renders them when
59+
the machine it runs on has Windows interop.
60+
- **Release notes and `SECURITY.md` say which GitHub CLI can check an attestation**: 2.49 or later.
61+
Ubuntu's packaged 2.45 does not have `gh attestation verify`.
4262

4363
## [0.3.6] — 2026-09-14
4464

README.md

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -173,27 +173,15 @@ Linux.
173173

174174
### The three things it does
175175

176-
```
177-
┌────────────────────────────────────────────────────────────────┐
178-
│ Cinder 0.3.1 · stable │
179-
├────────────────────────────────────────────────────────────────┤
180-
│ Player: D:\ [ Rescan ] │
181-
│ Cinder is installed (installer 0.3.0, stable) — Thu Sep 11 │
182-
│ │
183-
│ ┌──────────────────────────────────────────────────────────┐ │
184-
│ │ Install Cinder │ │
185-
│ │ Fresh install: choose the optional parts, then flash. │ │
186-
│ ├──────────────────────────────────────────────────────────┤ │
187-
│ │ Update Cinder │ │
188-
│ │ Same components as last time, new build. │ │
189-
│ ├──────────────────────────────────────────────────────────┤ │
190-
│ │ Uninstall │ │
191-
│ │ Put the stock Sony player back. │ │
192-
│ └──────────────────────────────────────────────────────────┘ │
193-
│ │
194-
│ [ Check for a newer release ] [ Clean up 11 staged files ] │
195-
└────────────────────────────────────────────────────────────────┘
196-
```
176+
<p align="center">
177+
<img src="docs/screenshots/installer-home.png" width="440" alt="The installer's home page: the player it found, what is installed on it, and Install, Update and Uninstall">
178+
</p>
179+
<p align="center">
180+
<img src="docs/screenshots/installer-options.png" width="330" alt="Choosing the optional components, with the selected one described below the list">
181+
<img src="docs/screenshots/installer-confirm.png" width="330" alt="The confirmation page: every choice, what is copied, and what the player does next">
182+
</p>
183+
184+
*The real window, rendered by `tools/render_installer_screenshots.sh` for a stand-in player.*
197185

198186
**Install** asks which optional parts you want, then stages them. **Update** reads the choices
199187
already on the player out of its own `cinder_components.conf` and keeps them, so a new build never

RECOVERY.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ the whole design: the 2026-07-26 brick happened because every escape that existe
1111

1212
| # | Escape | Depends on |
1313
|---|--------|-----------|
14-
| 0 | **Boot with the USB cable connected → stock.** | Nothing. No filesystem, no shell, no counter. |
14+
| 0 | **Boot with the USB cable connected → stock.** Except the first boot after an install or update: the installer leaves a one-shot pass so the player comes back on Cinder with the cable still in, and the launcher spends that pass on that boot whether Cinder starts or not. The boot after it escapes again. | Nothing. No filesystem, no shell, no counter. (A pass the launcher cannot delete is ignored, so a broken `/data` leaves the escape on.) |
1515
| 0b | **Press POWER while the Sony logo shows → stock**, for that boot only (verified 2026-09-11). Once or twice while the logo is up: a press in its first second or two is before the kernel, and after the launcher runs (~10 s) is too late. Holding POWER to switch on is one press and never counts. | The kernel's own log and a shell. No filesystem, no counter. |
1616
| 1 | **Bad-boot counter** hits `MAXBAD=4` → stock, by itself. | A writable `/data` (ext4). |
1717
| 2 | `/contents/cinderhome_off` over USB-MSC → stock. | A mountable `/contents` + a PC. |

SECURITY.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,9 @@ sha256sum -c SHA256SUMS
7272
`SHA256SUMS` is attached to each release. It proves the download matches what the release workflow
7373
produced. On its own it does **not** prove the workflow built this source tree.
7474

75-
Releases tagged after 2026-09-14 also carry a GitHub build attestation for every published file:
75+
Releases from v0.3.5 on also carry a GitHub build attestation for every published file. Checking
76+
it needs GitHub CLI 2.49 or later; older versions, including the 2.45 Ubuntu packages, do not have
77+
the command:
7678

7779
```sh
7880
gh attestation verify cinder-installer-windows-x64.exe -R superwilso/Cinder

cinder-home/deploy/install_cinderhome.sh

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -458,7 +458,9 @@ fi
458458
# after MAXBAD. (The old launcher reset the counter on a blind 60 s timer, which a hung
459459
# process "survives" → it never accumulated → soft-brick. That bug is removed.)
460460
# * ESCAPES, weakest dependency first (each works when the one below it cannot):
461-
# 1. USB cable connected at boot -> stock. No fs, no shell, no counter. Always works.
461+
# 1. USB cable connected at boot -> stock. No fs, no shell, no counter. Always works,
462+
# except the ONE boot after an install: the installer's pass stands it down so the updated
463+
# player comes back on Cinder with the cable in (see CABLE_PASS below).
462464
# 1b. POWER pressed while the logo shows -> stock. No fs either: reads the kernel log.
463465
# (added 2026-09-11 — NOT yet verified on hardware; the cable escape stays until it is)
464466
# 2. /contents/cinderhome_off (USB-MSC) -> stock. Needs a mountable /contents.
@@ -527,7 +529,28 @@ usb_connected() {
527529
}
528530
# Cost is zero on a cable-free boot: nothing sleeps unless a cable is actually present. The 3 s
529531
# re-check rejects the transient CONNECTED blip the gadget emits while enumerating.
530-
if [ ! -f /data/cinder/cable_escape_off ] && [ ! -f /contents/cinderhome_cable_off ] \
532+
#
533+
# FIRST BOOT AFTER AN INSTALL: the escape stands down ONCE (added 2026-09-14). The installer tells
534+
# people not to unplug the player while it updates, so the boot that follows an install always has
535+
# a cable in — and this escape sent every one of them to Sony's player, which looks exactly like a
536+
# failed install ("it refuses to switch into your bootloader now", the first community tester).
537+
# install_cinderhome.sh writes $CABLE_PASS after its sanity gate; this spends it.
538+
# * SPENT BEFORE IT IS HONOURED, on every boot, cable or not. The pass covers the next boot and
539+
# no other, whether or not the new build ever starts: it never waits on the app it would rescue.
540+
# A build that hangs on that boot costs one more restart, and the restart escapes.
541+
# * HONOURED ONLY IF THE DELETE WORKED. A pass that cannot be removed would stand the escape down
542+
# on every boot, so a failed rm leaves the escape armed — this fails toward stock.
543+
# * /data only, never /contents: it is not a switch a PC or a person can leave set.
544+
# * Nothing else stands down. POWER in the logo (1b) and the bad-boot counter cover this boot.
545+
# cinder-home also deletes a leftover pass once it has proven healthy (mark_healthy_maybe).
546+
CABLE_PASS=$STATE/cable_pass_once
547+
CABLE_PASS_SPENT=0
548+
if [ -e "$CABLE_PASS" ]; then
549+
rm "$CABLE_PASS" 2>/dev/null; sync
550+
[ -e "$CABLE_PASS" ] || CABLE_PASS_SPENT=1
551+
fi
552+
if [ "$CABLE_PASS_SPENT" = 0 ] \
553+
&& [ ! -f /data/cinder/cable_escape_off ] && [ ! -f /contents/cinderhome_cable_off ] \
531554
&& usb_connected; then
532555
sleep 3
533556
usb_connected && run_stock "$@"
@@ -818,6 +841,10 @@ log_sv() {
818841
[ -n "$LOGF" ] && ( echo "cinderhome-launch: $*" >> "$LOGF" ) 2>/dev/null
819842
true
820843
}
844+
# Said here, not at rung 0: the log is only chosen above. DEVICE_CHECKLIST 11.9 looks for this line.
845+
if [ "$CABLE_PASS_SPENT" = 1 ]; then
846+
log_sv "cable escape stood down for this boot: first boot after an install (the pass is spent)"
847+
fi
821848
# The redirect rides on a SIMPLE COMMAND, never on `exec`. A redirection failure on a simple
822849
# command is just a non-zero rc; on `exec` it makes sh exit WITHOUT running anything, which is the
823850
# precise shape of the 2026-07-26 brick. /contents also legitimately disappears mid-session during
@@ -943,6 +970,13 @@ fi
943970
# location and the legacy /contents one (an upgrade from a pre-2026-07-26 build leaves those).
944971
"$BB" mkdir -p /data/cinder 2>/dev/null
945972
"$BB" rm -f /data/cinder/off /data/cinder/bootcount /data/cinder/DISABLED_badboot /data/cinder/once_stock 2>/dev/null
973+
# The post-install cable pass ($CABLE_PASS in the launcher above). The installer tells people not to
974+
# unplug, so the boot after this one has a cable in, and without the pass it lands on Sony's player
975+
# and the install looks as if it failed. Written only past the sanity gate, so an aborted install
976+
# never leaves one; the launcher spends it on the next boot. 0644 for the same umask reason as
977+
# search_on below.
978+
echo 1 > /data/cinder/cable_pass_once 2>/dev/null && "$BB" chmod 644 /data/cinder/cable_pass_once 2>/dev/null \
979+
&& echo "cable pass: the next boot starts Cinder with the cable in"
946980
# Library search: an opt-in component with no files, so the choice itself is what gets installed.
947981
# A flag in /data/cinder — machine-written state, off the MSC volume a PC can edit — that
948982
# cinder-home reads at startup. Written or removed on EVERY install, so an Update that turns it
@@ -964,7 +998,8 @@ umount /system 2>/dev/null
964998
echo "== done. reboot to normal; appmgr launches cinder-home as the Home app. =="
965999
echo " SAFETY: a failed/hung launch AUTO-REVERTS to stock after 4 boots (no wbrt)."
9661000
echo " Escapes, in order of how little they depend on:"
967-
echo " 1. boot with the USB CABLE CONNECTED -> stock. Needs no filesystem, always works."
1001+
echo " 1. boot with the USB CABLE CONNECTED -> stock. Needs no filesystem, always works —"
1002+
echo " except the NEXT boot, which ignores the cable once so Cinder starts with it in."
9681003
echo " 2. create /contents/cinderhome_off over USB-MSC -> stock."
9691004
echo " 3. create /contents/cinderhome_clear over USB-MSC -> clears the latch, tries again"
9701005
echo " (same as tools/flash.sh --clear-latch)."

cinder-home/src/main.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1529,6 +1529,13 @@ void mark_healthy_maybe() {
15291529
::sync();
15301530
g_counter_reset = true;
15311531
clog_("healthy: bad-boot counter cleared");
1532+
// The post-install cable pass ($CABLE_PASS in deploy/install_cinderhome.sh). The launcher
1533+
// spends it on the boot after an install and honours it only if that delete worked, so one
1534+
// still here means the delete failed and the cable escape was armed this boot anyway.
1535+
// Removing it now stops a stale pass standing the escape down on some later boot.
1536+
// The path MUST stay in step with $CABLE_PASS.
1537+
if (::unlink("/data/cinder/cable_pass_once") == 0)
1538+
clog_("healthy: removed a leftover post-install cable pass — the cable escape is armed");
15321539
}
15331540
}
15341541

cinder-home/tools/test_launcher.sh

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,22 @@ scenario "only the HW keycode line" stock '{ echo "$BOOT"; HWK
108108
scenario "kernel log with no timestamps" cinder 'printf "(0)[28:pmic_thread_kth]kpd: Power Key generate, pressed=1\n" > $R/proc/klog'
109109
scenario "power escape ignores the cable opt-out" stock ': > $R/data/cinder/cable_escape_off; { echo "$BOOT"; KPD 12.000001 1; } > $R/proc/klog'
110110
scenario "late pwrkey_int_handler line: no count" cinder '{ echo "$BOOT"; printf "<5>[ 6.201337] (0)[28:pmic_thread_kth][Power/PMIC] [pwrkey_int_handler] Release pwrkey\n"; } > $R/proc/klog'
111+
# The post-install cable pass (2026-09-14). The installer says "do not unplug", so the first boot
112+
# after an install always has a cable in, and rung 0 sent every one of them to stock.
113+
# install_cinderhome.sh now leaves a one-shot pass on /data: spent on the next boot whatever
114+
# happens, honoured only if the launcher could delete it, and never a reason to skip POWER.
115+
CABLE='echo CONFIGURED > $R/sys/class/android_usb/android0/state'
116+
pass_state() { [ -e "$1/data/cinder/cable_pass_once" ] && echo present || echo spent; }
117+
scenario "cable + post-install pass -> cinder" cinder "$CABLE"'; : > $R/data/cinder/cable_pass_once'
118+
check " the pass is spent" "$(pass_state "$LAST_R")" spent
119+
check " the log says the escape stood down" "$(grep -c 'cable escape stood down' "$LAST_R/contents/cinderhome.log")" 1
120+
check " the next cable boot escapes again" "$(sh "$LAST_R/launch.sh" 2>/dev/null | grep -c STOCK)" 1
121+
check " and does not claim a pass" "$(grep -c 'cable escape stood down' "$LAST_R/contents/cinderhome.log")" 1
122+
scenario "pass with no cable is spent all the same" cinder ': > $R/data/cinder/cable_pass_once'
123+
check " the pass is spent" "$(pass_state "$LAST_R")" spent
124+
scenario "a pass on /contents grants nothing" stock "$CABLE"'; : > $R/contents/cable_pass_once'
125+
scenario "a pass that cannot be deleted -> stock" stock "$CABLE"'; mkdir -p $R/data/cinder/cable_pass_once/x'
126+
scenario "POWER in the logo beats the pass" stock "$CABLE"'; : > $R/data/cinder/cable_pass_once; { echo "$BOOT"; KPD 3.576327 1; } > $R/proc/klog'
111127
scenario "/contents NOT mounted (the brick)" stock 'printf "rootfs / rootfs rw 0 0\n" > $R/proc/mounts'
112128
# THE SAFETY NET CANNOT BE ARMED. Two ways of saying it, because one of them lies when the test
113129
# runs as root: chmod 555 does not stop uid 0, so on a root shell the counter write SUCCEEDS, the

0 commit comments

Comments
 (0)