Skip to content
Back to skills

cartwright

ASecurity

Use when working on a retro handheld — TrimUI Brick/Brick Pro/Smart Pro, NextUI, MinUI, or any Linux handheld running libretro cores. Covers building or repairing an SD card, sorting ROMs, sourcing BIOS, fixing arcade romsets that fail with "missing files for THIS VERSION of FBNeo", scraping box art, building cross-system collections, installing paks, theming, SSH into the device, and diagnosing a game or emulator that will not launch.

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 23, 2026
designrustgobashsqldebugginggitapidatabasedocumentation

Works with

  • api

Security analysis

A100/100

Pro scans all 14 files and shows the line behind each finding

Scanned September 23, 2026

npx -y skills add chiotas/cartwright --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of cartwright?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for cartwright
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/chiotas-cartwright/badge)](https://www.skillsdirectory.com/skills/chiotas-cartwright)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: cartwright
description: Use when working on a retro handheld — TrimUI Brick/Brick Pro/Smart Pro, NextUI, MinUI, or any Linux handheld running libretro cores. Covers building or repairing an SD card, sorting ROMs, sourcing BIOS, fixing arcade romsets that fail with "missing files for THIS VERSION of FBNeo", scraping box art, building cross-system collections, installing paks, theming, SSH into the device, and diagnosing a game or emulator that will not launch.
---

# Cartwright

A cartwright builds carts. Field-tested on a TrimUI Brick Pro (`tg5040`)
running NextUI: 19 systems, 2,718 games, every file verified bit for bit. The
arcade folder started at 375 romsets of which none ran, and finished at 541
working: part rebuilt from the original bytes by CRC, part from a replacement
set fetched once the core's naming era was known. Every path, command and
number here came from a real build.

**Scope:** paths and conventions are NextUI's. The method and all five tools
apply to any Linux handheld running libretro cores. If the device is not a
TrimUI, read `references/porting.md` first, say so once, and keep working.

## The one rule

**Never trust a database, a filename, a wiki, or this document. Interrogate the
binary.**

Documentation, DAT files and ROM sets routinely disagree with what the emulator
on the device actually wants. Every hard problem in this domain dissolved the
moment the core was asked directly. Every assumption cost an hour.

`scripts/corespec.py` is how you ask.

## Start here: symptom to first move

Most people arrive with one broken thing, not an empty card.

| Symptom | First move |
|---|---|
| Arcade games say "missing files for THIS VERSION of FBNeo" | `roms-and-arcade.md`. Fetch the DAT, run `fbneo.py validate`, and only then rebuild. Do not touch a ROM first |
| One game or one system will not launch | Read `.userdata/$PLATFORM/logs/<TAG>.txt` over SSH (`device.md`). The reason is in there and it is almost never what you guessed |
| Games listed by filename instead of title | `map.txt`, tab separated, no BOM (`roms-and-arcade.md`) |
| Box art missing | `libretro.py art` (`presentation.md`). A system reporting `Queued 0 ROMs` is missing its `.media` folder |
| Every PlayStation game appears twice | A redundant `.m3u` next to each single-disc `.chd` (`roms-and-arcade.md`) |
| Device frozen inside a game | `kill -9` the emulator over SSH (`device.md`) |
| An emulator opens its config screen instead of the game | Its bundled pad map does not match `TRIMUI Player1`. The user maps it once (`device.md`) |
| Card full of `._` files | xattrs went to exFAT (`card-and-verify.md`) |
| Building a card from nothing | Phase order below |

## Before anything: recon

Never guess the platform tag from the product name. The Brick **Pro** is
`tg5040`, not `tg5050`. Wrong tag, nothing loads.

Run this against the card's root, either mounted on your computer or over SSH
at `/mnt/SDCARD`. On a factory card the file is already there; this is recon on
the card you have, before you reformat anything.

```bash
grep -A2 'PLATFORM=' /Volumes/<CARD>/trimui/app/.tmp_update/updater
```

Then map every ROM folder tag to its emulator and every emulator to its
accepted extensions and BIOS names. See `references/recon-and-bios.md`.

## Phase order

Steps 1 and 5 are the real work. The rest is plumbing.

| # | Phase | Reference |
|---|---|---|
| 1 | Platform tag, folder tags, core extensions, core BIOS names | `recon-and-bios.md` |
| 2 | Install NextUI `all.zip` into staging (not onto the card) | `recon-and-bios.md` |
| 3 | BIOS by ranged extraction, validated against the cores | `recon-and-bios.md` |
| 4 | Sort ROMs into tagged folders, unzip everything but arcade | `roms-and-arcade.md`, `sourcing.md` |
| 5 | Rebuild arcade against the core's CRCs, generate `map.txt` | `roms-and-arcade.md` |
| 6 | Build the manifest, format exFAT/MBR, rsync, verify | `card-and-verify.md` |
| 7 | Boot, install paks from the Pak Store, verify each landed | `device.md` |
| 8 | Scrape box art, fill gaps from libretro-thumbnails | `presentation.md` |
| 9 | Generate collections from libretro-database | `presentation.md` |
| 10 | Apply a theme, rebuild whatever it does not cover | `presentation.md` |
| 11 | Configure backups | `device.md` |
| 12 | Sync the card back to staging, rebuild the manifest | `card-and-verify.md` |

Not a TrimUI? `references/porting.md` says which parts hold and how to derive
the rest from the device instead of guessing from a wiki.

Read `references/gotchas.md` once before starting. It is short and every entry
in it cost real time to learn.

## Tools

Run each with `--help`. All of them accept `--selftest`.

| Script | What it does |
|---|---|
| `scripts/ziprange.py` | Index and extract single entries from a **remote** ZIP over HTTP range requests, without downloading it. Handles split `.zip.001` archives. This is how you get 391 BIOS files out of multi-GB packs, or 496 arcade romsets out of a 9.6 GB archive. |
| `scripts/corespec.py` | Interrogate a libretro core `.so`: accepted extensions, expected BIOS filenames, FBNeo naming era, embedded-ROM detection. |
| `scripts/fbneo.py` | Parse an FBNeo DAT, prove it matches your core (`validate`), index romsets by CRC, rebuild broken zips with the filenames the core wants, rename misidentified sets, generate `map.txt`. Its `--help` names the DAT source. |
| `scripts/manifest.py` | Build a size+CRC32 manifest of a source tree, verify a copy against it. |
| `scripts/libretro.py` | Fill box-art gaps from libretro-thumbnails; build genre and franchise collections from libretro-database. |

## Getting ROMs

**You do the searching.** The user names what they want; you search, vet the
candidates and come back with two or three options, their sizes and a
recommendation. Making the user go find a URL and paste it back is the wrong
division of labour.

Decide the convention before searching — FBNeo for arcade, No-Intro for
cartridges, Redump/`.chd` for discs — because downloading the wrong *kind* of
set is the more common failure. Then vet with the archive.org metadata API,
pull only the entries needed with `scripts/ziprange.py`, and verify what
arrived before building on it.

Full procedure in `references/sourcing.md`. No list of sites lives in this
skill: they rot, and a live search is better every time.

## What you cannot do from here

Say so plainly and hand the user exact steps instead of pretending.

| Task | Why | What to tell the user |
|---|---|---|
| Installing paks | The Pak Store writes a SQLite row that drives update notifications. Copying folders over SSH does not. | Install from the Pak Store **on the device**. If it fails mid-install, the DB row already exists — finish the file copy over SSH and updates still work. |
| Saturn pad mapping | SDL derives the controller GUID from a CRC of the device name (`TRIMUI Player1`). It cannot be hand-written. | Launch any Saturn game once, map the pad on the config screen that appears. Saved to `Saves/SS/.yabasanshiro/keymapv2.json`. |
| Formatting / mounting the card | Physical. | Disk Utility needs **View → Show All Devices** to expose the partition-scheme selector. exFAT + **MBR**. |
| ScreenScraper credentials | The user's account. | Without them ScrapeGoat does ~1 request/minute, unusable at scale. With them, 100+/min. |

## Rules that prevent damage

- **Never delete from the device's `/tmp`.** The Pak Store stages downloads
  there. Cleaning it mid-session breaks installs already in flight.
- **Read the whole Pak Store catalog before calling a system impossible.**
  A wrong "Dreamcast can't work" cost a user 38 GB of deleted ROMs. The `DC`
  pak exists.
  `https://raw.githubusercontent.com/LoveRetro/nextui-pak-store/refs/heads/gh-pages/storefront.json`
- **Verify your own tooling before alarming the user.** Several false alarms in
  the original build traced to bugs in the checker, not the data —
  `os.path.splitext` on names containing dots, the wrong string pulled from a
  binary. When a scan reports something alarming, confirm it by hand on one
  file first.
- **Explain what is lost before any destructive step**, in two lines. Users
  approve readily and regret slowly.
- **Say the numbers.** File counts, CRC results, before/after. This domain is
  full of silent failures that only surface weeks later.

## Verification is the deliverable

A 240 GB copy that silently drops one file is the worst outcome in this
domain — it surfaces when a game will not boot, long after the context is gone.

Build the manifest from the source, verify the card against it, state the
count. `scripts/manifest.py` does both. Full CRC over 240 GB takes ~45 minutes
at 90 MB/s and is worth it.

When a game will not launch, the device already wrote the reason to
`.userdata/$PLATFORM/logs/<TAG>.txt`. Read it before forming a theory.

If the `superpowers` plugin is installed, `verification-before-completion` and
`systematic-debugging` pair well with this skill. Neither is required: the
verification discipline above stands on its own.

Files in this skill

  • SKILL.md8.9 KB
  • references/card-and-verify.md3.5 KB
  • references/device.md5.5 KB
  • references/gotchas.md4.2 KB
  • references/porting.md3.9 KB
  • references/presentation.md5 KB
  • references/recon-and-bios.md7.1 KB
  • references/roms-and-arcade.md9.5 KB
  • references/sourcing.md3.8 KB
  • scripts/corespec.py5.4 KB
  • scripts/fbneo.py16.6 KB
  • scripts/libretro.py17.9 KB
  • scripts/manifest.py6.3 KB
  • scripts/ziprange.py10.2 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…