Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .claude/skills/snes-developer/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ export PVSNESLIB_HOME=/c/tools/pvsneslib # if unset
export PATH=$PVSNESLIB_HOME/devkitsnes/bin:$PVSNESLIB_HOME/devkitsnes/tools:$PATH
```

Do not use node.js to build things, use python for tooling around graphics or sound assets. The build process relies on the PVSnesLib toolchain and Makefiles, not JavaScript-based tools.

## Building

PVSnesLib projects use a `Makefile` that includes the SDK's `snes_rules`. From an MSYS2 shell in the project dir:
Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ linkfile
# Helper-script generated backup/source cache
tools/sprites_src.bmp

# Python bytecode cache (helper scripts in tools/)
__pycache__/
*.pyc

# Keep hand-written assembly tracked
!hdr.asm
!data.asm
80 changes: 58 additions & 22 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
#---------------------------------------------------------------------------------
# snesgame2 - PVSnesLib build (Linux / WSL)
#
# Requires PVSNESLIB_HOME to point at your PVSnesLib install. Set it once in your
# shell profile (~/.bashrc), using a Unix-style path:
#
# export PVSNESLIB_HOME=/opt/pvsneslib
#
# Targets:
# make build everything (assets + ROM) -> snesgame2.sfc
# make all same as `make`
# make artifacts convert source art/sound into SNES data, without linking the ROM
# make clean remove the ROM, build intermediates, and generated asset data
#---------------------------------------------------------------------------------

ifeq ($(strip $(PVSNESLIB_HOME)),)
$(error "Please create an environment variable PVSNESLIB_HOME pointing at /c/tools/pvsneslib")
$(error PVSNESLIB_HOME is not set. Point it at your PVSnesLib install with a Unix-style path, e.g. `export PVSNESLIB_HOME=/opt/pvsneslib`)
endif

# BEFORE including snes_rules:
Expand All @@ -8,56 +23,77 @@ endif
AUDIOFILES := res/sfx.it
export SOUNDBANK := res/soundbank

include ${PVSNESLIB_HOME}/devkitsnes/snes_rules

# snes_rules' Windows lib-path math mangles the dir into a broken "C::\..."
# (double colon) under MSYS, so the standard library objects get dropped from
# the linkfile and the link fails. Point it straight at the forward-slash lib
# dir, which both `ls` and wlalink.exe accept.
LIBDIRSOBJSW := $(LIBDIRSOBJS)

.PHONY: bitmaps musics brrsound all
include $(PVSNESLIB_HOME)/devkitsnes/snes_rules

#---------------------------------------------------------------------------------
# ROMNAME is used in snes_rules file
# ROMNAME is used by snes_rules to name the linked ROM.
export ROMNAME := snesgame2

# smconv flags: -s strip, -o output, -V verbose, -b 5 = place the bank in ROM bank 5
SMCONVFLAGS := -s -o $(SOUNDBANK) -V -b 5
musics: $(SOUNDBANK).obj

all: musics brrsound bitmaps $(ROMNAME).sfc
.PHONY: all artifacts bitmaps musics brrsound clean

#---------------------------------------------------------------------------------
# Top-level targets
#---------------------------------------------------------------------------------
# Everything: convert the assets, then compile + link the ROM.
all: artifacts $(ROMNAME).sfc

# Just the converted SNES data: graphics, the sound bank, and the BRR sample.
# Useful to refresh assets (or sanity-check conversions) without a full link.
artifacts: bitmaps musics brrsound

# The ROM embeds the converted assets, so make sure they exist before linking
# (order-only: regenerated assets don't force a needless relink).
$(ROMNAME).sfc: | artifacts

# Remove build results and generated graphics/audio (rules from snes_rules).
clean: cleanBuildRes cleanRom cleanGfx cleanAudio

#---------------------------------------------------------------------------------
# Convert the bundled console font (8x8 tiles) -> .pic + .pal
# Graphics: convert source images -> .pic + .pal (+ .inc / _data.as)
#---------------------------------------------------------------------------------
# Bundled console font (8x8 tiles)
pvsneslibfont.pic: pvsneslibfont.png
@echo convert font ... $(notdir $@)
$(GFXCONV) -s 8 -o 16 -u 16 -p -e 0 -i $<

# Convert the 32x32 sprite sheet -> .pic + .pal (+ .inc / _data.as)
# Player sprite sheet (32x32)
sprites.pic: sprites.bmp
@echo convert sprite ... $(notdir $@)
$(GFXCONV) -s 32 -o 16 -u 16 -t bmp -i $<

# Convert the 32x32 Sega Genesis enemy -> .pic + .pal
# Sega Genesis enemy (32x32)
enemy.pic: enemy.bmp
@echo convert enemy ... $(notdir $@)
$(GFXCONV) -s 32 -o 16 -u 16 -t bmp -i $<

# Convert the 8x8 player bullet -> .pic + .pal
# Player bullet (8x8)
bullet.pic: bullet.bmp
@echo convert bullet ... $(notdir $@)
$(GFXCONV) -s 8 -o 16 -u 16 -t bmp -i $<

bitmaps: pvsneslibfont.pic sprites.pic enemy.pic bullet.pic
# Bomb power-up pickup (32x32)
powerup.pic: powerup.bmp
@echo convert powerup ... $(notdir $@)
$(GFXCONV) -s 32 -o 16 -u 16 -t bmp -i $<

# TAC-2 joystick, the tough 3-hit enemy (32x32)
tac2.pic: tac2.bmp
@echo convert tac2 ... $(notdir $@)
$(GFXCONV) -s 32 -o 16 -u 16 -t bmp -i $<

bitmaps: pvsneslibfont.pic sprites.pic enemy.pic bullet.pic powerup.pic tac2.pic

#---------------------------------------------------------------------------------
# Sound: music/effects bank (smconv) + gun-fire BRR sample (snesbrr)
#---------------------------------------------------------------------------------
musics: $(SOUNDBANK).obj

# Gun-fire effect: snesbrr-encode the synthesized WAV into a BRR sample.
# res/gunshot.wav is produced by tools/make_gunshot.js (Node) -- regenerate it
# with `node tools/make_gunshot.js` when tweaking the sound; the build itself
# only needs snesbrr so it has no Node dependency.
# res/gunshot.wav is produced by tools/make_gunshot.py -- regenerate it with
# `python3 tools/make_gunshot.py` when tweaking the sound; the build itself only
# needs snesbrr, so it has no Python dependency.
res/gunshot.brr: res/gunshot.wav
@echo convert gunshot wav -> brr ... $(notdir $@)
$(BRCONV) -e $< $@
Expand Down
141 changes: 66 additions & 75 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,55 +6,42 @@ Small SNES homebrew project built with PVSnesLib. The game ROM is generated as `

