Metadata-Version: 2.4
Name: agbcia
Version: 0.1.2
Summary: Pure-Python library and CLI for injecting GBA ROMs into 3DS CIA files, with custom Home Menu icons, banners, and sound.
Project-URL: Homepage, https://github.com/marco-zanella/agbcia
Project-URL: Repository, https://github.com/marco-zanella/agbcia
Author: Marco Zanella
License-Expression: GPL-3.0-or-later
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries
Classifier: Typing :: Typed
Requires-Python: >=3.12
Requires-Dist: pillow>=10.0
Requires-Dist: pycryptodome>=3.20
Requires-Dist: typer>=0.12
Description-Content-Type: text/markdown

# agbcia

[![PyPI](https://img.shields.io/pypi/v/agbcia.svg)](https://pypi.org/project/agbcia/)
[![CI](https://github.com/marco-zanella/agbcia/actions/workflows/ci.yml/badge.svg)](https://github.com/marco-zanella/agbcia/actions/workflows/ci.yml)
[![License: GPL v3+](https://img.shields.io/badge/license-GPL--3.0--or--later-blue.svg)](LICENSE)
[![Python 3.12+](https://img.shields.io/badge/python-3.12%2B-blue.svg)](pyproject.toml)

A pure-Python library and CLI for injecting Game Boy Advance ROMs into
installable 3DS CIA files — a free, open-source alternative to NSUI/vcoven —
with custom Home Menu icons, animated banners, sound, and box art.

Everything is implemented from scratch in Python, cross-checked against
public documentation (3dbrew, GBATEK) and the real behavior of existing
open-source tools (bannertool, 3dstool, vcoven, pyctr, SPICA). No C/C++
binaries are bundled or shelled out to.

## Table of contents

- [What this does](#what-this-does)
- [Features](#features)
- [Requirements](#requirements)
- [Installation](#installation)
- [Quick start](#quick-start)
- [Command reference](#command-reference)
- [The donor-banner feature](#the-donor-banner-feature)
- [Getting the assets you'll need](#getting-the-assets-youll-need)
- [Project layout](#project-layout)
- [Development](#development)
- [Known limitations](#known-limitations)
- [Acknowledgments](#acknowledgments)
- [License](#license)

## What this does

`agbcia` takes a GBA ROM and produces a `.cia` file you can install on a
real (or emulated) 3DS/2DS with any standard CIA installer. It supports two
independent ways of getting the GBA game to actually run once installed:

- **native** mode reuses the 3DS's own built-in GBA Virtual Console
  firmware (`AGB_FIRM`) — exactly the mechanism Nintendo's own GBA VC
  releases use. This needs a one-time-extracted *boot logo* asset from a
  GBA VC CIA you already own (see [Getting the assets you'll need](#getting-the-assets-youll-need)).
- **homebrew** mode wraps your ROM around an existing, already-working
  forwarder/emulator core CIA (for example a compiled 3DS build of mGBA).
  No donor CIA or boot logo needed, at the cost of running through an
  emulator instead of real GBA VC firmware.

Either way, you get to set your own title name, icon, animated banner, and
banner sound — the same customization NSUI/vcoven-style injectors offer,
built independently and kept in pure Python.

## Features

- Two injection modes (native/AGB_FIRM and homebrew), described above.
- Custom Home Menu icon (SMDH, both grid sizes) and a real, animated 3D
  banner (CGFX) with custom looping sound (CWAV) — not just a static image.
- **Donor-banner patching**: reuse a real GBA VC title's own animated 3D
  banner scene (mesh, skeleton, rotation animation, materials, shaders) and
  patch only your own box-art image, an optional bottom-screen badge image,
  and an optional box-shell color onto it. See
  [The donor-banner feature](#the-donor-banner-feature).
- GBA save-type (EEPROM/SRAM/FLASH512/FLASH1M/none) and real-time-clock
  auto-detection, both overridable.
- Auto-derived product code and TMD save-data size, both overridable.
- Fully stateless library and CLI: no config files, no cached keys or donor
  material between runs — every invocation takes everything it needs as
  explicit input.
- Lower-level building blocks (icon/banner building, footer building, boot
  logo extraction, key verification, ROM/CIA inspection) are all exposed as
  their own standalone commands too, not hidden behind one big `inject`
  step.
- A CLI usable entirely standalone; a separate webapp,
  [agbcia-web](https://github.com/marco-zanella/agbcia-web), builds on top
  of this library for people who'd rather not use a terminal.

## Requirements

- Python 3.12 or later.
- For **native** mode: a donor GBA Virtual Console CIA you legitimately own
  (used once, locally, to extract a boot logo), and your own ARM9 bootROM
  dump (`boot9.bin`/`boot9_prot.bin`).
- For **homebrew** mode: a working forwarder/emulator core CIA for the
  3DS (e.g. a compiled mGBA-3DS build). No donor CIA or bootROM dump needed
  unless that core CIA happens to be encrypted.
- Nothing from Nintendo is ever bundled with this project or fetched
  automatically — every donor/key asset above is something you already
  have and point the CLI at explicitly, every time.

## Installation

Published on [PyPI](https://pypi.org/project/agbcia/):

```sh
# As a standalone CLI tool (recommended for most users):
pipx install agbcia
# or, with uv:
uv tool install agbcia

# As a library, into your own project:
uv add agbcia
pip install agbcia
```

To install straight from the repository instead (e.g. an unreleased
commit on `main`), replace `agbcia` above with
`git+https://github.com/marco-zanella/agbcia`.

To work on `agbcia` itself, see [Development](#development) instead.

## Quick start

```sh
# One-time: extract the AGB_FIRM boot logo from a donor GBA VC CIA you own.
agbcia footer extract-logo --donor-cia donor.cia --boot9 boot9.bin --output logo.bin

# Native/Virtual Console-style mode:
agbcia inject native \
  --rom game.gba --icon icon.png --banner-image banner.png --banner-sound banner.wav \
  --boot-logo logo.bin --title-name "My Game" \
  --title-id 0004000000f09900 --output game.cia

# Homebrew-emulator mode (no donor CIA/boot logo needed):
agbcia inject homebrew \
  --rom game.gba --icon icon.png --banner-image banner.png \
  --emulator-core mgba-3ds-forwarder.cia --title-name "My Game" \
  --title-id 0004000012345678 --output game.cia
```

`--title-id` is always mandatory — `agbcia` never invents one for you (see
[Command reference](#command-reference) for why, and for the range it must
fall in for native mode).

## Command reference

Every command also accepts `--help` for the same information Typer
generates directly from the code, which is always the authoritative
source if this section and the CLI ever disagree.

### `agbcia inject native`

Builds a complete AGB_FIRM/Virtual Console-style CIA.

| Flag | Required | Description |
| --- | --- | --- |
| `--rom` | yes | The GBA ROM to inject. |
| `--icon` | yes | Home Menu icon image. |
| `--banner-image` | yes | Home Menu banner image. |
| `--boot-logo` | yes | Already-extracted AGB_FIRM boot logo (`agbcia footer extract-logo`). |
| `--title-name` | yes | Display title shown on the Home Menu. |
| `--title-id` | yes | 16-hex-digit title ID. Must be in Nintendo's real GBA VC range: `0004000000F???00` (only the `F???` nibbles are free — see below). |
| `--output` | yes | Where to write the finished `.cia`. |
| `--long-title` | no | Long description shown on the bottom screen. Defaults to `--title-name`. |
| `--publisher` | no | Publisher name shown on the bottom screen. Default: `Homebrew`. |
| `--banner-sound` | no | Banner sound (WAV). A short silence is used if omitted. |
| `--donor-banner` | no | A real GBA VC banner extracted from a donor CIA's ExeFS, to patch `--banner-image` onto instead of this project's own flat template. See [The donor-banner feature](#the-donor-banner-feature). |
| `--bottom-badge` | no | Bottom-screen badge image, patched onto a `--donor-banner`'s own badge texture. Only applies alongside `--donor-banner`; left blank if omitted. |
| `--box-shell-color` | no | `RRGGBB` hex color, patched onto a `--donor-banner`'s box-shell texture. Only applies alongside `--donor-banner`; left as the donor's own color if omitted. |
| `--save-type` | no | Override the auto-detected save type: `none`, `eeprom`, `sram`, `flash512`, or `flash1m`. |
| `--rtc` / `--no-rtc` | no | Override the auto-detected real-time-clock presence. |
| `--product-code` | no | Override the auto-derived product code (derived from the ROM's game code, e.g. `CTR-P-BPEE`; falls back to `CTR-P-AGBC` if the ROM has no 4-character game code). |
| `--title-version` | no | TMD title version, default `0`. Bump this on a reinstall over the same title ID — some installers/Application Manager treat an unchanged version as already up to date and skip replacing the installed content. |

**Why `--title-id` has to fall in a specific range:** the 3DS decides
whether to boot a title through AGB_FIRM's GBA-save-compatible path from
the title ID's own numeric value, not just its NCCH content. Title IDs
outside `0004000000F???00` fail to launch even with an otherwise
byte-for-byte correct CIA, so `agbcia` rejects them up front instead of
letting you find out on a console.

### `agbcia inject homebrew`

Builds a complete CIA around an existing forwarder/emulator core. Same
flags as `native` above, except:

| Flag | Required | Description |
| --- | --- | --- |
| `--emulator-core` | yes, instead of `--boot-logo` | A full, already-working forwarder/emulator `.cia` (e.g. a compiled mGBA-3DS build). Its own `.code`/extended header are reused verbatim. |
| `--title-id` | yes | 16-hex-digit title ID. Homebrew mode has no range restriction — it never boots through AGB_FIRM. |
| `--boot9` | no | Your ARM9 bootROM dump. Only needed if `--emulator-core` happens to be an encrypted donor CIA (rare). |

The GBA ROM is embedded into the built title's own RomFS, at a fixed path
(`rom.gba`) that this project defines as its own contract — the
`--emulator-core` binary itself has to be written to read the ROM from
there. This is not a wider 3DS/homebrew standard.

### `agbcia footer build`

Builds the GBA VC footer (the tail `AGB_FIRM` reads after a ROM, describing
ROM size and save type) standalone — already done automatically inside
`inject native`.

| Flag | Required | Description |
| --- | --- | --- |
| `--rom` | yes | The GBA ROM. |
| `--output` | yes | Where to write the footer. |
| `--save-type` | no | Override the auto-detected save type. |

### `agbcia footer extract-logo`

Extracts the AGB_FIRM boot logo from a donor CIA — a one-time step whose
output gets reused for every future `inject native` run.

| Flag | Required | Description |
| --- | --- | --- |
| `--donor-cia` | yes | A donor GBA Virtual Console CIA you own. |
| `--boot9` | yes | Your ARM9 bootROM dump (`boot9.bin`/`boot9_prot.bin`). |
| `--output` | yes | Where to write the extracted boot logo. |

### `agbcia banner build`

Builds a complete CBMD banner standalone — already done automatically
inside `inject`. Useful for previewing a banner without a full ROM/CIA
build.

| Flag | Required | Description |
| --- | --- | --- |
| `--image` | yes | Banner image. |
| `--output` | yes | Where to write the banner. |
| `--sound` | no | Banner sound (WAV). A short silence is used if omitted. |

### `agbcia icon build`

Builds a complete SMDH icon standalone — already done automatically inside
`inject`.

| Flag | Required | Description |
| --- | --- | --- |
| `--image` | yes | Icon image. |
| `--title-name` | yes | Display title used across every language slot. |
| `--output` | yes | Where to write the icon. |
| `--save-data` / `--no-save-data` | no | Whether the title uses save data (sets `FLAG_SAVE_DATA`). Default: `--no-save-data`. |

### `agbcia rom info` / `detect-save-type` / `detect-rtc`

Inspect a GBA ROM without building anything. Each takes the ROM path as a
plain positional argument (no flag).

```sh
agbcia rom info game.gba
# title:          MY GAME
# game_code:      BPEE
# maker_code:     01
# checksum_valid: True
# save_type:      flash1m
# rtc_present:    True

agbcia rom detect-save-type game.gba   # -> flash1m
agbcia rom detect-rtc game.gba         # -> True
```

### `agbcia cia info`

Shows a CIA's single content's NCCH header fields — works on any CIA, not
just this project's own output.

```sh
agbcia cia info game.cia
# title_id:      0004000000f09900
# product_code:  CTR-P-BPEE
# no_crypto:     True
# crypto_method: 0x0
# exheader:      present
# exefs:         present
# romfs:         present
# logo region:   present
```

### `agbcia keys verify`

Checks that a file is a genuine boot9 dump `agbcia` can use.

```sh
agbcia keys verify boot9.bin
# valid boot9 dump (prot)
```

## The donor-banner feature

By default, `inject`'s banner is this project's own flat, non-animated
CBMD template with your image glued onto it — simple, and works
everywhere, but visually plain next to a real GBA VC title's rotating 3D
box.

Pass `--donor-banner` (extracted from a real GBA VC title's own CIA — see
below) and `agbcia` instead reuses that donor's *entire* banner scene byte
for byte (its 3D mesh, skeleton, rotation animation, materials, and
shaders), patching only:

- **your box-art image**, onto the donor's front-face texture (always,
  required alongside `--donor-banner`);
- **an optional bottom-screen badge image** (`--bottom-badge`), left blank
  if omitted, instead of showing the donor's own game's name;
- **an optional flat box-shell color** (`--box-shell-color`), left as the
  donor's own color if omitted.

`--box-shell-color` is intentionally solid-color-only, not an arbitrary
image: the box-shell texture's UV mapping wraps around the whole 3D box in
a panoramic, non-obvious way (most of the texture stretches across the
sides, a thin strip covers the top/bottom, and the front is nearly
degenerate since it's covered by the box-art decal anyway) — a flat color
renders correctly there, but a photo or logo would come out visibly warped
around every rounded edge.

Any real GBA VC banner can be used as the donor, independent of which ROM
you're actually injecting.

## Getting the assets you'll need

None of the following are provided by this project — they're either your
own hardware's key material or something you dump from a title you
legitimately own, exactly once, and then reuse locally forever after.

- **`boot9.bin`/`boot9_prot.bin`** (your console's ARM9 bootROM dump):
  needed to extract a boot logo for native mode, and to decrypt an
  encrypted homebrew donor CIA. Dumped from a real console you own, using
  standard, widely-documented 3DS homebrew tools (e.g. `boot9strap` +
  a dumping homebrew app). `agbcia keys verify` checks a dump is usable.
- **A donor GBA Virtual Console CIA** (native mode only): any real GBA VC
  title you own works — its boot logo is the same across every GBA VC
  title, since it's Nintendo's own AGB_FIRM asset, not game-specific.
  Extract with `agbcia footer extract-logo`.
- **A donor banner** (optional, any mode): extract the `banner` entry from
  any real GBA VC donor CIA's ExeFS (e.g. with
  [ctrtool](https://github.com/profi200/Project_CTR) or
  [3dstool](https://github.com/dnasdw/3dstool)) and pass the resulting file to
  `--donor-banner`. See [The donor-banner feature](#the-donor-banner-feature).
- **A homebrew forwarder/emulator core CIA** (homebrew mode only): a
  full, working, already-built 3DS application — e.g. a compiled
  [mGBA](https://mgba.io/) 3DS build set up as a forwarder.

## Project layout

```
src/agbcia/
  formats/   generic 3DS container formats: CIA, NCCH, ExeFS, RomFS, TMD,
             ticket, SMDH, CBMD/CGFX, CWAV (read + write)
  crypto/    AES/RSA/SHA, keys.bin parsing, key derivation
  banner/    banner/icon/sound authoring
  gba/       GBA-specific logic (footer, save-type/RTC detection, AGB_FIRM,
             emulator-core packaging)
  inject/    high-level pipeline, public API surface
  cli/       Typer CLI, thin wrapper over the above
```

`formats/`, `crypto/`, and `banner/` are deliberately GBA-agnostic; `gba/`
depends on them, never the reverse.

## Development

```sh
uv sync --all-extras --dev
uv run pytest
uv run ruff check .
uv run ruff format --check .
uv run mypy src
```

The same four checks run in CI on every push/PR (see
`.github/workflows/ci.yml`).

## Known limitations

- `--box-shell-color` sets a flat color only, not an arbitrary image (see
  [The donor-banner feature](#the-donor-banner-feature) for why).
- Building a genuinely custom 3D banner scene (a different mesh, skeleton,
  or animation, rather than patching textures onto a donor's own scene) is
  out of scope.
- RomFS only supports a flat file list in the root directory — no
  subdirectories. Covers this project's own needs (an empty RomFS for
  native mode, a few files for homebrew mode) but not the fully general
  case.
- RTC-title detection is a lookup against the known, finite list of
  officially released GBA games that shipped with an RTC chip; a ROM hack
  or translation of one of those games won't match unless it keeps the
  original game code.

## Acknowledgments

No third-party binaries are bundled or shelled out to, but getting the
byte-level format details right leaned heavily on public documentation and
existing open-source implementations:

- [3dbrew](https://www.3dbrew.org/) and [GBATEK](https://problemkaputt.de/gbatek.htm) — the primary format documentation for the 3DS and GBA respectively.
- [pyctr](https://github.com/ihaveamac/pyctr) (ihaveamac, MIT) — NCCH/ExeFS/crypto reference behavior, and the source of the long-public cryptographic constants this project embeds (the same ones pyctr itself embeds, for the same reason: they're needed to read real-world donor content and aren't extractable from a boot9 dump).
- [makerom](https://github.com/profi200/Project_CTR) (Project_CTR, MIT) — the real, long-public 3DS "Test PKI" signing key and certificate chain embedded verbatim in `agbcia.crypto.test_pki`, used across the 3DS homebrew ecosystem to produce genuinely-valid, non-Nintendo signatures on self-built CIAs.
- [bannertool](https://github.com/Epicpkmn11/bannertool) (Epicpkmn11, MIT; itself based on the original Steveice10 bannertool) — the source of this project's own flat CGFX banner template and CWAV/CBMD container layout.
- [3dstool](https://github.com/dnasdw/3dstool) — NCCH/ExeFS/RomFS byte-layout details not fully covered by 3dbrew alone.
- [vcoven](https://github.com/vedoot/vcoven) — the GBA VC footer/config-block format and several CLI design conventions (mandatory title ID, its GBA VC range, default fallbacks).
- [SPICA](https://github.com/gdkchan/SPICA) (gdkchan, MIT) — reference for the donor-banner CGFX texture/animation byte layout.
- [gc-dspadpcm-encode](https://github.com/jackoalan/gc-dspadpcm-encode) (jackoalan, MIT) — the DSP-ADPCM encoder this project ports (currently unused by the default PCM16 banner audio path, kept for future use).
- [mGBA](https://github.com/mgba-emu/mgba) — the GBA RTC-title list this project's own list is taken from.
- [devkitPro](https://devkitpro.org/)'s `gbafix` — the GBA header checksum formula reference.

## License

GPL-3.0-or-later — see [LICENSE](LICENSE).
