feat(skills): gb-cart-flasher - lettura/scrittura cartucce Game Boy con FlashGBX e lettore USB (Joey Jr, GBxCart RW, GBFlash, Game Bub)

This commit is contained in:
2026-09-25 17:18:39 +02:00
parent e5cfa4840d
commit f7fa38fc69
5 changed files with 484 additions and 0 deletions
+131
View File
@@ -0,0 +1,131 @@
---
name: gb-cart-flasher
description: >
Legge e scrive ROM e salvataggi su cartucce Game Boy, Game Boy Color e Game Boy
Advance con un programmatore di cartucce USB (Joey Jr, GBxCart RW, GBFlash, Game Bub,
cloni/repro) pilotato dalla CLI di FlashGBX. Usare quando l'utente chiede di
identificare il lettore di cartucce collegato, fare un test di connessione, leggere
le informazioni della cartuccia, fare il backup di ROM o salvataggi, scrivere/flashare
una ROM su una cartuccia riscrivibile, ripristinare un backup, o diagnosticare errori
di lettura/scrittura ("No devices found", dump incoerenti, scrittura fallita). NON
usare per emulatori (PyBoy, mGBA), per compilare ROM (GBDK/SDCC, vedere la skill di
progetto), per cartucce di altre console, né per organizzare file ROM su disco.
---
# Programmatore di cartucce Game Boy (FlashGBX + lettore USB)
## Outcome
Cartuccia letta/scritta con **prova verificabile**: info della cartuccia prima e dopo,
backup del contenuto precedente salvato su file, rilettura post-scrittura confrontata
per hash con il file sorgente. Nessuna scrittura senza backup e consenso dell'utente.
## Preconditions
- Lettore collegato e alimentato via USB; cartuccia inserita nel verso corretto
(per Game Boy Camera: viti verso l'alto) e contatti puliti.
- `flashgbx` disponibile (`flashgbx --version` o `python3 -m FlashGBX`); in alternativa
AppImage/.deb dal repo Lesserkuma/FlashGBX.
- Permessi sulla seriale: utente nel gruppo `dialout` (Debian/Fedora) o `uucp`
(Arch/Steam Deck), oppure udev rule, oppure `sudo chmod 0666 /dev/ttyACM*` temporaneo.
- Modalità corretta: `--mode dmg` per GB/GBC, `--mode agb` per GBA.
## Non-negotiable gates
1. **Prima di ogni scrittura**: `--action backup-rom` del contenuto attuale + conferma
esplicita dell'utente (la scrittura è irreversibile sul supporto fisico; il ripristino
è possibile solo riscrivendo il backup).
2. **Il titolo nell'header non identifica il contenuto**: hack/bootleg/repro mantengono
l'header del gioco originale (caso reale: un bootleg di Super Mario Land letto come
`SUPER MARIOLAND`). Per identificare davvero la ROM, avviarla in emulatore (PyBoy).
3. **Originale ≠ riscrivibile**: le cartucce Nintendo originali sono mask ROM (sola
lettura). Sono scrivibili solo flashcart e repro con chip flash. Indizio tipico di
repro: FlashGBX avverte *"the checksum is not correct … normal for reproduction
cartridges"*; conferma definitiva: l'autodetect del profilo flash riesce in scrittura.
4. **Non inventare profili flash**: usare `--flashcart-type autodetect`. Se l'autodetect
fallisce, leggere i profili in `<config>/FlashGBX/flashcarts/*.txt` e chiedere
all'utente prima di forzarne uno.
5. **Non disattivare la verifica**: non usare `--no-verify-write` salvo richiesta esplicita.
6. **Voltaggio**: di default 3,3 V; `--force-5v` solo se la scrittura fallisce e il chip
lo richiede (5 V può danneggiare alcuni chip). Non usarlo "a caso".
## Workflow
### 1. Identificare il lettore (read-only)
```bash
lsusb
ls -l /dev/ttyACM* /dev/ttyUSB* 2>/dev/null
journalctl -k --no-pager | grep -iE "cdc_acm|ch341|usb.*tty" | tail -20
```
VID:PID attesi (vedi `references/hardware-usb.md`): Joey Jr `0483:5740`,
GBxCart RW / GBFlash `1a86:7523`, Game Bub `1209:b010`. Un device CDC generico
"STM32 Virtual ComPort" è quasi sempre un Joey Jr (o clone).
### 2. Test di connessione + info cartuccia (read-only)
```bash
flashgbx --cli --mode dmg --action info
```
Attesi: riga `Connected to <device> – Firmware <ver> (/dev/ttyXXX)` e il blocco
`Cartridge Information:` con ROM Title, ROM Size, Mapper Type, Save Type, Boot Logo.
`Boot Logo: OK` + checksum coerente = lettura buona. Output non conforme → contatti
sporchi, cartuccia non inserita o modalità sbagliata (`dmg` vs `agb`).
### 3. Backup del contenuto attuale (obbligatorio prima di scrivere)
```bash
mkdir -p "$HOME/gb-cart-backups"
flashgbx --cli --mode dmg --action backup-rom --overwrite "$HOME/gb-cart-backups/<titolo>_$(date +%F).gb"
```
Registrare hash e CRC32 stampati da FlashGBX. Lo stesso comando con
`--action backup-save` salva la SRAM (usa `--save-filename-add-datetime` per non
sovrascrivere; `--store-rtc` se la cartuccia ha un RTC).
### 4. Scrittura della ROM (azione irreversibile)
Dopo il gate 1 (backup + consenso):
```bash
printf 'y\n' | flashgbx --cli --mode dmg --action flash-rom \
--flashcart-type autodetect /percorso/rom.gb
```
- Atteso: riga `Flashcart Profile: …` con il profilo scelto, poi
**`The ROM was written and verified successfully!`**.
- `--compare-sectors` (default on) riscrive solo i settori diversi.
- `--prefer-chip-erase` se la scheda supporta sia sector sia full chip erase.
- ROM più corta del chip: normale, si scrive dall'indirizzo 0; una ROM "ROM only"
da 32 KiB parte comunque su schede MBC1/MBC5 perché a reset i banchi 0 e 1 mappano
i primi 32 KiB. FlashGBX avvisa solo se il file è **più grande** del chip.
- In CLI non interattiva i prompt di conferma (`[y/N]`) vanno alimentati con `printf 'y\n'`
oppure si usa la modalità interattiva (`--action interactive`).
### 5. Verifica (exit criteria)
1. Ripetere `--action info`: deve mostrare il **nuovo** ROM Title / ROM Size.
2. Rileggere la ROM e confrontarla con il sorgente:
```bash
printf 'y\n' | flashgbx --cli --mode dmg --action backup-rom --overwrite /tmp/verify.gb
cmp /tmp/verify.gb /percorso/rom.gb && echo IDENTICI
```
3. Conservare il backup del contenuto precedente e riportare all'utente:
device+firmware, profilo flash, tensione, hash del backup e della verifica.
4. Registrare l'evidenza in memoria condivisa (qmem: progetto della skill o `host-<host>`)
con path dei file e hash — non duplicare qui la procedura.
## Troubleshooting (dettaglio in `references/cartucce-profili-errori.md`)
- *"No devices found"* con GBxCart RW/GBFlash su Linux: il modulo `brltty` cattura i
CH340/341 → rimuoverlo/blacklistarlo.
- Errori di lettura intermittenti o dump incoerenti: pulire i contatti con IPA 99%,
reinserire la cartuccia, evitare hub USB non alimentati.
- `Auto-detection failed` in scrittura: `--flashcart-type` con il nome esatto del profilo.
- Scrittura fallita a 3,3 V: ritentare con `--force-5v` solo dopo aver verificato il chip.
- Salvataggi "batteryless" (ROM patchate senza batteria): servono `--dmg-savetype
batteryless` e `--bl-offset/--bl-size/--bl-layout`.
## Salvataggio
- La procedura vive qui; in qmem vanno solo **puntatori** a questa skill ed **evidenze
di esecuzione** (device, firmware, profilo, tensione, path, hash) nei progetti
`gb-cart-flasher` (evidenze generiche) e `host-<hostname>` (dettagli macchina-specifici:
gruppo/permessi, udev, seriali, lettore posseduto).
## Resource loading
- `references/flashgbx-cli.md` — riferimento completo CLI di FlashGBX (azioni, opzioni,
parsing output, exit code, config dir, GUI).
- `references/hardware-usb.md` — VID:PID dei lettori, udev/permessi, firmware, modalità
DMG/AGB, conflitti driver.
- `references/cartucce-profili-errori.md` — mask ROM vs repro/flashcart, categorie di
cartucce scrivibili, tensione/erase, batteryless, catalogo errori.
- `scripts/gb-cart.sh` — wrapper deterministico (detect/info/backup/flash con backup
obbligatorio e confronto hash finale).
@@ -0,0 +1,73 @@
# Cartucce, profili flash ed errori
## Che cartuccia ho davanti?
| Tipo | ROM | Riscrivibile | Note |
|---|---|---|---|
| Originale Nintendo (GB/GBC/GBA) | mask ROM | **no** | solo backup; la scrittura fallisce o non ha effetto |
| Repro / bootleg (PCB cinesi) | chip flash (spesso più capiente dell'header) | **sì** | spesso MBC1/MBC5 emulati; riscrivibili con FlashGBX |
| Flashcart moderne (insideGadgets, EZ-Flash, ModRetro, MidnightTrace, GBFlash…) | flash + eventuale FRAM/SRAM | **sì** | profili dedicati in FlashGBX |
| Cartuccia di sviluppo / dev cart | flash, header libero | **sì** | può richiedere profilo manuale |
| Multicart / X-in-1 | flash grande + menu | **sì** | riscrivibile, ma il menu va ricostruito |
Indizi operativi:
- **`the checksum is not correct … normal for reproduction cartridges`** → quasi sempre repro.
- L'header dichiara `ROM Size` minore del chip reale (es. 64 KiB dichiarati su un flash da
4 MiB): normale, i repro usano chip grandi per più giochi.
- L'autodetect del profilo flash **riesce** durante la scrittura → chip flash presente e
quindi cartuccia riscrivibile; se fallisce su una cartuccia "originale" è il comportamento
atteso (mask ROM).
**Il titolo nell'header non prova l'identità del gioco.** Hack, traduzioni e bootleg
conservano l'header originale (caso reale: un bootleg di Super Mario Land con Pikachu
letto come `SUPER MARIOLAND`, `MBC1`, 64 KiB). Per sapere cosa c'è davvero dentro:
avviare il dump in emulatore (PyBoy headless: `pyboy` con `window="null"`, `tick()` senza
argomenti — con `tick(1, False)` lo schermo non si aggiorna) o confrontare con un dump noto.
## Header e mapper: perché una ROM "ROM only" da 32 KiB parte su una scheda MBC
A reset MBC1 e MBC5 mappano il banco 0 a `0x0000-0x3FFF` e il banco 1 a `0x4000-0x7FFF`:
una ROM da 32 KiB occupa esattamente quei due banchi, quindi parte anche se l'header
dichiara `cart type 0x00 (ROM only)` e la scheda monta un MBC. Scrivere una ROM più
piccola del chip è quindi normale; FlashGBX avvisa solo se il file è **più grande** del
flash disponibile.
## Scrittura: tensione, erase, verifica
- **Tensione**: default 3,3 V. Alcuni chip richiedono 5 V → `--force-5v`, ma 5 V può
danneggiare chip non tolleranti: provarlo solo dopo un errore e con cartuccia sacrificabile.
- **Erase**: `sector erase` (più veloce, solo i settori da riscrivere) di default quando
disponibile; `--prefer-chip-erase` per cancellare tutto il chip (utile dopo scritture
parziali o contenuti corrotti).
- **`--compare-sectors`** (default on) salta i settori già identici: meno usura, ma non
ripara settori logicamente corretti e fisicamente deboli.
- **Verifica**: FlashGBX rilegge e confronta da sé (`written and verified successfully`).
Aggiungere comunque una rilettura indipendente con `cmp`/`sha256sum` per l'evidenza.
## Salvataggi
- Tipi: `None`, `SRAM` (con batteria), `FRAM`, `EEPROM`, **batteryless** (dati dentro la ROM).
- Batteryless: servono `--dmg-savetype batteryless` e `--bl-offset/--bl-size/--bl-layout`
(0 = continuo, 1 = prima metà del banco ROM, 2 = seconda metà).
- RTC (Pokémon Oro/Argento, ecc.): `--store-rtc` in backup; il ripristino dei registri RTC
è supportato solo da alcuni profili.
- Su repro con ROM patchate "batteryless" il backup/restore del save può non funzionare:
ROM e salvataggio condividono lo stesso chip flash (il dump completo contiene anche il save).
## Catalogo errori
| Sintomo | Causa probabile | Azione |
|---|---|---|
| `No devices found` | permessi, `brltty` sui CH340/341, cavo solo-carica, porta occupata | gruppo `dialout`/`uucp` o udev; rimuovere `brltty`; collegare diretto; chiudere altri programmi |
| `Couldn't read cartridge header` | cartuccia non inserita/contatti sporchi, `--mode` sbagliato | pulire con IPA 99%, reinserire, correggere `dmg`/`agb` |
| `Invalid data was detected …` | lettura instabile o header incoerente | pulire i contatti; `--ignore-bad-header` solo se l'incoerenza è voluta/nota |
| `Auto-detection failed` in scrittura | chip flash non in tabella, alimentazione, contatti | verificare il profilo in `<config>/FlashGBX/flashcarts/*.txt` e passarlo con `--flashcart-type`; chiedere conferma all'utente |
| `The ROM was dumped, but the checksum is not correct` | repro/bootleg/prototipo/overdump | non è di per sé un errore: valutare `Boot Logo: OK` e un secondo dump identico |
| Scrittura fallita a 3,3 V | chip che richiede 5 V | ritentare con `--force-5v` (rischio dichiarato) |
| Scrittura fallita senza altri indizi | profilo sbagliato o chip usurato | provare `--prefer-chip-erase`, poi profilo manuale; cartuccia possibile a fine vita |
| `The target file … already exists` | file di backup già presente | `--overwrite` o nome nuovo (`--save-filename-add-datetime`) |
| `seems to support ROMs that are up to X, but the file is bigger` | file troppo grande per il profilo | verificare di aver scelto la ROM giusta |
| Game Boy Camera non letta | inserita al contrario | contatti/viti verso l'alto |
## Buone pratiche di verifica
1. **Due dump identici** della stessa cartuccia (hash uguali) per escludere letture instabili;
su cartuccia originale il checksum globale deve tornare.
2. Conservare il dump con hash nel nome o in un `SHA256SUMS` accanto al file.
3. Per un dump di conservazione: due lettori diversi o due sessioni diverse prima di
dichiararlo valido.
4. Non distribuire ROM di giochi commerciali: la copia è per uso personale/backup.
@@ -0,0 +1,91 @@
# FlashGBX — riferimento CLI
Autore: Lesserkuma (github.com/Lesserkuma/FlashGBX, GPL-3.0-or-later).
Verificato su **FlashGBX 5.0.1** (Python 3.10+; su questa macchina Python 3.14, pyserial 3.5).
Installazione: `pip3 install "FlashGBX[qt6]"` (o `[qt5]`, o senza extra per la sola CLI),
oppure AppImage/.deb dai release GitHub. Launcher tipico: `flashgbx` oppure
`python3 -m FlashGBX`. `python3 -m FlashGBX --version` per la versione.
## Modalità di esecuzione
- **GUI** (default): senza argomenti, oppure doppio click.
- **CLI**: `--cli`, oppure automaticamente se si passa `--mode` o `--action`.
- **Console interattiva**: `--action interactive` (comandi manuali sul device).
- Config dir: `~/.config/FlashGBX` su Linux (opzione `--cfgdir appdata|subdir`);
contiene `settings.ini`, i profili flashcart (`flashcarts/*.txt`) e i log.
## Sintassi
```
flashgbx [--cli] --mode {dmg,agb} --action <azione> [opzioni] [path]
```
`path` è facoltativo in lettura (default `auto` = nome generato), **obbligatorio in scrittura**.
Se manca e l'azione lo richiede, la CLI lo chiede a terminale.
## Azioni (`--action`)
| Azione | Effetto | Scrive sulla cartuccia |
|---|---|---|
| `info` | legge l'header e stampa `Cartridge Information` | no |
| `backup-rom` | dump della ROM su file | no |
| `flash-rom` | scrittura di una ROM (richiede backup + consenso) | **sì** |
| `backup-save` | dump della SRAM/save (+ RTC se `--store-rtc`) | no |
| `restore-save` | riscrive la SRAM da file | **sì** |
| `erase-save` | cancella la SRAM | **sì** |
| `gbcamera-extract` | estrae le foto da un backup di Game Boy Camera | no |
| `interactive` | console interattiva sul device | dipende |
| `fwupdate-joeyjr` (e simili) | aggiorna il firmware del lettore, se supportato | — |
## Opzioni principali (CLI)
Generali: `--cli`, `--mode dmg|agb`, `--action`, `--overwrite`, `--language`,
`--cfgdir appdata|subdir`, `--debug`, `--reset`, `--wait`.
Device: `--device-port /dev/ttyACM0` (forza la porta), `--device-limit-baudrate`.
Scrittura ROM: `--flashcart-type autodetect|<nome profilo>` (default `autodetect`),
`--prefer-chip-erase` (full chip erase invece di sector erase),
`--force-5v` (forza 5 V, default 3,3 V), `--no-verify-write`,
`--compare-sectors` (default **on**: riscrive solo i settori diversi).
Cartuccia/header: `--ignore-bad-header` (non si ferma se l'header è incoerente),
`--dmg-romsize`, `--dmg-mbc`, `--dmg-savetype`, `--agb-romsize`, `--agb-savetype`,
`--bl-offset`, `--bl-size`, `--bl-layout 0|1|2` (salvataggi batteryless),
`--store-rtc`, `--keep-calibration` (e-Reader).
Backup/save: `--save-filename-add-datetime`, `--generate-dump-report`,
`--gbcamera-palette`, `--gbcamera-outfile-format`, `--gbcamera-extract`.
## Esempi
```bash
# info cartuccia DMG
flashgbx --cli --mode dmg --action info
# backup ROM (non sovrascrive per default: chiede)
flashgbx --cli --mode dmg --action backup-rom --overwrite ~/gb-cart-backups/mario.gb
# scrittura con autodetect del chip flash, non interattiva
printf 'y\n' | flashgbx --cli --mode dmg --action flash-rom \
--flashcart-type autodetect ~/roms/gioco.gb
# save backup di una cartuccia GBA con data nel nome file
flashgbx --cli --mode agb --action backup-save --save-filename-add-datetime ~/saves/
# force del chip (autodetect fallito) e 5 V
flashgbx --cli --mode dmg --action flash-rom \
--flashcart-type "insideGadgets 4 MiB (S29GL032M)" --force-5v ~/roms/gioco.gb
```
## Output e parsing
- Connessione: `Connected to <Device> – Firmware <ver> (<data>) on /dev/ttyXXX`.
- Info: blocco `Cartridge Information:` con `ROM Title`, `Revision`, `Platform`,
`Real Time Clock`, `Boot Logo`, `ROM Checksum`, `ROM Size`, `Save Type`, `Mapper Type`.
- Scrittura: `Flashcart Profile:` + `The following ROM file will now be written …`,
barra di avanzamento con `\r` (nei log: `tr '\r' '\n'`), quindi
`The ROM was written and verified successfully!` (o messaggio d'errore in rosso).
- Backup: stampa `CRC32` e `SHA-1`; se il **checksum globale** della ROM non torna,
avvisa che è normale per repro/bootleg/prototipi — non è di per sé un errore di lettura,
ma va interpretato insieme a `Boot Logo: OK`.
- Progresso/prompt: i `[y/N]` in modalità non interattiva vanno alimentati da stdin.
- Exit code: 0 = successo; 1 = errore (header illeggibile, dati non validi, ecc.).
## Alternative coperte
Lo stesso tool pilota **GBxCart RW**, **GBFlash**, **Joey Jr** e **Game Bub**; la scelta
del driver è automatica in base a VID:PID. Per hardware non supportato servono i tool
del produttore (es. YAGB per l'omonimo dumper STM32) o librerie specifiche (pyGBx).
@@ -0,0 +1,57 @@
# Hardware, USB e permessi
## Tabella VID:PID dei lettori supportati da FlashGBX
| Lettore | VID:PID | Chip/ifaccia | Note |
|---|---|---|---|
| **Joey Jr** (BennVenn) | `0483:5740` | STM32 USB CDC (`cdc_acm`) | si presenta come "STM32 Virtual ComPort"; supportato da firmware CFW `cfw_id = L` (FlashGBX avvisa se gira il firmware GUI del venditore) |
| **GBxCart RW** (insideGadgets) | `1a86:7523` | CH340/CH341 (`ch341`) | richiede profilo flashcart; attenzione al conflitto `brltty` |
| **GBFlash** (simonkwng) | `1a86:7523` | CH340/CH341 | testato v1.2/v1.3 |
| **Game Bub** | `1209:b010` | CDC | PID di progetto (pid.codes) |
Un device `0483:5740` che *non* risponde al protocollo può essere un qualsiasi
progetto STM32 generico (es. dumper DIY, programmer SPI → `flashrom -p serprog`).
Percorsi tipici: `/dev/ttyACM0` (CDC), `/dev/ttyUSB0` (CH340).
## Identificazione rapida
```bash
lsusb # VID:PID + produttore
ls -l /dev/ttyACM* /dev/ttyUSB* # porta e gruppo
udevadm info -q property -n /dev/ttyACM0 # ID_VENDOR_ID, ID_MODEL, ID_SERIAL
journalctl -k --no-pager | grep -iE "cdc_acm|ch341|ttyACM|ttyUSB" | tail
```
FlashGBX conferma l'identità con la riga
`Connected to Joey Jr – Firmware L15 (…) on /dev/ttyACM0`.
## Permessi
1. Gruppo seriale (permanente, richiede nuovo login): `sudo usermod -a -G dialout $USER`
(Fedora/Debian) o `uucp` (Arch/Steam Deck).
2. Regola udev permanente, es. `/etc/udev/rules.d/50-flashgbx.rules`:
```
SUBSYSTEM=="tty", ATTRS{idVendor}=="0483", ATTRS{idProduct}=="5740", MODE="0666"
SUBSYSTEM=="tty", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", MODE="0666"
```
poi `sudo udevadm control --reload-rules && sudo udevadm trigger`.
3. Temporaneo: `sudo chmod 0666 /dev/ttyACM0` (da rinnovare a ogni riconnessione).
Verifica del gruppo: `id | tr ',' '\n' | grep -E 'dialout|uucp'`.
`crw-rw---- root dialout` + utente in `dialout` = accesso in scrittura senza sudo.
## Conflitti noti
- **`brltty`** (driver per display braille) cattura i chip CH340/CH341: con GBxCart RW o
GBFlash "No devices found" anche con cavo dati valido → `sudo dnf remove brltty` /
`sudo apt remove brltty` o blacklist del modulo, poi reboot. Non riguarda il Joey Jr.
- Cavi USB solo-carica: la porta compare ma non comunica.
- Hub USB non alimentati o porte frontali: errori intermittenti → collegare diretto.
- Altri programmi che aprono la seriale (ModemManager, IDE, monitor seriale) possono
bloccare la porta: chiuderli.
## Firmware del lettore
- Joey Jr: aggiornabile da FlashGBX (`Firmware Update for Joey Jr` in GUI, oppure
`--action fwupdate-joeyjr`). Firmware più recenti di quelli noti al tool producono
un avviso non bloccante. Il firmware "GUI" del venditore non è compatibile con FlashGBX.
- GBFlash/GBxCart RW: aggiornamento con gli strumenti del produttore.
## Modalità piattaforma
- `--mode dmg` → Game Boy / Game Boy Color (DMG). Copre anche le repro GB in formato DMG.
- `--mode agb` → Game Boy Advance. La modalità sbagliata produce header illeggibili o
richieste di `--ignore-bad-header`: correggerla prima di ogni altra diagnosi.
+132
View File
@@ -0,0 +1,132 @@
#!/usr/bin/env bash
# gb-cart.sh — wrapper deterministico per FlashGBX (lettore di cartucce Game Boy).
# Niente path macchina-specifici: usa $HOME e le variabili d'ambiente.
#
# GB_MODE=dmg|agb modalità piattaforma (default: dmg)
# FLASHGBX=flashgbx eseguibile (default: flashgbx dal PATH)
# GB_BACKUP_DIR=... cartella backup (default: $HOME/gb-cart-backups)
#
# Uso:
# gb-cart.sh detect
# gb-cart.sh info
# gb-cart.sh backup-rom [file.gb]
# gb-cart.sh backup-save [file.sav]
# gb-cart.sh flash-rom rom.gb [--flashcart-type NAME] [--force-5v] ...
# gb-cart.sh verify file.gb
#
# flash-rom fa SEMPRE il backup del contenuto attuale prima di scrivere e chiede
# conferma esplicita (GB_CART_ASSUME_YES=1 per l'uso non interattivo, consapevole).
set -euo pipefail
GB_MODE="${GB_MODE:-dmg}"
FLASHGBX="${FLASHGBX:-flashgbx}"
GB_BACKUP_DIR="${GB_BACKUP_DIR:-$HOME/gb-cart-backups}"
STAMP="$(date +%Y-%m-%d_%H%M%S)"
die() { printf 'errore: %s\n' "$*" >&2; exit 1; }
usage() { awk 'NR==1{next} /^#/{sub(/^# ?/,""); print; next} {exit}' "$0"; }
require_flashgbx() {
command -v "$FLASHGBX" >/dev/null 2>&1 || die "eseguibile '$FLASHGBX' non trovato (FLASHGBX=path)"
}
run() { # esegue flashgbx alimentando i prompt [y/N] con 'y'
printf 'y\ny\ny\n' | "$FLASHGBX" --cli --mode "$GB_MODE" "$@" 2>&1 | tr '\r' '\n'
}
cmd_detect() {
echo "== lsusb (lettori noti: 0483:5740 Joey Jr | 1a86:7523 GBxCart RW/GBFlash | 1209:b010 Game Bub) =="
(lsusb || true) | grep -iE "0483:5740|1a86:7523|1209:b010|STM32|Serial|CH34" || echo " nessun lettore noto in lsusb"
echo "== porte seriali =="
local ports
ports="$(ls /dev/ttyACM* /dev/ttyUSB* 2>/dev/null || true)"
if [ -n "$ports" ]; then
# shellcheck disable=SC2086
ls -l $ports
else
echo " nessuna /dev/ttyACM* o /dev/ttyUSB*"
fi
echo "== gruppi utente (serve dialout o uucp) =="
id -nG
}
cmd_info() { require_flashgbx; run --action info; }
cmd_backup_rom() {
require_flashgbx
local out="${1:-}"
mkdir -p "$GB_BACKUP_DIR"
[ -n "$out" ] || out="$GB_BACKUP_DIR/rom_${STAMP}.gb"
echo ">> backup ROM in $out"
run --action backup-rom --overwrite "$out"
echo ">> hash del backup:"
sha256sum "$out"
}
cmd_backup_save() {
require_flashgbx
local out="${1:-}"
mkdir -p "$GB_BACKUP_DIR"
[ -n "$out" ] || out="$GB_BACKUP_DIR/save_${STAMP}.sav"
echo ">> backup salvataggio in $out"
run --action backup-save --overwrite --save-filename-add-datetime "$out"
[ -f "$out" ] && sha256sum "$out" || echo " (nessun file .sav prodotto: verificare il Save Type o i nomi generati)"
}
cmd_flash_rom() {
require_flashgbx
local rom="${1:-}"; shift || true
[ -n "$rom" ] && [ -f "$rom" ] || die "serve il percorso di una ROM esistente"
echo ">> ROM sorgente: $rom"
sha256sum "$rom"
echo
echo ">> 1/2 backup obbligatorio del contenuto attuale"
cmd_backup_rom
echo
if [ "${GB_CART_ASSUME_YES:-0}" != "1" ]; then
printf 'La scrittura sovrascrive il contenuto della cartuccia ed è irreversibile.\nProcedo? [y/N] '
read -r answer
[ "$answer" = "y" ] || [ "$answer" = "Y" ] || die "annullato dall'utente"
fi
echo ">> 2/2 scrittura"
run --action flash-rom --flashcart-type "${FLASHCART_TYPE:-autodetect}" "$@" "$rom"
echo
echo ">> verifica: rilettura indipendente e confronto hash"
local verify="$GB_BACKUP_DIR/verify_${STAMP}.gb"
run --action backup-rom --overwrite "$verify"
if cmp -s "$verify" "$rom"; then
echo "OK: la ROM riletta è identica alla sorgente"
sha256sum "$verify"
else
echo "ATTENZIONE: la rilettura differisce dalla sorgente — NON considerare riuscita la scrittura" >&2
sha256sum "$verify" "$rom"
exit 1
fi
}
cmd_verify() {
require_flashgbx
local rom="${1:-}"
[ -n "$rom" ] && [ -f "$rom" ] || die "serve il file di riferimento"
mkdir -p "$GB_BACKUP_DIR"
local verify="$GB_BACKUP_DIR/verify_${STAMP}.gb"
run --action backup-rom --overwrite "$verify"
if cmp -s "$verify" "$rom"; then
echo "OK: contenuto della cartuccia identico a $rom"; sha256sum "$verify"
else
echo "DIFFERENZE rispetto a $rom:" >&2; sha256sum "$verify" "$rom"; exit 1
fi
}
case "${1:-}" in
detect) shift; cmd_detect "$@" ;;
info) shift; cmd_info "$@" ;;
backup-rom) shift; cmd_backup_rom "$@" ;;
backup-save) shift; cmd_backup_save "$@" ;;
flash-rom) shift; cmd_flash_rom "$@" ;;
verify) shift; cmd_verify "$@" ;;
""|-h|--help|help) usage ;;
*) die "comando sconosciuto: $1 (vedi --help)" ;;
esac