- C for game logic in `src/main.c`
- PVSnesLib for the SNES SDK and build rules
- MSYS2 to provide the shell environment used for builds on Windows
- GNU Make under a Linux or WSL shell
- PVSnesLib graphics conversion tools during `make`
- PowerShell helper scripts in `tools/` for regenerating source BMP art
- Python helper scripts in `tools/` for regenerating source BMP art

## Project Layout

- `src/main.c`: main game code
- `Makefile`: build rules and graphics conversion steps
- `build.sh`: Windows-friendly wrapper that sets `PVSNESLIB_HOME` and runs `make`
- `tools/`: helper scripts to regenerate some sprite BMP assets
- `*.bmp` / `pvsneslibfont.png`: authored source graphics used by the build
- `*.pic`, `*.pal`, `*.inc`, `*_data.as`: generated SNES graphics data consumed by the game
- `snesgame2.sfc`: built ROM output

## Required Tools

This repository assumes a Windows setup matching the current build scripts.
The build targets a Linux or WSL shell with GNU Make.

### 1. MSYS2
### 1. PVSnesLib

Expected location:
Install PVSnesLib and set `PVSNESLIB_HOME` to point at it, using a Unix-style
path. Add it to your shell profile (e.g. `~/.bashrc`) so every shell has it:

```powershell
C:/msys64
```

The README commands below use:

```powershell
C:/msys64/usr/bin/bash.exe
```

### 2. PVSnesLib

Expected location:

```powershell
C:/tools/pvsneslib
```sh
export PVSNESLIB_HOME=/opt/pvsneslib
```

The build expects `PVSNESLIB_HOME` to point there. The provided `build.sh` sets it automatically.
The Makefile reads `PVSNESLIB_HOME` and stops with an error if it isn't set.

### 3. PowerShell
### 2. Python 3

Needed to run the helper scripts in `tools/` that regenerate some BMP assets.
They use only the standard library (no `pip install` required).

### 4. SNES Emulator
### 3. SNES Emulator

Any emulator that can load `.sfc` ROMs should work. The local project notes target ZSNES on Windows.
Any emulator that can load `.sfc` ROMs should work.

## Build Process

Expand All @@ -66,56 +53,45 @@ The build works in two stages:
The Makefile target flow is:

```make
all: bitmaps snesgame2.sfc
all: artifacts snesgame2.sfc
```

`artifacts` groups the asset-conversion steps (`bitmaps`, `musics`, `brrsound`).
The `bitmaps` target regenerates SNES-ready graphics data from the source images:

- `pvsneslibfont.png` -> `pvsneslibfont.pic`, `pvsneslibfont.pal`
- `sprites.bmp` -> `sprites.pic`, `sprites.pal`
- `enemy.bmp` -> `enemy.pic`, `enemy.pal`
- `tac2.bmp` -> `tac2.pic`, `tac2.pal`
- `bullet.bmp` -> `bullet.pic`, `bullet.pal`
Comment on lines 62 to 66

From those assets, the PVSnesLib build rules generate the final ROM.

## Build Commands

### Recommended: use the wrapper script
With `PVSNESLIB_HOME` set (see Required Tools), build from the project directory:

From PowerShell:

```powershell
& C:/msys64/usr/bin/bash.exe -lc "sh /c/dev/snesgame2/build.sh"
```sh
make
```

This script does the environment setup for you:

- sets `PVSNESLIB_HOME=C:/tools/pvsneslib`
- adds the PVSnesLib tools to `PATH`
- changes into `/c/dev/snesgame2`
- runs `make`
To rebuild just the converted assets without linking the ROM:

### Clean build artifacts

```powershell
& C:/msys64/usr/bin/bash.exe -lc "sh /c/dev/snesgame2/build.sh clean"
```sh
make artifacts
```

### Alternative: build manually inside MSYS2

If you want to invoke `make` directly, you need the same environment variables:
To remove all build output and generated asset data:

```sh
export PVSNESLIB_HOME=C:/tools/pvsneslib
export PATH=$PVSNESLIB_HOME/devkitsnes/bin:$PVSNESLIB_HOME/devkitsnes/tools:$PATH
cd /c/dev/snesgame2
make
make clean
```

## Make Targets

- `make`: build graphics, compile code, and produce `snesgame2.sfc`
- `make clean`: remove build results and generated graphics outputs through the PVSnesLib clean rules
- `make` / `make all`: convert assets, compile code, and produce `snesgame2.sfc`
- `make artifacts`: convert source art/sound into SNES data, without linking the ROM
- `make clean`: remove the ROM, build intermediates, and generated asset data through the PVSnesLib clean rules

## Asset Workflow

Expand All @@ -124,7 +100,8 @@ This project keeps source art in standard image formats and converts them during
### Source assets used by `make`

- `sprites.bmp`: player sprite sheet
- `enemy.bmp`: enemy sprite sheet
- `enemy.bmp`: enemy sprite sheet (Genesis console; one hit to destroy)
- `tac2.bmp`: tough enemy sprite (TAC-2 joystick; takes three hits to destroy)
- `bullet.bmp`: bullet sprite
- `pvsneslibfont.png`: font image
Comment on lines 102 to 106

Expand All @@ -141,41 +118,54 @@ These generated files are already present in the repository, but they can be reg

## Helper Scripts

The PowerShell scripts in `tools/` are not part of the normal `make` target. They are helper utilities for regenerating the source BMP files before running the build.
The Python scripts in `tools/` are not part of the normal `make` target. They are helper utilities for regenerating the source BMP files before running the build. They share `tools/snesbmp.py` (a small standard-library helper that draws onto an indexed canvas and writes the 8bpp BMP), and they use only the Python standard library, so they run the same on Windows, Linux, and WSL.

### `tools/make_player_bmp.ps1`
### `tools/make_player_bmp.py`

- Rebuilds `sprites.bmp`
- Preserves an original backup at `tools/sprites_src.bmp`
- Downscales the player art to roughly `21x21` inside a `32x32` sprite cell

Run it with:

```powershell
& ./tools/make_player_bmp.ps1
```sh
python3 tools/make_player_bmp.py
```

### `tools/make_enemy_bmp.ps1`
### `tools/make_enemy_bmp.py`

- Generates `enemy.bmp`
- Creates an 8bpp `32x32` enemy sprite with a fixed palette
- Downscales the art to roughly `21x21` inside a `32x32` sprite cell

Run it with:

```powershell
& ./tools/make_enemy_bmp.ps1
```sh
python3 tools/make_enemy_bmp.py
```

### `tools/make_bullet_bmp.ps1`
### `tools/make_tac2_bmp.py`

- Generates `tac2.bmp`
- Creates an 8bpp `32x32` sprite of a Suncom TAC-2 arcade joystick (red ball-top stick, chrome shaft, black base with a red fire button)
- Draws the art in the top-left `21x21` of the `32x32` cell so it lines up with the enemy hit-box
- This is the tougher enemy: it takes three hits to destroy (a screen-clearing bomb still flattens it in one), descends more slowly, and is worth more points

Run it with:

```sh
python3 tools/make_tac2_bmp.py
```

### `tools/make_bullet_bmp.py`

- Generates `bullet.bmp`
- Creates an 8bpp `8x8` bullet sprite with a small fixed palette

Run it with:

```powershell
& ./tools/make_bullet_bmp.ps1
```sh
python3 tools/make_bullet_bmp.py
```

After running any of these scripts, rebuild the project so the `.bmp` changes are converted into SNES asset data.
Expand All @@ -194,26 +184,27 @@ Other intermediate files such as object files, symbol files, and converted graph

### `PVSNESLIB_HOME` is not set

If `make` fails immediately with a message about `PVSNESLIB_HOME`, use the provided wrapper command instead of running `make` directly, or export the variable manually before building.

### `bash.exe` or MSYS2 path not found
If `make` stops immediately with a message about `PVSNESLIB_HOME`, export the
variable so it points at your PVSnesLib install (a Unix-style path), e.g.
`export PVSNESLIB_HOME=/opt/pvsneslib`, and add it to your shell profile so it
persists.

Install MSYS2 in `C:/msys64`, or adjust the commands in this README to match your installation path.
### PVSnesLib path errors

### PVSnesLib not found

Install PVSnesLib at `C:/tools/pvsneslib`, or update `build.sh` and your environment to match the actual location.
`PVSNESLIB_HOME` must be a Unix-style path (PVSnesLib's build rules reject paths
containing a backslash). Under WSL, point it at a Linux path or a `/mnt/c/...`
mount, not a `C:\...` path.

### Asset changes do not appear in the ROM

If you edited or regenerated a BMP or the font PNG, rebuild the ROM so the graphics conversion step runs again.

## Typical Windows Workflow
## Typical Workflow

```powershell
& ./tools/make_enemy_bmp.ps1
& ./tools/make_bullet_bmp.ps1
& C:/msys64/usr/bin/bash.exe -lc "sh /c/dev/snesgame2/build.sh"
```sh
python3 tools/make_enemy_bmp.py
python3 tools/make_bullet_bmp.py
make
```

If you did not change the source art, only the build command is needed.
If you did not change the source art, only `make` is needed.
Loading